feat(est): link quantity to individual rows

This commit is contained in:
2026-11-07 07:51:42 +09:00
parent b6d98ac9d5
commit b279b964ce
6 changed files with 89 additions and 20 deletions
+47 -12
View File
@@ -151,18 +151,34 @@ const EstQuantity = (() => {
const quantities = new Map();
for (const item of items) {
const code = item.TargetCode ?? item.LinkCode;
if (!code) continue;
const targets = project.tables.EstMainDat.filter((r) => r.Code === code && !Number(r.Level));
if (!targets.length) throw Error(`연결 내역코드 없음: ${code}`);
if (targets.length > 1) throw Error(`연결 내역코드 중복: ${code}`);
quantities.set(code, (quantities.get(code) || 0) + Number(item.GN || 0));
if (!code && !item.TargetKey) continue;
const targets = project.tables.EstMainDat.map((row, index) => ({ row, index })).filter(
({ row }) =>
!Number(row.Level) &&
(item.TargetKey
? row.PkID != null && `id:${row.PkID}` === item.TargetKey
: row.Code === code),
);
if (!targets.length) throw Error(`연결 내역 없음: ${item.TargetKey || code}`);
if (targets.length > 1) throw Error(`연결 내역 중복: ${item.TargetKey || code}`);
const index = targets[0].index;
quantities.set(index, (quantities.get(index) || 0) + Number(item.GN || 0));
}
if (!quantities.size) throw Error("연결할 내역코드를 먼저 입력하세요.");
return quantities;
}
function applyTotals(project, quantities) {
for (const row of project.tables.EstMainDat)
if (quantities.has(row.Code) && !Number(row.Level)) row.GN = quantities.get(row.Code);
for (const [index, value] of quantities) project.tables.EstMainDat[index].GN = value;
}
function targetSelect(project, item, section, index) {
const { esc } = EstApp;
const rows = project.tables.EstMainDat.filter((row) => row.PkID != null && !Number(row.Level));
return `<select data-quantity-select="${section}" data-index="${index}" aria-label="연결 내역 행"><option value="">내역 행 선택</option>${rows
.map((row) => {
const key = `id:${row.PkID}`;
return `<option value="${esc(key)}" ${item.TargetKey === key ? "selected" : ""}>${esc(row.Name || "내역")} · ${esc(row.Code || "코드 없음")} · ${esc(row.PkID)}</option>`;
})
.join("")}</select>`;
}
function newCode(items) {
const codes = new Set(items.map((row) => row.Code));
@@ -186,7 +202,7 @@ const EstQuantity = (() => {
{ esc, number, byId } = EstApp;
byId("work-content").innerHTML =
selector(state) +
`<div class="business-tools"><label>산출서명 <input data-quantity-name value="${esc(sheet.Name)}"></label><button data-quantity-add>산출 행 추가</button><button data-quantity-apply>내역 수량 반영</button><button data-quantity-delete-sheet>산출서 삭제</button></div><table class="sheet"><thead><tr><th>산출코드</th><th>품명</th><th>규격</th><th>저장 수량</th><th>재계산 수량</th><th>연결 내역코드</th></tr></thead><tbody>${output.items.map((item, index) => `<tr><td>${esc(item.Code)}</td><td><input data-quantity-item="Name" data-index="${index}" value="${esc(item.Name || "")}" aria-label="품명"></td><td><input data-quantity-item="Size" data-index="${index}" value="${esc(item.Size || "")}" aria-label="규격"></td><td>${number(sheet.items[index].GN)}</td><td>${number(item.GN)}</td><td><input data-quantity-target="${index}" value="${esc(item.TargetCode ?? item.LinkCode ?? "")}" aria-label="연결 내역코드"></td></tr>`).join("")}</tbody></table><p class="business-note">${esc(output.errors.join(" / "))} 산출 행의 결과 표지로 수량 항목을 연결합니다. 신규 산출서와 행 변경은 JSON으로 저장합니다.</p><table class="sheet"><thead><tr><th>줄</th><th>산출근거 편집</th><th>작업</th></tr></thead><tbody>${sheet.rows.map((row, index) => `<tr><td>${index + 1}</td><td><textarea data-quantity-line="${index}" rows="1" style="min-width:750px" aria-label="산출근거 ${index + 1}">${esc(EstText.display(output.rows[index].LineData))}</textarea></td><td><button data-quantity-add data-index="${index}">아래 추가</button><button data-quantity-move="-1" data-index="${index}" ${index === 0 ? "disabled" : ""}>↑</button><button data-quantity-move="1" data-index="${index}" ${index === sheet.rows.length - 1 ? "disabled" : ""}>↓</button><button data-quantity-delete-row="${index}">삭제</button></td></tr>`).join("")}</tbody></table>`;
`<div class="business-tools"><label>산출서명 <input data-quantity-name value="${esc(sheet.Name)}"></label><button data-quantity-add>산출 행 추가</button><button data-quantity-apply>내역 수량 반영</button><button data-quantity-delete-sheet>산출서 삭제</button></div><table class="sheet"><thead><tr><th>산출코드</th><th>품명</th><th>규격</th><th>저장 수량</th><th>재계산 수량</th><th>연결 내역코드</th></tr></thead><tbody>${output.items.map((item, index) => `<tr><td>${esc(item.Code)}</td><td><input data-quantity-item="Name" data-index="${index}" value="${esc(item.Name || "")}" aria-label="품명"></td><td><input data-quantity-item="Size" data-index="${index}" value="${esc(item.Size || "")}" aria-label="규격"></td><td>${number(sheet.items[index].GN)}</td><td>${number(item.GN)}</td><td><input data-quantity-target="${index}" value="${esc(item.TargetCode ?? item.LinkCode ?? "")}" aria-label="연결 내역코드">${targetSelect(state.project, item, "sheet", index)}</td></tr>`).join("")}</tbody></table><p class="business-note">${esc(output.errors.join(" / "))} 산출 행의 결과 표지로 수량 항목을 연결합니다. 신규 산출서와 행 변경은 JSON으로 저장합니다.</p><table class="sheet"><thead><tr><th>줄</th><th>산출근거 편집</th><th>작업</th></tr></thead><tbody>${sheet.rows.map((row, index) => `<tr><td>${index + 1}</td><td><textarea data-quantity-line="${index}" rows="1" style="min-width:750px" aria-label="산출근거 ${index + 1}">${esc(EstText.display(output.rows[index].LineData))}</textarea></td><td><button data-quantity-add data-index="${index}">아래 추가</button><button data-quantity-move="-1" data-index="${index}" ${index === 0 ? "disabled" : ""}>↑</button><button data-quantity-move="1" data-index="${index}" ${index === sheet.rows.length - 1 ? "disabled" : ""}>↓</button><button data-quantity-delete-row="${index}">삭제</button></td></tr>`).join("")}</tbody></table>`;
}
function install() {
document.addEventListener("change", (event) => {
@@ -197,6 +213,23 @@ const EstQuantity = (() => {
EstApp.render();
return;
}
if (node.dataset.quantitySelect !== undefined) {
const item =
node.dataset.quantitySelect === "manual"
? state.project.quantities[Number(node.dataset.index)]
: state.project.quantitySheets?.find((s) => s.Code === state.quantitySheet)?.items[
Number(node.dataset.index)
];
if (!item) return;
const row = state.project.tables.EstMainDat.find(
(r) => r.PkID != null && `id:${r.PkID}` === node.value && !Number(r.Level),
);
EstApp.change(() => {
item.TargetKey = row ? node.value : "";
item.TargetCode = row?.Code || "";
}, false);
return;
}
const sheet = state.project.quantitySheets?.find((s) => s.Code === state.quantitySheet);
if (!sheet) return;
if (node.dataset.quantityName !== undefined)
@@ -213,10 +246,11 @@ const EstQuantity = (() => {
false,
);
if (node.dataset.quantityTarget !== undefined)
EstApp.change(
() => (sheet.items[Number(node.dataset.quantityTarget)].TargetCode = node.value),
false,
);
EstApp.change(() => {
const item = sheet.items[Number(node.dataset.quantityTarget)];
item.TargetCode = node.value;
item.TargetKey = "";
}, false);
});
document.addEventListener("click", (event) => {
const { state } = EstApp;
@@ -298,6 +332,7 @@ const EstQuantity = (() => {
manual,
totals,
applyTotals,
targetSelect,
newCode,
selector,
render,