fix(est): allocate annual component amounts
This commit is contained in:
@@ -648,3 +648,47 @@ def test_text_total_without_result_marker_evaluates_its_expression():
|
||||
assert result["rows"][0]["TC"] == 20
|
||||
assert result["rows"][1]["TC"] == 20
|
||||
assert result["rows"][2]["TC"] == 0
|
||||
|
||||
|
||||
def test_annual_formulas_split_component_amounts_and_preserve_remainder():
|
||||
result = run(
|
||||
"math",
|
||||
"""(()=>{
|
||||
const p=EstNative.empty();p.options.SourceRefresh=false;
|
||||
p.tables.EstMainDat=[{PkID:1,Code:'R',Name:'item',GN:1,NU:5,GU:3},{LinkIndex:54}];
|
||||
p.annual=[{name:'1차',formula:'[기준]*0.5'},{name:'2차',formula:'[기준]-[1차]'}];
|
||||
const q=EstNative.calculate(p),before=JSON.stringify(q);
|
||||
return {output:EstBusiness.annual(q),preserved:before===JSON.stringify(q)};
|
||||
})()""",
|
||||
)
|
||||
assert result["preserved"]
|
||||
output = result["output"]
|
||||
assert output["errors"] == []
|
||||
first, second = [item["project"] for item in output["results"]]
|
||||
assert first["tables"]["EstMainDat"][0]["GN"] == 0.5
|
||||
assert second["tables"]["EstMainDat"][0]["GN"] == 0.5
|
||||
assert [item["calculation"]["direct"]["TC"] for item in [first, second]] == [3, 5]
|
||||
assert first["tables"]["EstMainDat"][0]["NC"] == 2
|
||||
assert second["tables"]["EstMainDat"][0]["NC"] == 3
|
||||
assert second["tables"]["EstMainDat"][0]["GC"] == 2
|
||||
assert all(first["tables"]["EstMainDat"][0][key] == 0 for key in ["TU", "NU", "GU", "JU"])
|
||||
|
||||
|
||||
def test_annual_fixed_amount_with_zero_quantity_and_duplicate_names():
|
||||
result = run(
|
||||
"math",
|
||||
"""(()=>{
|
||||
const p=EstNative.empty();p.options.SourceRefresh=false;
|
||||
p.tables.EstMainDat=[{PkID:1,Code:'R',GN:0,NC:3,GC:7,TC:10,CalcIndex:1},{LinkIndex:54}];
|
||||
p.annual=[{name:'1차',formula:'[기준]*0.5'},{name:'2차',formula:'[기준]-[1차]'}];
|
||||
const output=EstBusiness.annual(EstNative.calculate(p));
|
||||
p.annual[1].name='1차';
|
||||
return {output,invalid:EstBusiness.annual(p)};
|
||||
})()""",
|
||||
)
|
||||
assert result["output"]["errors"] == []
|
||||
assert [
|
||||
item["project"]["calculation"]["direct"]["TC"] for item in result["output"]["results"]
|
||||
] == [4, 6]
|
||||
assert result["invalid"]["results"] == []
|
||||
assert len(result["invalid"]["errors"]) == 1
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
단가 변경은 산근 소계(61), 비목 참조 비율(58), 선택 구간 합계(62)를 거쳐 내역으로 전달됩니다. 산출근거 편집기의 `구간지정`·`구간합계` 표지로 A~Z 묶음을 제어합니다. 소계 표지 1,433개를 추가한 대용량 원본 대조는 115,145개 일치·해석 오류 0이며, 원본 산근 낙찰 행(52)의 성분 계산과 연결 편집도 회귀 시험합니다.
|
||||
|
||||
연차공사의 `[기준]*0.5` 수식은 수량과 각 성분 금액에 각각 적용됩니다. 금액은 정수 절사하고, 다음 차수의 `[기준]-[1차]`로 절사 후 잔액을 배분합니다. 차수 사본은 단가를 비운 고정 금액으로 저장하여 다시 열어도 금액을 유지합니다. `[계약수량]`은 이전 작업 파일과의 호환 표기로 `[기준]`과 같은 역할을 합니다.
|
||||
|
||||
**전체 원본 동등성은 아직 확보하지 못했습니다.** 기성·물가변동·연차 등은 분석 분기와 합성 시나리오를 시험했으며 원본 업무 샘플 대조는 없습니다. 원본 화면 실행 비교는 못했습니다. 원본 페이지·도면 배치, CAD/VEC0650 편집, 원본 인쇄 서식, Excel/XML/JDL 변환, ESTX 수정 저장은 미구현입니다. 원본 대체품의 완료 판정으로 보지 않습니다.
|
||||
|
||||
검사:
|
||||
|
||||
@@ -393,8 +393,7 @@ const EstActions = (() => {
|
||||
}, false);
|
||||
} else if (name === "add-annual")
|
||||
EstApp.change(
|
||||
() =>
|
||||
project.annual.push({ name: `${project.annual.length + 1}차`, formula: "[계약수량]" }),
|
||||
() => project.annual.push({ name: `${project.annual.length + 1}차`, formula: "[기준]" }),
|
||||
false,
|
||||
);
|
||||
else if (name === "open-annual") {
|
||||
|
||||
@@ -336,11 +336,16 @@ const EstBusiness = (() => {
|
||||
function annual(project) {
|
||||
const errors = [],
|
||||
results = [];
|
||||
const names = (project.annual || []).map((year) => String(year.name || ""));
|
||||
if (names.some((name) => !name.trim()) || new Set(names).size !== names.length)
|
||||
return { results, errors: ["차수 이름은 비어 있거나 중복될 수 없습니다."] };
|
||||
for (const year of project.annual || []) {
|
||||
const next = copy(project);
|
||||
next.name = `${project.name} · ${year.name}`;
|
||||
next.progress = [];
|
||||
next.annual = [];
|
||||
next.options.TotalFix = false;
|
||||
next.options.SourceRefresh = false;
|
||||
for (let index = 0; index < next.tables.EstMainDat.length; index++) {
|
||||
const row = next.tables.EstMainDat[index];
|
||||
if (num(row.LinkIndex) === 57) {
|
||||
@@ -351,11 +356,20 @@ const EstBusiness = (() => {
|
||||
}
|
||||
if (num(row.Level) > 0 || [54, 55, 58, 59, 62, 67].includes(num(row.LinkIndex))) continue;
|
||||
try {
|
||||
row.GN = EstExpression.evaluate(year.formula || "[계약수량]", (name) => {
|
||||
if (name === "계약수량") return num(project.tables.EstMainDat[index].GN);
|
||||
const previous = results.find((r) => r.name === name);
|
||||
return previous ? num(previous.project.tables.EstMainDat[index]?.GN) : undefined;
|
||||
});
|
||||
// f0f9b8/f1098c resolve the same column formula against each current field.
|
||||
const value = (field) =>
|
||||
EstExpression.evaluate(year.formula || "[기준]", (name) => {
|
||||
if (["기준", "계약", "계약수량"].includes(name))
|
||||
return num(project.tables.EstMainDat[index][field]);
|
||||
const previous = results.find((result) => result.name === name);
|
||||
return previous ? num(previous.project.tables.EstMainDat[index]?.[field]) : undefined;
|
||||
});
|
||||
row.GN = value("GN");
|
||||
components.forEach((key) => (row[key] = Math.trunc(value(key))));
|
||||
row.TC = sum(row);
|
||||
units.forEach((key) => (row[key] = 0));
|
||||
row.TU = 0;
|
||||
row.CalcIndex = num(row.CalcIndex) >= 2 ? 3 : 1;
|
||||
} catch (error) {
|
||||
errors.push(`${year.name}: ${error.message}`);
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ const EstExtra = (() => {
|
||||
"연차공사",
|
||||
button("add-annual", "차수 추가"),
|
||||
table(
|
||||
["차수", "수량 배분 수식", "순공사비", "도급액", "작업", ""],
|
||||
["차수", "수량·금액 배분 수식", "순공사비", "도급액", "작업", ""],
|
||||
project.annual.map((year, index) => [
|
||||
field("annual", index, "name", year.name, "text"),
|
||||
field("annual", index, "formula", year.formula, "text"),
|
||||
@@ -232,7 +232,7 @@ const EstExtra = (() => {
|
||||
button("delete-extra", "삭제", `data-section="annual" data-row="${index}"`),
|
||||
]),
|
||||
),
|
||||
`수식 예: [계약수량]*0.5, [계약수량]-[1차]. ${esc(output.errors.join(" / "))}`,
|
||||
`수식 예: [기준]*0.5, [기준]-[1차]. 같은 수식을 수량과 각 성분 금액에 따로 적용하고 금액은 원 미만 절사합니다. ${esc(output.errors.join(" / "))}`,
|
||||
);
|
||||
} else if (view === "resources") {
|
||||
const output = EstResources.calculate(project);
|
||||
|
||||
Reference in New Issue
Block a user