- AP-09 Produktbestand inkl. Bewegungshistorie (product_stock_movements, ProductStockService) - AP-10 Rohstoffbestand-Ansicht je Lager (RawMaterialStockController) - AP-11 Bestandsschwellen / Out-of-Stock-Handling fuer Produkte und Shop - AP-12 Ausgang/Ausschuss (stock_disposals, StockDisposalController, InventoryService) - Set-Produkte (product_set_items) inkl. Aufloesung - Produktentwicklung & Hinweise-Verwaltung (Notices) - AP-13 Entwicklungskonzept Shop-Bestandsabzug im Plan dokumentiert - Feature-Tests fuer neue Module + aktualisierter Entwicklungsplan Co-authored-by: Cursor <cursoragent@cursor.com>
582 lines
25 KiB
PHP
Executable file
582 lines
25 KiB
PHP
Executable file
@extends('layouts.layout-2')
|
|
|
|
@section('content')
|
|
|
|
@if ($errors->any())
|
|
<div class="row">
|
|
<div class="col-sm-12">
|
|
<div class="alert alert-danger">
|
|
<ul>
|
|
@foreach ($errors->all() as $error)
|
|
<li>{{ $error }}</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<h4 class="font-weight-bold py-2 mb-2">
|
|
{{ __('Create/Edit Produkt') }}
|
|
</h4>
|
|
|
|
{!! Form::open(['action' => route('admin_product_store'), 'class' => 'form-horizontal', 'id'=>'']) !!}
|
|
<input type="hidden" name="id" id="id" value="@if($product->id>0){{$product->id}}@else new @endif">
|
|
|
|
<div class="text-left mt-0 mb-2">
|
|
<button type="submit" class="btn btn-submit">{{ __('save') }}</button>
|
|
<a href="{{ route('admin_product_show') }}" class="btn btn-default">{{ __('back') }}</a>
|
|
</div>
|
|
|
|
@include('admin.product.form')
|
|
|
|
<div class="text-left mt-0 mb-2">
|
|
<button type="submit" class="btn btn-submit">{{ __('save') }}</button>
|
|
<a href="{{ route('admin_product_show') }}" class="btn btn-default">{{ __('back') }}</a>
|
|
</div>
|
|
|
|
{!! Form::close() !!}
|
|
|
|
@include('admin.product.images')
|
|
|
|
@include('admin.product.upload_whitelabel')
|
|
|
|
@endsection
|
|
|
|
@section('scripts')
|
|
@php
|
|
$ingredient_catalog_for_js = $ingredient_catalog->keyBy('id')->map(function ($item) {
|
|
return [
|
|
'id' => $item->id,
|
|
'name' => $item->name,
|
|
'inci' => $item->inci,
|
|
'effect' => $item->effect,
|
|
'default_factor' => $item->default_factor,
|
|
'quality_name' => $item->materialQuality?->name ?? '',
|
|
];
|
|
});
|
|
$packaging_catalog_for_js = $packaging_catalog->keyBy('id')->map(function ($item) {
|
|
return [
|
|
'name' => $item->name,
|
|
'weight_grams' => $item->weight_grams,
|
|
'material_name' => $item->packagingMaterial?->name ?? '',
|
|
];
|
|
});
|
|
$set_product_catalog_for_js = $set_product_catalog->keyBy('id')->map(function ($item) {
|
|
return [
|
|
'name' => $item->name,
|
|
'number' => $item->number,
|
|
'weight' => $item->weight ? $item->weight . ' g' : '—',
|
|
'price' => $item->getFormattedPrice() !== '' ? $item->getFormattedPrice() . ' €' : '—',
|
|
];
|
|
});
|
|
@endphp
|
|
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js"></script>
|
|
<script>
|
|
(function ($) {
|
|
var catalog = @json($ingredient_catalog_for_js);
|
|
var packagingCatalog = @json($packaging_catalog_for_js);
|
|
var setCatalog = @json($set_product_catalog_for_js);
|
|
|
|
function parseDeNumber(val) {
|
|
if (val === undefined || val === null || val === '') return NaN;
|
|
var s = String(val).replace(/\s/g, '').replace(',', '.');
|
|
var n = parseFloat(s);
|
|
return isNaN(n) ? NaN : n;
|
|
}
|
|
|
|
function updateEffective($row) {
|
|
var g = parseDeNumber($row.find('.pi-gram').val());
|
|
var f = parseDeNumber($row.find('.pi-factor').val());
|
|
var $out = $row.find('.pi-effective');
|
|
if (!isNaN(g) && !isNaN(f)) {
|
|
var eff = g * f;
|
|
$out.text(eff.toLocaleString('de-DE', { minimumFractionDigits: 6, maximumFractionDigits: 6 }));
|
|
} else {
|
|
$out.text('—');
|
|
}
|
|
}
|
|
|
|
function updateRecipeTotal() {
|
|
var total = 0;
|
|
var hasValue = false;
|
|
$('#ingredient-sortable-rows tr').each(function () {
|
|
var v = parseDeNumber($(this).find('.pi-gram').val());
|
|
if (!isNaN(v)) { total += v; hasValue = true; }
|
|
});
|
|
var $cell = $('#recipe-total-percent');
|
|
if (!hasValue) {
|
|
$cell.text('—').removeClass('text-danger text-success');
|
|
return;
|
|
}
|
|
var formatted = total.toLocaleString('de-DE', { minimumFractionDigits: 6, maximumFractionDigits: 6 }) + ' %';
|
|
$cell.text(formatted);
|
|
var diff = Math.abs(total - 100);
|
|
if (diff < 0.000001) {
|
|
$cell.removeClass('text-danger').addClass('text-success');
|
|
$cell.attr('title', '');
|
|
} else {
|
|
$cell.removeClass('text-success').addClass('text-danger');
|
|
$cell.attr('title', 'Die Gesamtrezeptur ergibt nicht 100 %!');
|
|
}
|
|
}
|
|
|
|
function refreshAllEffectives() {
|
|
$('#ingredient-sortable-rows tr').each(function () { updateEffective($(this)); });
|
|
updateRecipeTotal();
|
|
}
|
|
|
|
$(document).ready(function () {
|
|
var $tbody = document.getElementById('ingredient-sortable-rows');
|
|
if ($tbody && typeof Sortable !== 'undefined') {
|
|
new Sortable($tbody, {
|
|
handle: '.ingredient-drag-handle',
|
|
animation: 150,
|
|
});
|
|
}
|
|
|
|
function appendIngredientRow(id) {
|
|
id = String(id);
|
|
var ing = catalog[id];
|
|
if (!ing) return;
|
|
var factor = ing.default_factor != null ? String(ing.default_factor).replace('.', ',') : '1,10';
|
|
var row = $('<tr data-ingredient-id="' + id + '">' +
|
|
'<td class="text-muted align-middle ingredient-drag-handle" style="cursor:grab">☰</td>' +
|
|
'<td class="align-middle"></td><td class="align-middle small text-muted"></td><td class="align-middle"></td>' +
|
|
'<td><input type="hidden" name="pi_ingredient_id[]" value="' + id + '">' +
|
|
'<input type="text" name="pi_gram[]" class="form-control form-control-sm pi-gram" value="" autocomplete="off" step="0.000001"></td>' +
|
|
'<td><input type="text" name="pi_factor[]" class="form-control form-control-sm pi-factor" value="' + factor + '" autocomplete="off"></td>' +
|
|
'<td class="align-middle pi-effective text-right small text-muted">—</td>' +
|
|
'<td class="align-middle"><a class="text-danger ingredient-row-remove" href="#" title="Entfernen"><i class="far fa-trash-alt"></i></a></td></tr>');
|
|
row.find('td').eq(1).text(ing.name || '');
|
|
row.find('td').eq(2).text(ing.quality_name || '—');
|
|
row.find('td').eq(3).text(ing.inci || '');
|
|
$('#ingredient-sortable-rows').append(row);
|
|
updateEffective(row);
|
|
updateRecipeTotal();
|
|
}
|
|
|
|
function syncIngredientModalRows() {
|
|
var usedIds = {};
|
|
$('#ingredient-sortable-rows tr').each(function () {
|
|
var id = $(this).data('ingredient-id');
|
|
if (id) usedIds[String(id)] = true;
|
|
});
|
|
$('#modal-ingredients-pick .ingredient-modal-row').each(function () {
|
|
var id = String($(this).data('ingredient-id'));
|
|
var inUse = !!usedIds[id];
|
|
var $cb = $(this).find('.js-ingredient-modal-cb');
|
|
$cb.prop('disabled', inUse);
|
|
$cb.attr('title', inUse ? '{{ __('Bereits im Produkt') }}' : '');
|
|
if (inUse) {
|
|
$cb.prop('checked', false);
|
|
}
|
|
$(this).toggleClass('text-muted', inUse);
|
|
$(this).css('opacity', inUse ? 0.65 : 1);
|
|
$(this).find('.js-ing-modal-name').toggleClass('text-muted', inUse);
|
|
$(this).find('.js-ing-modal-inci').toggleClass('text-muted', inUse);
|
|
});
|
|
}
|
|
|
|
function filterIngredientModalRows() {
|
|
var q = $('#ingredient-modal-search').val().trim().toLowerCase();
|
|
var visible = 0;
|
|
$('#ingredient-modal-tbody .ingredient-modal-row').each(function () {
|
|
var hay = $(this).attr('data-ingredient-search') || '';
|
|
var match = !q || hay.indexOf(q) !== -1;
|
|
$(this).toggle(match);
|
|
if (match) {
|
|
visible++;
|
|
}
|
|
});
|
|
$('#ingredient-modal-no-results').toggleClass('d-none', !(q.length > 0 && visible === 0));
|
|
}
|
|
|
|
$('#ingredient-modal-search').on('input', filterIngredientModalRows);
|
|
|
|
$('#modal-ingredients-pick').on('show.bs.modal', function () {
|
|
$('#ingredient-modal-search').val('');
|
|
$('#ingredient-modal-tbody .ingredient-modal-row').show();
|
|
$('#ingredient-modal-no-results').addClass('d-none');
|
|
syncIngredientModalRows();
|
|
});
|
|
|
|
$('#btn-ingredients-modal-add').on('click', function () {
|
|
var added = 0;
|
|
$('#modal-ingredients-pick .js-ingredient-modal-cb:checked:not(:disabled)').each(function () {
|
|
var id = String($(this).val());
|
|
appendIngredientRow(id);
|
|
$(this).prop('checked', false);
|
|
added++;
|
|
});
|
|
if (added > 0) {
|
|
syncIngredientModalRows();
|
|
$('#modal-ingredients-pick').modal('hide');
|
|
}
|
|
});
|
|
|
|
$(document).on('click', '.ingredient-row-remove', function (ev) {
|
|
ev.preventDefault();
|
|
var $tr = $(this).closest('tr');
|
|
$tr.remove();
|
|
syncIngredientModalRows();
|
|
updateRecipeTotal();
|
|
});
|
|
|
|
$(document).on('input change', '.pi-gram, .pi-factor', function () {
|
|
updateEffective($(this).closest('tr'));
|
|
updateRecipeTotal();
|
|
});
|
|
|
|
refreshAllEffectives();
|
|
|
|
var $packTbody = document.getElementById('packaging-sortable-rows');
|
|
if ($packTbody && typeof Sortable !== 'undefined') {
|
|
new Sortable($packTbody, {
|
|
handle: '.packaging-drag-handle',
|
|
animation: 150,
|
|
});
|
|
}
|
|
|
|
function appendPackagingRow(id) {
|
|
id = String(id);
|
|
var pk = packagingCatalog[id];
|
|
if (!pk) {
|
|
return;
|
|
}
|
|
var wCell = '—';
|
|
if (pk.weight_grams != null && pk.weight_grams !== '') {
|
|
wCell = String(pk.weight_grams).replace('.', ',');
|
|
}
|
|
var row = $('<tr data-packaging-item-id="' + id + '">' +
|
|
'<td class="text-muted align-middle packaging-drag-handle" style="cursor:grab">☰</td>' +
|
|
'<td class="align-middle"></td><td class="align-middle"></td><td class="align-middle text-right"></td>' +
|
|
'<td><input type="hidden" name="pp_packaging_item_id[]" value="' + id + '">' +
|
|
'<input type="text" name="pp_quantity[]" class="form-control form-control-sm pp-quantity" value="1" autocomplete="off"></td>' +
|
|
'<td class="align-middle"><a class="text-danger packaging-row-remove" href="#" title="{{ __('Entfernen') }}"><i class="far fa-trash-alt"></i></a></td></tr>');
|
|
row.find('td').eq(1).text(pk.name || '');
|
|
row.find('td').eq(2).text(pk.material_name || '—');
|
|
row.find('td').eq(3).text(wCell);
|
|
$('#packaging-sortable-rows').append(row);
|
|
}
|
|
|
|
function syncPackagingModalRows() {
|
|
var usedIds = {};
|
|
$('#packaging-sortable-rows tr').each(function () {
|
|
var pid = $(this).data('packaging-item-id');
|
|
if (pid) {
|
|
usedIds[String(pid)] = true;
|
|
}
|
|
});
|
|
$('#modal-packaging-pick .packaging-modal-row').each(function () {
|
|
var pid = String($(this).data('packaging-item-id'));
|
|
var inUse = !!usedIds[pid];
|
|
var $cb = $(this).find('.js-packaging-modal-cb');
|
|
$cb.prop('disabled', inUse);
|
|
$cb.attr('title', inUse ? '{{ __('Bereits im Produkt') }}' : '');
|
|
if (inUse) {
|
|
$cb.prop('checked', false);
|
|
}
|
|
$(this).toggleClass('text-muted', inUse);
|
|
$(this).css('opacity', inUse ? 0.65 : 1);
|
|
$(this).find('.js-pk-modal-name').toggleClass('text-muted', inUse);
|
|
$(this).find('.js-pk-modal-mat').toggleClass('text-muted', inUse);
|
|
});
|
|
}
|
|
|
|
function filterPackagingModalRows() {
|
|
var q = $('#packaging-modal-search').val().trim().toLowerCase();
|
|
var visible = 0;
|
|
$('#packaging-modal-tbody .packaging-modal-row').each(function () {
|
|
var hay = $(this).attr('data-packaging-search') || '';
|
|
var match = !q || hay.indexOf(q) !== -1;
|
|
$(this).toggle(match);
|
|
if (match) {
|
|
visible++;
|
|
}
|
|
});
|
|
$('#packaging-modal-no-results').toggleClass('d-none', !(q.length > 0 && visible === 0));
|
|
}
|
|
|
|
$('#packaging-modal-search').on('input', filterPackagingModalRows);
|
|
|
|
$('#modal-packaging-pick').on('show.bs.modal', function () {
|
|
$('#packaging-modal-search').val('');
|
|
$('#packaging-modal-tbody .packaging-modal-row').show();
|
|
$('#packaging-modal-no-results').addClass('d-none');
|
|
syncPackagingModalRows();
|
|
});
|
|
|
|
$('#btn-packaging-modal-add').on('click', function () {
|
|
var added = 0;
|
|
$('#modal-packaging-pick .js-packaging-modal-cb:checked:not(:disabled)').each(function () {
|
|
var id = String($(this).val());
|
|
appendPackagingRow(id);
|
|
$(this).prop('checked', false);
|
|
added++;
|
|
});
|
|
if (added > 0) {
|
|
syncPackagingModalRows();
|
|
$('#modal-packaging-pick').modal('hide');
|
|
}
|
|
});
|
|
|
|
$(document).on('click', '.packaging-row-remove', function (ev) {
|
|
ev.preventDefault();
|
|
$(this).closest('tr').remove();
|
|
syncPackagingModalRows();
|
|
});
|
|
|
|
// === Hersteller-Rezeptur (Manufacturer) ===
|
|
var $mfgTbody = document.getElementById('mfg-ingredient-sortable-rows');
|
|
if ($mfgTbody && typeof Sortable !== 'undefined') {
|
|
new Sortable($mfgTbody, {
|
|
handle: '.mfg-ingredient-drag-handle',
|
|
animation: 150,
|
|
});
|
|
}
|
|
|
|
function updateMfgEffective($row) {
|
|
var g = parseDeNumber($row.find('.mfg-gram').val());
|
|
var f = parseDeNumber($row.find('.mfg-factor').val());
|
|
var $out = $row.find('.mfg-effective');
|
|
if (!isNaN(g) && !isNaN(f)) {
|
|
$out.text((g * f).toLocaleString('de-DE', { minimumFractionDigits: 6, maximumFractionDigits: 6 }));
|
|
} else {
|
|
$out.text('—');
|
|
}
|
|
}
|
|
|
|
function updateMfgRecipeTotal() {
|
|
var total = 0, hasValue = false;
|
|
$('#mfg-ingredient-sortable-rows tr').each(function () {
|
|
var v = parseDeNumber($(this).find('.mfg-gram').val());
|
|
if (!isNaN(v)) { total += v; hasValue = true; }
|
|
});
|
|
var $cell = $('#mfg-recipe-total-percent');
|
|
if (!hasValue) { $cell.text('—').removeClass('text-danger text-success'); return; }
|
|
$cell.text(total.toLocaleString('de-DE', { minimumFractionDigits: 6, maximumFractionDigits: 6 }) + ' %');
|
|
if (Math.abs(total - 100) < 0.000001) {
|
|
$cell.removeClass('text-danger').addClass('text-success').attr('title', '');
|
|
} else {
|
|
$cell.removeClass('text-success').addClass('text-danger').attr('title', 'Die Gesamtrezeptur ergibt nicht 100 %!');
|
|
}
|
|
}
|
|
|
|
function refreshAllMfgEffectives() {
|
|
$('#mfg-ingredient-sortable-rows tr').each(function () { updateMfgEffective($(this)); });
|
|
updateMfgRecipeTotal();
|
|
}
|
|
|
|
function appendMfgIngredientRow(id) {
|
|
id = String(id);
|
|
var ing = catalog[id];
|
|
if (!ing) return;
|
|
var factor = ing.default_factor != null ? String(ing.default_factor).replace('.', ',') : '1,10';
|
|
var row = $('<tr data-ingredient-id="' + id + '">' +
|
|
'<td class="text-muted align-middle mfg-ingredient-drag-handle" style="cursor:grab">☰</td>' +
|
|
'<td class="align-middle"></td><td class="align-middle small text-muted"></td><td class="align-middle"></td>' +
|
|
'<td><input type="hidden" name="mfg_ingredient_id[]" value="' + id + '">' +
|
|
'<input type="text" name="mfg_gram[]" class="form-control form-control-sm mfg-gram" value="" autocomplete="off" step="0.000001"></td>' +
|
|
'<td><input type="text" name="mfg_factor[]" class="form-control form-control-sm mfg-factor" value="' + factor + '" autocomplete="off"></td>' +
|
|
'<td class="align-middle mfg-effective text-right small text-muted">—</td>' +
|
|
'<td class="align-middle"><a class="text-danger mfg-ingredient-row-remove" href="#" title="Entfernen"><i class="far fa-trash-alt"></i></a></td></tr>');
|
|
row.find('td').eq(1).text(ing.name || '');
|
|
row.find('td').eq(2).text(ing.quality_name || '—');
|
|
row.find('td').eq(3).text(ing.inci || '');
|
|
$('#mfg-ingredient-sortable-rows').append(row);
|
|
updateMfgEffective(row);
|
|
updateMfgRecipeTotal();
|
|
}
|
|
|
|
function syncMfgIngredientModalRows() {
|
|
var usedIds = {};
|
|
$('#mfg-ingredient-sortable-rows tr').each(function () {
|
|
var id = $(this).data('ingredient-id');
|
|
if (id) usedIds[String(id)] = true;
|
|
});
|
|
$('#modal-mfg-ingredients-pick .mfg-ingredient-modal-row').each(function () {
|
|
var id = String($(this).data('ingredient-id'));
|
|
var inUse = !!usedIds[id];
|
|
var $cb = $(this).find('.js-mfg-ingredient-modal-cb');
|
|
$cb.prop('disabled', inUse);
|
|
if (inUse) $cb.prop('checked', false);
|
|
$(this).css('opacity', inUse ? 0.65 : 1);
|
|
});
|
|
}
|
|
|
|
$('#mfg-ingredient-modal-search').on('input', function () {
|
|
var q = $(this).val().trim().toLowerCase();
|
|
var visible = 0;
|
|
$('#mfg-ingredient-modal-tbody .mfg-ingredient-modal-row').each(function () {
|
|
var hay = $(this).attr('data-ingredient-search') || '';
|
|
var match = !q || hay.indexOf(q) !== -1;
|
|
$(this).toggle(match);
|
|
if (match) visible++;
|
|
});
|
|
$('#mfg-ingredient-modal-no-results').toggleClass('d-none', !(q.length > 0 && visible === 0));
|
|
});
|
|
|
|
$('#modal-mfg-ingredients-pick').on('show.bs.modal', function () {
|
|
$('#mfg-ingredient-modal-search').val('');
|
|
$('#mfg-ingredient-modal-tbody .mfg-ingredient-modal-row').show();
|
|
$('#mfg-ingredient-modal-no-results').addClass('d-none');
|
|
syncMfgIngredientModalRows();
|
|
});
|
|
|
|
$('#btn-mfg-ingredients-modal-add').on('click', function () {
|
|
var added = 0;
|
|
$('#modal-mfg-ingredients-pick .js-mfg-ingredient-modal-cb:checked:not(:disabled)').each(function () {
|
|
appendMfgIngredientRow(String($(this).val()));
|
|
$(this).prop('checked', false);
|
|
added++;
|
|
});
|
|
if (added > 0) { syncMfgIngredientModalRows(); $('#modal-mfg-ingredients-pick').modal('hide'); }
|
|
});
|
|
|
|
$(document).on('click', '.mfg-ingredient-row-remove', function (ev) {
|
|
ev.preventDefault();
|
|
$(this).closest('tr').remove();
|
|
syncMfgIngredientModalRows();
|
|
updateMfgRecipeTotal();
|
|
});
|
|
|
|
$(document).on('input change', '.mfg-gram, .mfg-factor', function () {
|
|
updateMfgEffective($(this).closest('tr'));
|
|
updateMfgRecipeTotal();
|
|
});
|
|
|
|
refreshAllMfgEffectives();
|
|
|
|
function toggleShelfMonths() {
|
|
var v = $('.js-shelf-life-type:checked').val();
|
|
if (v === 'fixed') {
|
|
$('#shelf-life-months-wrap').show();
|
|
} else {
|
|
$('#shelf-life-months-wrap').hide();
|
|
}
|
|
}
|
|
$(document).on('change', '.js-shelf-life-type', toggleShelfMonths);
|
|
toggleShelfMonths();
|
|
|
|
function toggleRecipeFields() {
|
|
var noRecipe = $('#no_recipe_required').is(':checked');
|
|
$('.js-recipe-fields').toggle(!noRecipe);
|
|
}
|
|
$(document).on('change', '#no_recipe_required', toggleRecipeFields);
|
|
toggleRecipeFields();
|
|
|
|
// === Set-Bestandteile ===
|
|
var $setTbody = document.getElementById('set-sortable-rows');
|
|
if ($setTbody && typeof Sortable !== 'undefined') {
|
|
new Sortable($setTbody, {
|
|
handle: '.set-drag-handle',
|
|
animation: 150,
|
|
});
|
|
}
|
|
|
|
function appendSetRow(id) {
|
|
id = String(id);
|
|
var sp = setCatalog[id];
|
|
if (!sp) {
|
|
return;
|
|
}
|
|
var row = $('<tr data-set-product-id="' + id + '">' +
|
|
'<td class="text-muted align-middle set-drag-handle" style="cursor:grab">☰</td>' +
|
|
'<td class="align-middle"></td><td class="align-middle small text-muted"></td>' +
|
|
'<td class="align-middle text-right small text-muted"></td>' +
|
|
'<td class="align-middle text-right small text-muted"></td>' +
|
|
'<td><input type="hidden" name="set_component_id[]" value="' + id + '">' +
|
|
'<input type="number" min="1" step="1" name="set_quantity[]" class="form-control form-control-sm set-quantity" value="1" autocomplete="off"></td>' +
|
|
'<td class="align-middle"><a class="text-danger set-row-remove" href="#" title="{{ __('Entfernen') }}"><i class="far fa-trash-alt"></i></a></td></tr>');
|
|
row.find('td').eq(1).text(sp.name || '');
|
|
row.find('td').eq(2).text(sp.number || '—');
|
|
row.find('td').eq(3).text(sp.weight || '—');
|
|
row.find('td').eq(4).text(sp.price || '—');
|
|
$('#set-sortable-rows').append(row);
|
|
}
|
|
|
|
function syncSetModalRows() {
|
|
var usedIds = {};
|
|
$('#set-sortable-rows tr').each(function () {
|
|
var sid = $(this).data('set-product-id');
|
|
if (sid) {
|
|
usedIds[String(sid)] = true;
|
|
}
|
|
});
|
|
$('#modal-set-pick .set-modal-row').each(function () {
|
|
var sid = String($(this).data('set-product-id'));
|
|
var inUse = !!usedIds[sid];
|
|
var $cb = $(this).find('.js-set-modal-cb');
|
|
$cb.prop('disabled', inUse);
|
|
$cb.attr('title', inUse ? '{{ __('Bereits im Set') }}' : '');
|
|
if (inUse) {
|
|
$cb.prop('checked', false);
|
|
}
|
|
$(this).css('opacity', inUse ? 0.65 : 1);
|
|
$(this).find('.js-set-modal-name').toggleClass('text-muted', inUse);
|
|
$(this).find('.js-set-modal-number').toggleClass('text-muted', inUse);
|
|
});
|
|
}
|
|
|
|
$('#set-modal-search').on('input', function () {
|
|
var q = $(this).val().trim().toLowerCase();
|
|
var visible = 0;
|
|
$('#set-modal-tbody .set-modal-row').each(function () {
|
|
var hay = $(this).attr('data-set-search') || '';
|
|
var match = !q || hay.indexOf(q) !== -1;
|
|
$(this).toggle(match);
|
|
if (match) {
|
|
visible++;
|
|
}
|
|
});
|
|
$('#set-modal-no-results').toggleClass('d-none', !(q.length > 0 && visible === 0));
|
|
});
|
|
|
|
$('#modal-set-pick').on('show.bs.modal', function () {
|
|
$('#set-modal-search').val('');
|
|
$('#set-modal-tbody .set-modal-row').show();
|
|
$('#set-modal-no-results').addClass('d-none');
|
|
syncSetModalRows();
|
|
});
|
|
|
|
$('#btn-set-modal-add').on('click', function () {
|
|
var added = 0;
|
|
$('#modal-set-pick .js-set-modal-cb:checked:not(:disabled)').each(function () {
|
|
appendSetRow(String($(this).val()));
|
|
$(this).prop('checked', false);
|
|
added++;
|
|
});
|
|
if (added > 0) {
|
|
syncSetModalRows();
|
|
$('#modal-set-pick').modal('hide');
|
|
}
|
|
});
|
|
|
|
$(document).on('click', '.set-row-remove', function (ev) {
|
|
ev.preventDefault();
|
|
$(this).closest('tr').remove();
|
|
syncSetModalRows();
|
|
});
|
|
|
|
function toggleSetMode() {
|
|
var isSet = $('#is_set').is(':checked');
|
|
$('.js-set-fields').toggle(isSet);
|
|
$('#product-section-rezeptur, #product-section-herstellerrezeptur, #product-section-verpackung, #product-section-warenwirtschaft').toggle(!isSet);
|
|
$('.js-nav-recipe').toggleClass('d-none', isSet);
|
|
}
|
|
$(document).on('change', '#is_set', toggleSetMode);
|
|
toggleSetMode();
|
|
|
|
function toggleOutOfStock() {
|
|
var indefinite = $('#out_of_stock_indefinite').is(':checked');
|
|
var active = $('#out_of_stock_active').is(':checked');
|
|
$('#out_of_stock_active').prop('disabled', indefinite);
|
|
$('.js-out-of-stock-days').toggle(active && !indefinite);
|
|
}
|
|
$(document).on('change', '#out_of_stock_active, #out_of_stock_indefinite', toggleOutOfStock);
|
|
toggleOutOfStock();
|
|
});
|
|
})(jQuery);
|
|
</script>
|
|
@endsection
|