gruene-seele/resources/views/admin/inventory/stock-entries/_scripts.blade.php
Kevin Adametz 23381a37f5 Warenwirtschaft: ml→g-Live-Vorschau direkt am Wareneingang
Ergänzt die selbsterklärende Dichte um eine fette Live-Vorschau direkt am
Mengenfeld — in beiden Stufen des Wareneingangs:

- Einkauf erfassen (Stufe 1): bei Einheit „ml" zeigt sich beim Tippen
  „<ml> ml × <Dichte> g/ml = <g> g"; der Hinweistext bleibt Führung.
- Wareneingang buchen (Stufe 2): gleiche Vorschau am Feld „Eingegangene
  Menge", wenn in ml eingekauft wurde.

So ist an jeder Eingabestelle sofort sichtbar, wie und wann die Dichte
rechnet. Kein Mechanik-Wechsel; Speicherung bleibt in Gramm.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-14 11:47:51 +00:00

186 lines
7.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script>
(function ($) {
function toggleBlocks() {
var t = $('#entry_type').val();
var isIng = t === 'ingredient';
$('#stock-entry-ingredient-block').toggle(isIng);
$('#stock-entry-quality-block').toggle(isIng);
$('#stock-entry-packaging-block').toggle(!isIng);
$('#price-per-kg-block').toggle(isIng);
$('#price-total-block').toggle(!isIng);
}
function initIngredientSelect2() {
var $el = $('#ingredient_id');
if ($el.data('select2')) {
$el.select2('destroy');
}
$el.select2({
theme: 'default',
width: '100%',
placeholder: '{{ __('Inhaltsstoff suchen…') }}',
allowClear: true,
ajax: {
url: $el.data('search-url'),
dataType: 'json',
delay: 250,
data: function (params) {
return {q: params.term || ''};
},
processResults: function (data) {
return {results: data.results || []};
},
cache: true
},
minimumInputLength: 1
});
}
function initPackagingSelect2() {
var $el = $('#packaging_item_id');
if ($el.data('select2')) {
$el.select2('destroy');
}
$el.select2({
theme: 'default',
width: '100%',
placeholder: '{{ __('Verpackungsartikel suchen…') }}',
allowClear: true,
ajax: {
url: $el.data('search-url'),
dataType: 'json',
delay: 250,
data: function (params) {
return {
q: params.term || '',
entry_type: $('#entry_type').val()
};
},
processResults: function (data) {
return {results: data.results || []};
},
cache: true
},
minimumInputLength: 0
});
}
// AP-42: aktuelle Dichte des gewählten Rohstoffs (aus dem AJAX-Ergebnis oder
// — bei vorbelegtem Rohstoff — aus der data-density-Option).
function currentDensity() {
var sel = $('#ingredient_id').select2('data');
if (sel && sel[0] && sel[0].density != null && sel[0].density !== '') {
return parseFloat(sel[0].density);
}
var d = $('#ingredient_id option:selected').data('density');
return (d != null && d !== '') ? parseFloat(d) : null;
}
function applyOrderedUnit() {
var isIng = $('#entry_type').val() === 'ingredient';
$('#ordered-unit-wrap').toggle(isIng);
var density = isIng ? currentDensity() : null;
var hasDensity = density != null && density > 0;
$('#ordered_unit option[value="ml"]').prop('disabled', !hasDensity);
if (!hasDensity && $('#ordered_unit').val() === 'ml') {
$('#ordered_unit').val('gram');
}
var $hint = $('#ordered-quantity-hint');
var $preview = $('#ordered-quantity-preview');
$preview.hide().text('');
if (!isIng) {
$hint.text('{{ __('Bei Verpackung in Stück.') }}');
return;
}
if ($('#ordered_unit').val() === 'ml' && hasDensity) {
$hint.text('{{ __('Erfassung in ml — der Bestand wird über die Dichte in Gramm gebucht.') }}');
var ml = parseNumber($('#ordered_quantity').val());
if (ml != null && ml > 0) {
// AP-42: Live-Vorschau der ml→g-Umrechnung direkt am Mengenfeld.
$preview.text(formatQuantity(ml) + ' ml × ' + formatDensity(density) + ' g/ml = ' + formatQuantity(ml * density) + ' g').show();
}
} else if (hasDensity) {
$hint.text('{{ __('In Gramm — oder Einheit auf ml stellen (Dichte hinterlegt).') }}');
} else {
$hint.text('{{ __('In Gramm. Für ml-Erfassung zuerst die Dichte am Rohstoff pflegen.') }}');
}
}
function parseNumber(value) {
if (value === null || value === undefined) {
return null;
}
// AP-53: gleiche Regel wie reFormatWawiNumber — Punkt mit 1–2 Stellen oder "0.xxx" = Komma.
var raw = String(value).trim();
var normalized = /^-?(\d+\.\d{1,2}|0\.\d{3})$/.test(raw) ? raw : raw.replace(/\./g, '').replace(',', '.');
if (normalized === '' || isNaN(normalized)) {
return null;
}
return parseFloat(normalized);
}
// Prüfung AP-53 (4.3): Mengen (3 Stellen), Preise (2 Stellen) und Dichte (4 Stellen) getrennt formatieren.
function formatQuantity(value) {
return Number(value).toLocaleString('de-DE', {minimumFractionDigits: 2, maximumFractionDigits: 3, useGrouping: false});
}
function formatPrice(value) {
return Number(value).toFixed(2).replace('.', ',');
}
function formatDensity(value) {
return Number(value).toLocaleString('de-DE', {minimumFractionDigits: 2, maximumFractionDigits: 4, useGrouping: false});
}
function currentFactor() {
var percent = parseFloat($('#tax_rate_id option:selected').data('percent')) || 0;
return 1 + percent / 100;
}
function recalcFromNet() {
var net = parseNumber($('#price_per_kg').val());
if (net === null) {
return;
}
$('#price_per_kg_gross').val(formatPrice(net * currentFactor()));
}
function recalcFromGross() {
var gross = parseNumber($('#price_per_kg_gross').val());
if (gross === null) {
return;
}
var factor = currentFactor();
$('#price_per_kg').val(formatPrice(factor > 0 ? gross / factor : gross));
}
$(document).ready(function () {
toggleBlocks();
initIngredientSelect2();
initPackagingSelect2();
applyOrderedUnit();
$('#entry_type').on('change', function () {
toggleBlocks();
applyOrderedUnit();
$('#packaging_item_id').val(null).trigger('change');
initPackagingSelect2();
});
$('#ingredient_id').on('change', applyOrderedUnit);
$('#ordered_unit').on('change', applyOrderedUnit);
$('#ordered_quantity').on('input', applyOrderedUnit);
$('#price_per_kg').on('input', recalcFromNet);
$('#price_per_kg_gross').on('input', recalcFromGross);
$('#tax_rate_id').on('change', function () {
if (parseNumber($('#price_per_kg').val()) !== null) {
recalcFromNet();
} else {
recalcFromGross();
}
});
});
})(jQuery);
</script>