feat(est): show existing quantity links
This commit is contained in:
@@ -23,7 +23,7 @@ Ctrl+S는 입력 중인 셀도 확정한 뒤 저장합니다. 연속 저장 요
|
||||
|
||||
당초 확정 사본에는 재계산 입력을 한 번만 보관하며, 작업본의 도면·원본 압축 자료는 보존합니다. 설치된 `main.estx`의 당초 확정 후 실제 저장 API를 통과했고, 산근 77,395행과 원본 압축 해시 보존·계산 오류 0을 확인했습니다(요청 약 215MB, 저장 약 221MB).
|
||||
|
||||
초기 자료는 기술정보 DB의 실제 REA 저장 샘플입니다. `REA 샘플 대조`에서 원본 저장값과 275개 계산 결과를 비교합니다. 테스트용 임의 요율을 원본 기본값으로 넣지 않습니다. 새 원가 서식의 요율은 사용자가 입력합니다.
|
||||
초기 자료는 기술정보 DB의 실제 REA 저장 샘플입니다. `REA 샘플 대조`에서 원본 저장값과 275개 계산 결과를 비교합니다. 기존 ESTX의 기본 수량 연결은 문서·항목 코드와 내역 코드가 유일한 경우 화면에 표시하며, 중복·다른 모드는 원문에 보존하고 경고합니다. 원본 연결 데이터 실자료 검증과 새 연결의 ESTX 쓰기는 아직 지원하지 않습니다. 테스트용 임의 요율을 원본 기본값으로 넣지 않습니다. 새 원가 서식의 요율은 사용자가 입력합니다.
|
||||
|
||||
## 검증과 남은 범위
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
const zlib = require("node:zlib");
|
||||
const quantity = require("./native-quantity.cjs");
|
||||
const { readPrintOptions } = require("./import-print.cjs");
|
||||
const { existingTargets } = require("./native-links.cjs");
|
||||
const decoder = new TextDecoder("euc-kr", { fatal: true });
|
||||
const LIMIT = 256 * 1024 * 1024;
|
||||
const crcTable = Array.from({ length: 256 }, (_, n) => {
|
||||
@@ -367,6 +368,7 @@ function importEstx(buffer, name = "가져온 내역서") {
|
||||
warnings.add(`수량산출 ${row.Code}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
existingTargets(tables, quantitySheets, warnings);
|
||||
return {
|
||||
schema: "est-web-2",
|
||||
name: props.TCommonOption?.ProjectName || name,
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// REA 0x11167e4 / 0xa911d0: MulCode is split at a tab into sheet and item codes.
|
||||
function existingTargets(tables, sheets, warnings) {
|
||||
const candidates = new Map();
|
||||
const targetCounts = new Map();
|
||||
for (const row of tables.EstMainDat || [])
|
||||
if (row.Code && !Number(row.Level))
|
||||
targetCounts.set(row.Code, (targetCounts.get(row.Code) || 0) + 1);
|
||||
for (const row of tables.EstMainDat || []) {
|
||||
if (Number(row.Level) || !row.MulCode?.binary || !row.MullangIndex || row.ExcelSheetIndex)
|
||||
continue;
|
||||
if (row.MullangIndex !== 1) {
|
||||
warnings.add("수량 연결: 기본 모드 외 연결은 원문에 보존했습니다.");
|
||||
continue;
|
||||
}
|
||||
const bytes = Buffer.from(row.MulCode.binary, "base64");
|
||||
let text;
|
||||
try {
|
||||
let wide = bytes.length > 1 && bytes.length % 2 === 0;
|
||||
for (let i = 1; wide && i < bytes.length; i += 2) wide = bytes[i] === 0;
|
||||
text = wide
|
||||
? bytes.toString("utf16le")
|
||||
: new TextDecoder("euc-kr", { fatal: true }).decode(bytes);
|
||||
} catch {
|
||||
warnings.add("수량 연결: 코드 인코딩을 읽을 수 없습니다.");
|
||||
continue;
|
||||
}
|
||||
const parts = text.replace(/\0+$/, "").split("\t");
|
||||
if (parts.length !== 2 || !parts[0] || !parts[1] || !row.Code) {
|
||||
warnings.add("수량 연결: 문서·항목 코드 또는 내역 코드가 없습니다.");
|
||||
continue;
|
||||
}
|
||||
if (targetCounts.get(row.Code) !== 1) {
|
||||
warnings.add(`수량 연결: 내역 코드 ${row.Code}가 중복되어 자동 선택하지 않았습니다.`);
|
||||
continue;
|
||||
}
|
||||
const sheet = sheets.find((value) => value.Code === parts[0]);
|
||||
const item = sheet?.items.find((value) => value.Code === parts[1]);
|
||||
if (!item) {
|
||||
warnings.add(`수량 연결: ${parts[0]}/${parts[1]} 항목을 찾을 수 없습니다.`);
|
||||
continue;
|
||||
}
|
||||
if (!candidates.has(item)) candidates.set(item, new Set());
|
||||
candidates.get(item).add(row.Code);
|
||||
}
|
||||
for (const [item, codes] of candidates)
|
||||
if (codes.size === 1) item.TargetCode = [...codes][0];
|
||||
else
|
||||
warnings.add(`수량 연결: ${item.Code}에 여러 내역 코드가 연결되어 자동 선택하지 않았습니다.`);
|
||||
}
|
||||
module.exports = { existingTargets };
|
||||
Reference in New Issue
Block a user