v22: modal confirm, validate operador/turno, print from confirm, fix parseNum/fmtNum
This commit is contained in:
+52
-44
@@ -576,11 +576,30 @@ let estado = {
|
|||||||
|
|
||||||
// ─── Helpers ──────────────────────────────────────────────────────────────
|
// ─── Helpers ──────────────────────────────────────────────────────────────
|
||||||
const fmt = v => 'R$ ' + (parseFloat(v) || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2 });
|
const fmt = v => 'R$ ' + (parseFloat(v) || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2 });
|
||||||
const parseNum = v => parseFloat((String(v) || '0').replace(/\./g,'').replace(',','.')) || 0;
|
const parseNum = v => {
|
||||||
|
const s = String(v || '0');
|
||||||
|
// Handle empty or whitespace
|
||||||
|
if (!s.trim()) return 0;
|
||||||
|
// Brazilian format: 1.234,56 → 1234.56
|
||||||
|
// Strategy: remove thousand dots, replace decimal comma with dot
|
||||||
|
// "10,55" → "1055,00" (wrong) → fix: split on comma first
|
||||||
|
// "1.055,00" → parts=["1.055","00"] → intPart="1055", decPart="00" → 1055.00
|
||||||
|
const lastComma = s.lastIndexOf(',');
|
||||||
|
if (lastComma === -1) {
|
||||||
|
// No comma: just remove thousand separators (dots)
|
||||||
|
return parseFloat(s.replace(/,/g, '').replace(/\./g, '')) || 0;
|
||||||
|
}
|
||||||
|
const intPart = s.slice(0, lastComma).replace(/\./g, '').replace(/,/g, '');
|
||||||
|
const decPart = s.slice(lastComma + 1);
|
||||||
|
return parseFloat(intPart + '.' + decPart) || 0;
|
||||||
|
};
|
||||||
const setText = (id, t) => { const e = document.getElementById(id); if (e) e.textContent = t; };
|
const setText = (id, t) => { const e = document.getElementById(id); if (e) e.textContent = t; };
|
||||||
const setVal = (id, v) => { const e = document.getElementById(id); if (e) e.value = v; };
|
const setVal = (id, v) => { const e = document.getElementById(id); if (e) e.value = v; };
|
||||||
const val = id => { const e = document.getElementById(id); return e ? e.value : ''; };
|
const val = id => { const e = document.getElementById(id); return e ? e.value : ''; };
|
||||||
const fmtNum = v => { const n = parseNum(v); return isNaN(n) ? '' : n.toLocaleString('pt-BR', { minimumFractionDigits: 2 }); };
|
const fmtNum = v => {
|
||||||
|
const n = typeof v === 'number' ? v : parseNum(v);
|
||||||
|
return isNaN(n) ? '' : n.toLocaleString('pt-BR', { minimumFractionDigits: 2 });
|
||||||
|
};
|
||||||
|
|
||||||
// Format time: 4 digits → HH:MM
|
// Format time: 4 digits → HH:MM
|
||||||
function fmtTime(v) {
|
function fmtTime(v) {
|
||||||
@@ -839,45 +858,43 @@ function updateTotals() {
|
|||||||
// Strategy: detect "comma-only" input and convert to ",00" immediately.
|
// Strategy: detect "comma-only" input and convert to ",00" immediately.
|
||||||
// For normal digit input: add ,00 on blur (not on every digit).
|
// For normal digit input: add ,00 on blur (not on every digit).
|
||||||
|
|
||||||
let _justEnteredDatalist = false;
|
// ─── Numeric input: auto ,00 on digit+blur pattern ───────────────────────
|
||||||
|
// These fields are plain text inputs (no datalist). Format on blur only.
|
||||||
|
// For sys-troco which is user-editable: also format on Enter keydown.
|
||||||
|
|
||||||
['troco','credito','debito','alimentacao','vales','receber','pix'].forEach(k => {
|
['troco','credito','debito','alimentacao','vales','receber','pix'].forEach(k => {
|
||||||
const el = document.getElementById(`sys-${k}`);
|
const el = document.getElementById(`sys-${k}`);
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
|
|
||||||
// On input: handle comma-only typed as immediate ,00
|
// On input: just track for autosave
|
||||||
el.addEventListener('input', () => {
|
el.addEventListener('input', () => {
|
||||||
const v = el.value;
|
updateTotals();
|
||||||
// If user just typed a comma as last char (and no ,00 yet), convert to ,00
|
scheduleSave();
|
||||||
if (v.endsWith(',') && !v.endsWith(',00')) {
|
|
||||||
el.value = v + '00';
|
|
||||||
// Move cursor before the trailing 00
|
|
||||||
const pos = v.length; // position after the comma
|
|
||||||
setTimeout(() => { el.selectionStart = el.selectionEnd = pos; }, 0);
|
|
||||||
}
|
|
||||||
updateTotals(); scheduleSave();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// On Enter: trigger datalist, then blur+focus cycle
|
// On keydown Enter: format value directly (Enter does not trigger blur for tabindex=-1)
|
||||||
el.addEventListener('keydown', e => {
|
el.addEventListener('keydown', e => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
_justEnteredDatalist = true;
|
e.preventDefault();
|
||||||
el.blur();
|
const raw = el.value.trim();
|
||||||
el.focus();
|
if (!raw) { el.value = ''; return; }
|
||||||
setTimeout(() => { _justEnteredDatalist = false; }, 400);
|
const num = parseNum(raw);
|
||||||
|
if (isNaN(num) || num === 0) { el.value = ''; }
|
||||||
|
else { el.value = fmtNum(num); }
|
||||||
|
updateTotals();
|
||||||
|
scheduleSave();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// On blur: format to proper number if it looks like a partial entry
|
// On blur: format the value
|
||||||
el.addEventListener('blur', () => {
|
el.addEventListener('blur', () => {
|
||||||
if (_justEnteredDatalist) return;
|
const raw = el.value.trim();
|
||||||
const v = parseNum(el.value);
|
if (!raw) { el.value = ''; return; }
|
||||||
if (isNaN(v) || v === 0) {
|
const num = parseNum(raw);
|
||||||
el.value = '';
|
if (isNaN(num) || num === 0) { el.value = ''; }
|
||||||
} else {
|
else { el.value = fmtNum(num); }
|
||||||
el.value = fmtNum(v);
|
|
||||||
}
|
|
||||||
updateTotals();
|
updateTotals();
|
||||||
|
scheduleSave();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -887,32 +904,23 @@ let _justEnteredDatalist = false;
|
|||||||
|
|
||||||
el.addEventListener('input', () => {
|
el.addEventListener('input', () => {
|
||||||
const v = el.value;
|
const v = el.value;
|
||||||
if (v.endsWith(',') && !v.endsWith(',00')) {
|
if (v.endsWith(',') && !v.includes(',') && v !== ',') {
|
||||||
el.value = v + '00';
|
el.value = v + '00';
|
||||||
const pos = v.length;
|
const pos = v.length;
|
||||||
setTimeout(() => { el.selectionStart = el.selectionEnd = pos; }, 0);
|
setTimeout(() => { el.selectionStart = el.selectionEnd = pos; }, 0);
|
||||||
}
|
}
|
||||||
updateTotals(); scheduleSave();
|
updateTotals();
|
||||||
});
|
scheduleSave();
|
||||||
|
|
||||||
el.addEventListener('keydown', e => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
_justEnteredDatalist = true;
|
|
||||||
el.blur();
|
|
||||||
el.focus();
|
|
||||||
setTimeout(() => { _justEnteredDatalist = false; }, 400);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('blur', () => {
|
el.addEventListener('blur', () => {
|
||||||
if (_justEnteredDatalist) return;
|
const raw = el.value.trim();
|
||||||
const v = parseNum(el.value);
|
if (!raw) { el.value = ''; return; }
|
||||||
if (isNaN(v) || v === 0) {
|
const num = parseNum(raw);
|
||||||
el.value = '';
|
if (isNaN(num) || num === 0) { el.value = ''; }
|
||||||
} else {
|
else { el.value = fmtNum(num); }
|
||||||
el.value = fmtNum(v);
|
|
||||||
}
|
|
||||||
updateTotals();
|
updateTotals();
|
||||||
|
scheduleSave();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user