fix: correct rounding drift, XSS hazard, nookPct coupling, confirm guard, BE filename
- Rounding drift: last installment computed as remainder (contract - cumSoFar) so column sums match exactly - XSS: input values set via .value property assignment instead of attribute interpolation in innerHTML - nookPct: getInputs() now derives nookPct as 100 - ngpmPct (single source of truth) - Confirm guard: onCountChange only prompts when n differs from current installments.length - Export filename: uses Buddhist Era year (getFullYear() + 543) consistent with all date displays
This commit is contained in:
@@ -281,11 +281,12 @@ function toThaiDate(date) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getInputs() {
|
function getInputs() {
|
||||||
|
const ngpmPct = parseFloat(document.getElementById('ngpmPct').value) || 0;
|
||||||
return {
|
return {
|
||||||
contract: parseAmount(document.getElementById('contractAmount').value),
|
contract: parseAmount(document.getElementById('contractAmount').value),
|
||||||
startDate: document.getElementById('startDate').value,
|
startDate: document.getElementById('startDate').value,
|
||||||
ngpmPct: parseFloat(document.getElementById('ngpmPct').value) || 0,
|
ngpmPct,
|
||||||
nookPct: parseFloat(document.getElementById('nookPct').value) || 0,
|
nookPct: 100 - ngpmPct,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,12 +300,18 @@ function recalcAll() {
|
|||||||
installments.forEach((inst, i) => {
|
installments.forEach((inst, i) => {
|
||||||
const days = parseFloat(inst.days) || 0;
|
const days = parseFloat(inst.days) || 0;
|
||||||
const pct = parseFloat(inst.percent) || 0;
|
const pct = parseFloat(inst.percent) || 0;
|
||||||
|
const isLast = i === installments.length - 1;
|
||||||
cumDays += days;
|
cumDays += days;
|
||||||
sumPct += pct;
|
sumPct += pct;
|
||||||
|
|
||||||
const total = contract > 0 ? Math.round(contract * pct / 100) : 0;
|
let total, ngpm, nook;
|
||||||
const ngpm = Math.round(total * ngpmPct / 100);
|
if (contract > 0) {
|
||||||
const nook = total - ngpm;
|
total = isLast ? (contract - cumTotal) : Math.round(contract * pct / 100);
|
||||||
|
ngpm = isLast ? (Math.round(contract * ngpmPct / 100) - cumNgpm) : Math.round(total * ngpmPct / 100);
|
||||||
|
nook = total - ngpm;
|
||||||
|
} else {
|
||||||
|
total = 0; ngpm = 0; nook = 0;
|
||||||
|
}
|
||||||
cumTotal += total; cumNgpm += ngpm; cumNook += nook;
|
cumTotal += total; cumNgpm += ngpm; cumNook += nook;
|
||||||
|
|
||||||
const deliveryDate = hasStart ? toThaiDate(addDays(startDate, cumDays)) : '—';
|
const deliveryDate = hasStart ? toThaiDate(addDays(startDate, cumDays)) : '—';
|
||||||
@@ -360,12 +367,12 @@ function renderTable() {
|
|||||||
tr.innerHTML = `
|
tr.innerHTML = `
|
||||||
<td class="center">${i + 1}</td>
|
<td class="center">${i + 1}</td>
|
||||||
<td class="editable-cell">
|
<td class="editable-cell">
|
||||||
<input type="number" value="${inst.days}" min="0"
|
<input type="number" class="inp-days" min="0"
|
||||||
onchange="installments[${i}].days=this.value; recalcAll()"
|
onchange="installments[${i}].days=this.value; recalcAll()"
|
||||||
oninput="installments[${i}].days=this.value; recalcAll()">
|
oninput="installments[${i}].days=this.value; recalcAll()">
|
||||||
</td>
|
</td>
|
||||||
<td class="editable-cell">
|
<td class="editable-cell">
|
||||||
<input type="number" value="${inst.percent}" min="0" max="100" step="0.01"
|
<input type="number" class="inp-pct" min="0" max="100" step="0.01"
|
||||||
onchange="installments[${i}].percent=this.value; recalcAll()"
|
onchange="installments[${i}].percent=this.value; recalcAll()"
|
||||||
oninput="installments[${i}].percent=this.value; recalcAll()">
|
oninput="installments[${i}].percent=this.value; recalcAll()">
|
||||||
</td>
|
</td>
|
||||||
@@ -380,6 +387,9 @@ function renderTable() {
|
|||||||
<button class="btn-del" onclick="deleteRow(${i})" title="ลบงวดนี้">✕</button>
|
<button class="btn-del" onclick="deleteRow(${i})" title="ลบงวดนี้">✕</button>
|
||||||
</td>
|
</td>
|
||||||
`;
|
`;
|
||||||
|
// Set values safely via property assignment (not attribute interpolation)
|
||||||
|
tr.querySelector('.inp-days').value = inst.days;
|
||||||
|
tr.querySelector('.inp-pct').value = inst.percent;
|
||||||
tbody.appendChild(tr);
|
tbody.appendChild(tr);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -416,7 +426,7 @@ function deleteRow(i) {
|
|||||||
function onCountChange(n) {
|
function onCountChange(n) {
|
||||||
n = parseInt(n) || 1;
|
n = parseInt(n) || 1;
|
||||||
if (n < 1) n = 1;
|
if (n < 1) n = 1;
|
||||||
if (installments.length > 0) {
|
if (n !== installments.length && installments.length > 0) {
|
||||||
if (!confirm(`การเปลี่ยนจำนวนงวดเป็น ${n} งวด จะล้างข้อมูลเดิม ต้องการดำเนินการต่อไหม?`)) {
|
if (!confirm(`การเปลี่ยนจำนวนงวดเป็น ${n} งวด จะล้างข้อมูลเดิม ต้องการดำเนินการต่อไหม?`)) {
|
||||||
document.getElementById('installmentCount').value = installments.length;
|
document.getElementById('installmentCount').value = installments.length;
|
||||||
return;
|
return;
|
||||||
@@ -472,7 +482,7 @@ function exportExcel() {
|
|||||||
XLSX.utils.book_append_sheet(wb, ws, 'งวดงาน');
|
XLSX.utils.book_append_sheet(wb, ws, 'งวดงาน');
|
||||||
|
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const fname = `งวดงาน_${today.getFullYear()}${String(today.getMonth()+1).padStart(2,'0')}${String(today.getDate()).padStart(2,'0')}.xlsx`;
|
const fname = `งวดงาน_${today.getFullYear()+543}${String(today.getMonth()+1).padStart(2,'0')}${String(today.getDate()).padStart(2,'0')}.xlsx`;
|
||||||
XLSX.writeFile(wb, fname);
|
XLSX.writeFile(wb, fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user