April 2026 waren Wirtschaft Feedback
This commit is contained in:
parent
02f2a4c23e
commit
9ce711d6b2
167 changed files with 25278 additions and 8518 deletions
191
resources/views/admin/inventory/productions/create.blade.php
Normal file
191
resources/views/admin/inventory/productions/create.blade.php
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
@extends('layouts.layout-2')
|
||||
|
||||
@section('content')
|
||||
<h4 class="font-weight-bold py-2 mb-2">{{ __('Neue Produktion') }}</h4>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="post" action="{{ route('admin.inventory.productions.store') }}" id="form-production">
|
||||
@csrf
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="product_id">{{ __('Produkt') }}</label>
|
||||
<select name="product_id" id="product_id" class="form-control @error('product_id') is-invalid @enderror" required>
|
||||
<option value="">{{ __('Bitte wählen') }}</option>
|
||||
@foreach($products as $p)
|
||||
<option value="{{ $p->id }}" @selected(old('product_id', $model?->product_id) == $p->id)>{{ $p->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('product_id')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="location_id">{{ __('Lagerort / Produktionsstandort') }}</label>
|
||||
<select name="location_id" id="location_id" class="form-control @error('location_id') is-invalid @enderror" required>
|
||||
<option value="">{{ __('Bitte wählen') }}</option>
|
||||
@foreach($locations as $loc)
|
||||
<option value="{{ $loc->id }}" @selected(old('location_id', $model?->location_id ?? $defaultLocationId) == $loc->id)>{{ $loc->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('location_id')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="produced_at">{{ __('Produktionsdatum') }}</label>
|
||||
<input type="date" name="produced_at" id="produced_at" class="form-control @error('produced_at') is-invalid @enderror"
|
||||
value="{{ old('produced_at', now()->toDateString()) }}" required>
|
||||
@error('produced_at')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="quantity">{{ __('Produzierte Stückzahl') }}</label>
|
||||
<input type="number" name="quantity" id="quantity" min="1" step="1" class="form-control @error('quantity') is-invalid @enderror"
|
||||
value="{{ old('quantity', $model?->quantity ?? 1) }}" required>
|
||||
@error('quantity')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="recipe-area" class="mb-3" style="display:none;">
|
||||
<hr>
|
||||
<h6>{{ __('Chargen zuordnen') }}</h6>
|
||||
<p class="text-muted small" id="recipe-hint"></p>
|
||||
<div id="recipe-ingredient-lines"></div>
|
||||
@error('ingredient_lines')
|
||||
<div class="text-danger small">{{ $message }}</div>
|
||||
@enderror
|
||||
|
||||
<h6 class="mt-3">{{ __('Verpackung (Vorschau)') }}</h6>
|
||||
<div class="table-responsive border rounded">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>{{ __('Artikel') }}</th><th>{{ __('Stück gesamt') }}</th></tr></thead>
|
||||
<tbody id="recipe-packaging-preview"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="notes">{{ __('Notizen') }}</label>
|
||||
<textarea name="notes" id="notes" class="form-control" rows="2">{{ old('notes') }}</textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" id="btn-submit-production">{{ __('Produktion speichern') }}</button>
|
||||
<a href="{{ route('admin.inventory.productions.index') }}" class="btn btn-outline-secondary">{{ __('Abbrechen') }}</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
(function ($) {
|
||||
var recipeBase = @json(url('/admin/inventory/api/products'));
|
||||
var lineIndex = 0;
|
||||
|
||||
function recipeUrl() {
|
||||
var pid = $('#product_id').val();
|
||||
var lid = $('#location_id').val();
|
||||
var qty = $('#quantity').val() || 1;
|
||||
if (!pid || !lid) return null;
|
||||
return recipeBase + '/' + pid + '/recipe?location_id=' + encodeURIComponent(lid) + '&quantity=' + encodeURIComponent(qty);
|
||||
}
|
||||
|
||||
function addIngredientRow($tbody, ing, stockEntries) {
|
||||
var idx = lineIndex;
|
||||
lineIndex++;
|
||||
var tr = $('<tr></tr>');
|
||||
var select = $('<select class="form-control form-control-sm" name="ingredient_lines[' + idx + '][stock_entry_id]" required></select>');
|
||||
select.append('<option value="">{{ __('Charge wählen') }}</option>');
|
||||
(stockEntries || []).forEach(function (se) {
|
||||
var label = '#' + se.id;
|
||||
if (se.batch_number) label += ' — ' + se.batch_number;
|
||||
if (se.best_before) label += ' (MHD ' + se.best_before + ')';
|
||||
select.append($('<option></option>').attr('value', se.id).text(label));
|
||||
});
|
||||
var td1 = $('<td></td>');
|
||||
td1.append($('<input type="hidden" name="ingredient_lines[' + idx + '][ingredient_id]" value="' + ing.id + '">'));
|
||||
td1.append(select);
|
||||
tr.append(td1);
|
||||
tr.append($('<td></td>').append(
|
||||
$('<input type="text" class="form-control form-control-sm" required name="ingredient_lines[' + idx + '][quantity_used]" placeholder="0">')
|
||||
));
|
||||
$tbody.append(tr);
|
||||
}
|
||||
|
||||
function renderRecipe(data) {
|
||||
$('#recipe-ingredient-lines').empty();
|
||||
lineIndex = 0;
|
||||
var hintParts = [];
|
||||
var hasMissingGram = false;
|
||||
(data.ingredients || []).forEach(function (ing) {
|
||||
if (ing.required_grams_total === null) {
|
||||
hasMissingGram = true;
|
||||
hintParts.push(ing.name + ': {{ __('Gramm in der Rezeptur fehlt') }}');
|
||||
return;
|
||||
}
|
||||
var wrap = $('<div class="border rounded p-2 mb-2" data-ingredient-id="' + ing.id + '"></div>');
|
||||
wrap.data('recipe-ing', ing);
|
||||
var soll = '<strong>' + $('<div/>').text(ing.name).html() + '</strong> — {{ __('Soll') }}: ' +
|
||||
ing.required_grams_total.toLocaleString('de-DE', {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' g';
|
||||
wrap.append(soll);
|
||||
var tbody = $('<tbody></tbody>');
|
||||
var tbl = $('<table class="table table-sm table-bordered mb-1"><thead><tr><th>{{ __('Charge') }}</th><th>{{ __('Menge (g)') }}</th></tr></thead></table>');
|
||||
tbl.append(tbody);
|
||||
wrap.append(tbl);
|
||||
wrap.append('<button type="button" class="btn btn-sm btn-outline-secondary btn-add-split">{{ __('Weitere Charge') }}</button>');
|
||||
$('#recipe-ingredient-lines').append(wrap);
|
||||
addIngredientRow(tbody, ing, ing.stock_entries || []);
|
||||
});
|
||||
if (hasMissingGram) {
|
||||
$('#recipe-hint').text(hintParts.join(' · ')).removeClass('text-muted').addClass('text-danger');
|
||||
} else {
|
||||
$('#recipe-hint').text('{{ __('Pro Charge die entnommene Menge in Gramm eintragen. Summe je Inhaltsstoff muss dem Soll entsprechen.') }}').removeClass('text-danger').addClass('text-muted');
|
||||
}
|
||||
|
||||
var $pk = $('#recipe-packaging-preview').empty();
|
||||
(data.packagings || []).forEach(function (pk) {
|
||||
$pk.append('<tr><td>' + $('<div/>').text(pk.name).html() + '</td><td>' + pk.total_pieces + '</td></tr>');
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('click', '.btn-add-split', function () {
|
||||
var $wrap = $(this).closest('[data-ingredient-id]');
|
||||
var ing = $wrap.data('recipe-ing');
|
||||
if (!ing) return;
|
||||
var $tbody = $wrap.find('tbody').first();
|
||||
addIngredientRow($tbody, ing, ing.stock_entries || []);
|
||||
});
|
||||
|
||||
function loadRecipe() {
|
||||
var url = recipeUrl();
|
||||
if (!url) {
|
||||
$('#recipe-area').hide();
|
||||
return;
|
||||
}
|
||||
$('#recipe-hint').text('{{ __('Lade …') }}');
|
||||
$.getJSON(url)
|
||||
.done(function (data) {
|
||||
$('#recipe-area').show();
|
||||
renderRecipe(data);
|
||||
})
|
||||
.fail(function () {
|
||||
$('#recipe-area').show();
|
||||
$('#recipe-hint').text('{{ __('Rezept konnte nicht geladen werden.') }}').addClass('text-danger');
|
||||
});
|
||||
}
|
||||
|
||||
$('#product_id, #location_id, #quantity').on('change', loadRecipe);
|
||||
$(document).ready(function () {
|
||||
if ($('#product_id').val() && $('#location_id').val()) {
|
||||
loadRecipe();
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
||||
</script>
|
||||
@endsection
|
||||
Loading…
Add table
Add a link
Reference in a new issue