fix(est): preserve precision and parse source notes

This commit is contained in:
2026-11-05 17:57:49 +09:00
parent 3e9fb28714
commit f255ebc0fe
3 changed files with 143 additions and 11 deletions
+17 -9
View File
@@ -18,19 +18,21 @@ const EstText = (() => {
.replace(/[;;]/g, ":");
function expression(text) {
return clean(
String(text).replace(
/\x0e[^\x0e]*\x0e|\x13[^\x13]*\x13|\x15[^\x15]*\x15|\{[^}]*\}|〔[^〕]*〕|[[^]]*]|\([가-힣][^()]*\)|\(\s*\d+(?:\.\d+)?\s*[˚°]\s*\)|<[^>]*>/g,
"",
),
String(text)
.replace(///g, "/")
.replace(
/\x0e[^\x0e]*\x0e|\x13[^\x13]*\x13|\x15[^\x15]*\x15|\{[^}]*\}|〔[^〕]*〕|[[^]]*]|\[[가-힣][^\]]*\]|\([가-힣][^()]*\)|\(\s*\d+(?:\.\d+)?\s*[˚°]\s*\)|<[^>]*>/g,
"",
),
)
.replace(
/(?<![A-Za-z0-9_가-힣.])(\d[\d,.]*|\.\d+)\s*(?:공\/?㎥|공|단|면|겹|경간|개월|월|년|인|㎥|㎢|㎠|㎡|hr|sec|min|set|kwh|kw|km|ton|kg|㎏|㎞|ℓ|회|개소|개|대|분|초|본|장|매|병|조|식|일|시간|원|포|동|주|톤|배|번|줄|μ|₩|EA|㎜|㎝|mm|cm|m3|m2|m|g|L|a)(?:\/(?:개소|경간|개|대|본|인|분|포|매|주|톤|공|회|조|span|set|ton|\$|hr|일|㎥|㎡|m3|m2|m|a|EA))?/gi,
/(?<![A-Za-z0-9_가-힣.])(\d[\d,.]*|\.\d+)\s*(?:공\/?㎥|공|단|면|겹|경간|개월|월|년|인|㎥|㎢|㎠|㎡|hr|sec|min|set|kwh|kw|km|ton|kg|㎏|㎞|ℓ|회|개소|개|대|분|초|본|장|매|병|조|식|일|시간|원|포|동|주|톤|배|번|줄|μ|₩|₩|\\|EA|㎜|㎝|mm|cm|m3|m2|m|g|L|a)(?:\/(?:개소|경간|개|대|본|인|분|포|매|주|톤|공|회|조|span|set|ton|\$|hr|일|㎥|㎡|m3|m2|m|a|EA))?/gi,
"$1",
)
.replace(/(?<=\d)\s*\/\s*(?:일|㎥|㎡|hr)(?=\s|$|[×*+)])/gi, "")
.replace(/[】]〕]\s*$/, "")
.replace(/(?<=\))\s*(?:개|인|본|면|단|회|㎥|㎡)(?:\/(?:㎡|㎥|hr))?/g, "")
.replace(/^\s*[-*•]?\s*[가-힣][가-힣\s]*\s+(?=[\d.(])/, "")
.replace(/^\s*[-*•※]?\s*[가-힣㎡㎥][가-힣㎡㎥\s]*\s+(?=[\d.(])/, "")
.replace(/[①-⑳]/g, "")
.replace(/=\s*$/, "");
}
@@ -180,7 +182,8 @@ const EstText = (() => {
? EstMath.precision(row)
: { digits: 1, method: 2 };
const subtotal = floating(
sumCurrency(values),
// f251d5-f251df accumulates Extended values before applying the row precision.
values.reduce((total, value) => total + value, 0),
options.CostValueTrunc && sc < 0 ? 0 : precision.digits,
precision.method,
);
@@ -318,9 +321,9 @@ const EstText = (() => {
? []
: [
...assignmentText.matchAll(
/(?:^\s*| {2,}|[,,]\s*|(?<=[가-힣])\s+|[::]\s*|['"]\s*)([A-Za-z][A-Za-z0-9]*)\s*=/g,
/(?:^\s*| {2,}|[,,]\s*|(?<=[가-힣])\s+|(?<=hr)\s+|[::]\s*|['"]\s*)([A-Za-z][A-Za-z0-9]*)\s*=/g,
),
];
].filter((match) => !/[+*/÷×−-]\s*$/.test(assignmentText.slice(0, match.index)));
for (let j = 0; j < assignments.length; j++) {
const assignment = assignments[j],
end = assignments[j + 1]?.index ?? text.length;
@@ -336,6 +339,10 @@ const EstText = (() => {
continue;
}
const result = body.match(marker);
if (!result && /\d\s*[~~]\s*\d/.test(body)) {
delete variables[assignment[1]];
continue;
}
const parts = body.split("=");
body = parts[0];
body = expression(body)
@@ -385,6 +392,7 @@ const EstText = (() => {
.filter((x) => x.trim())
.pop();
if (body.includes(":")) body = body.slice(body.indexOf(":") + 1);
if (/^\s*\{[\s\S]*\}\s*$/.test(text)) body = body.replace(/^\s*\{/, "");
body = expression(body)
.replace(/\s*([×÷·+*/−-])\s*/g, "$1")
.trim()