diff --git a/src/index.html b/src/index.html
index 0e7c7ed..b0aa3d9 100644
--- a/src/index.html
+++ b/src/index.html
@@ -576,11 +576,30 @@ let estado = {
// ─── Helpers ──────────────────────────────────────────────────────────────
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 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 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
function fmtTime(v) {
@@ -839,45 +858,43 @@ function updateTotals() {
// Strategy: detect "comma-only" input and convert to ",00" immediately.
// 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 => {
const el = document.getElementById(`sys-${k}`);
if (!el) return;
- // On input: handle comma-only typed as immediate ,00
+ // On input: just track for autosave
el.addEventListener('input', () => {
- const v = el.value;
- // If user just typed a comma as last char (and no ,00 yet), convert to ,00
- 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();
+ 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 => {
if (e.key === 'Enter') {
- _justEnteredDatalist = true;
- el.blur();
- el.focus();
- setTimeout(() => { _justEnteredDatalist = false; }, 400);
+ e.preventDefault();
+ const raw = el.value.trim();
+ if (!raw) { el.value = ''; return; }
+ 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', () => {
- if (_justEnteredDatalist) return;
- const v = parseNum(el.value);
- if (isNaN(v) || v === 0) {
- el.value = '';
- } else {
- el.value = fmtNum(v);
- }
+ const raw = el.value.trim();
+ if (!raw) { el.value = ''; return; }
+ const num = parseNum(raw);
+ if (isNaN(num) || num === 0) { el.value = ''; }
+ else { el.value = fmtNum(num); }
updateTotals();
+ scheduleSave();
});
});
@@ -887,32 +904,23 @@ let _justEnteredDatalist = false;
el.addEventListener('input', () => {
const v = el.value;
- if (v.endsWith(',') && !v.endsWith(',00')) {
+ if (v.endsWith(',') && !v.includes(',') && v !== ',') {
el.value = v + '00';
const pos = v.length;
setTimeout(() => { el.selectionStart = el.selectionEnd = pos; }, 0);
}
- updateTotals(); scheduleSave();
- });
-
- el.addEventListener('keydown', e => {
- if (e.key === 'Enter') {
- _justEnteredDatalist = true;
- el.blur();
- el.focus();
- setTimeout(() => { _justEnteredDatalist = false; }, 400);
- }
+ updateTotals();
+ scheduleSave();
});
el.addEventListener('blur', () => {
- if (_justEnteredDatalist) return;
- const v = parseNum(el.value);
- if (isNaN(v) || v === 0) {
- el.value = '';
- } else {
- el.value = fmtNum(v);
- }
+ const raw = el.value.trim();
+ if (!raw) { el.value = ''; return; }
+ const num = parseNum(raw);
+ if (isNaN(num) || num === 0) { el.value = ''; }
+ else { el.value = fmtNum(num); }
updateTotals();
+ scheduleSave();
});
});