Files
Aislo/resources/tester/test_est_business.py
T

248 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Annual allocation and resource expansion regression cases.
from test_est_rea_parity import run
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
def test_resource_expansion_preserves_different_labor_and_material_bid_rates():
result = run(
"math",
r"""(()=>{
const p=EstNative.empty();
p.tables.StuffcDat=[{Code:'M',Name:'material',ValueIndex:4,InputTC:100}];
p.tables.LaborcDat=[{Code:'L',Name:'labor',ValueIndex:4,InputTC:100}];
p.tables.CostMainDat=[{Index:1,Code:'C'},
{Code:'M',LinkIndex:5,SCheck:-2,LineData:' \x0ematerial\x0e: \x03100\x03 = \x12100\x12'},
{Code:'L',LinkIndex:6,SCheck:-1,LineData:' \x0elabor\x0e: \x03100\x03 = \x12100\x12'}];
p.tables.CostTitleDat=[{Code:'C'}];
p.tables.EstMainDat=[{PkID:1,Code:'C',LinkIndex:104,GN:10},{LinkIndex:54}];
const bid=EstBusiness.bid(EstNative.calculate(p),[80,90,100,100]);
const direct=EstResources.calculate(bid);
p.tables.HeadMainDat=[{Index:1,Code:'H'},{Code:'C',LinkIndex:104,GN:1}];
p.tables.HeadTitleDat=[{Code:'H'}];p.tables.EstMainDat[0].LinkIndex=103;p.tables.EstMainDat[0].Code='H';
const nested=EstResources.calculate(EstBusiness.bid(EstNative.calculate(p),[80,90,100,100]));
return {direct,nested};
})()""",
)
for output in result.values():
assert output["errors"] == []
resources = {row["Code"]: row for row in output["rows"]}
assert resources["M"]["quantity"] == 9
assert resources["M"]["amount"] == 900
assert resources["L"]["quantity"] == 8
assert resources["L"]["amount"] == 800
def test_resource_expansion_selects_nested_head_component_and_all_conversions():
result = run(
"math",
r"""(()=>{
const p=EstNative.empty();
p.tables.StuffcDat=[{Code:'M',Name:'material',ValueIndex:4,InputTC:100}];
p.tables.LaborcDat=[{Code:'L',Name:'labor',ValueIndex:4,InputTC:100}];
p.tables.HeadMainDat=[{Index:1,Code:'H',Name:'head'},
{Code:'M',LinkIndex:5,GN:1},{Code:'L',LinkIndex:6,GN:1}];
p.tables.HeadTitleDat=[{Code:'H',Name:'head'}];
p.tables.CostMainDat=[{Index:1,Code:'C'},
{Code:'H',LinkIndex:103,LineData:' \x0ehead\x0e'},
{Code:'H',LinkIndex:103,SCheck:-1,LineData:' 노무비: \x03100\x03 = \x12100\x12'},
{Code:'H',LinkIndex:103,SCheck:-2,LineData:' 재료비: \x03100\x03 = \x12100\x12'}];
p.tables.CostTitleDat=[{Code:'C'}];
p.tables.EstMainDat=[{PkID:1,Code:'C',LinkIndex:104,GN:10},{LinkIndex:54}];
const selected=EstResources.calculate(p);
p.tables.HeadMainDat.push({LinkIndex:68,GN:0.5},{LinkIndex:68,GN:0.1});
p.tables.EstMainDat[0].Code='H';p.tables.EstMainDat[0].LinkIndex=103;
return {selected,converted:EstResources.calculate(p)};
})()""",
)
assert all(output["errors"] == [] for output in result.values())
assert len(result["selected"]["rows"]) == 2
assert all(
row["quantity"] == 10 and row["amount"] == 1000 for row in result["selected"]["rows"]
)
assert all(
row["quantity"] == 0.5 and row["amount"] == 50 for row in result["converted"]["rows"]
)
def test_resource_expansion_includes_money_only_rows_and_percentage_costs():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();p.options.SourceRefresh=false;
p.tables.EstMainDat=[{PkID:1,Name:'fixed',GN:0,NC:3,GC:7,JC:2,TC:12,CalcIndex:1},{LinkIndex:54}];
const fixed=EstResources.calculate(p);
const q=EstNative.empty();q.tables.StuffcDat=[{Code:'M',Name:'material',ValueIndex:4,InputTC:100}];
q.tables.HeadMainDat=[{Index:1,Code:'H'},{Code:'M',LinkIndex:5,GN:1},
{LinkIndex:58,Size:'합계',SCheck:3,GN:10}];q.tables.HeadTitleDat=[{Code:'H'}];
q.tables.EstMainDat=[{Code:'H',LinkIndex:103,GN:2},{LinkIndex:54}];
return {fixed,percentage:EstResources.calculate(q)};
})()""",
)
assert all(output["errors"] == [] for output in result.values())
fixed = {row["Code"]: row for row in result["fixed"]["rows"]}
assert fixed["LZZZZZZZZZZ"]["amount"] == fixed["LZZZZZZZZZZ"]["quantity"] == 3
assert fixed["MZZZZZZZZZZ"]["amount"] == fixed["MZZZZZZZZZZ"]["quantity"] == 7
assert fixed["EZZZZZZZZZZ"]["amount"] == 2
assert fixed["EZZZZZZZZZZ"]["Unite"] == "제외금액"
assert sum(row["amount"] for row in result["percentage"]["rows"]) == 220
def test_cost_code_refresh_without_name_marker_still_tracks_the_actual_resource():
result = run(
"math",
r"""(()=>{
const p=EstNative.empty();
p.tables.StuffcDat=[{Code:'M',Name:'material',ValueIndex:4,InputTC:150}];
p.tables.CostMainDat=[{Index:1,Code:'C'},
{Code:'M',LinkIndex:5,SCheck:-2,LineData:' 재료비: \x03100\x03 ×2 = \x12200\x12'}];
p.tables.CostTitleDat=[{Code:'C'}];
p.tables.EstMainDat=[{Code:'C',LinkIndex:104,GN:1},{LinkIndex:54}];
return {calculated:EstNative.calculate(p),resources:EstResources.calculate(p)};
})()""",
)
assert result["calculated"]["calculation"]["errors"] == []
assert result["calculated"]["calculation"]["direct"]["TC"] == 300
assert result["resources"]["errors"] == []
assert result["resources"]["rows"][0]["Code"] == "M"
assert result["resources"]["rows"][0]["quantity"] == 2
assert result["resources"]["rows"][0]["amount"] == 300
def test_unlinked_text_price_does_not_inherit_the_previous_rows_source():
result = run(
"math",
r"""(()=>{
const p=EstNative.empty();
p.tables.StuffcDat=[{Code:'M',Name:'material',ValueIndex:4,InputTC:150}];
p.tables.CostMainDat=[{Index:1,Code:'C'},
{Code:'M',LinkIndex:5,SCheck:-2,LineData:' \x0ematerial\x0e: \x03100\x03 = \x12100\x12'},
{Code:'M',LinkIndex:0,SCheck:-2,LineData:' 직접입력: \x0350\x03 ×2 = \x12100\x12'}];
p.tables.CostTitleDat=[{Code:'C'}];
p.tables.EstMainDat=[{Code:'C',LinkIndex:104,GN:1},{LinkIndex:54}];
return {calculated:EstNative.calculate(p),resources:EstResources.calculate(p)};
})()""",
)
assert result["calculated"]["calculation"]["errors"] == []
assert result["calculated"]["calculation"]["direct"]["TC"] == 250
resources = {row["Code"]: row for row in result["resources"]["rows"]}
assert resources["M"]["quantity"] == 1
assert resources["M"]["amount"] == 150
assert resources["MZZZZZZZZZZ"]["amount"] == 100
def test_change_comparison_uses_original_file_values_without_overwriting_a_baseline():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();p.tables.EstMainDat=[
{PkID:1,Code:'R',GN:3,TC:150,GN_:2,TC_:100},
{PkID:2,Code:'Z',GN:1,TC:10,GN_:0,TC_:0},
{PkID:3,Code:'N',GN:1,TC:20}];
const stored=EstBusiness.changes(p);
p.baseline={name:'explicit',tables:{EstMainDat:[{PkID:1,Code:'R',GN:4,TC:200}]}};
return {stored,explicit:EstBusiness.changes(p),unchanged:p.tables.EstMainDat[0].TC_};
})()""",
)
assert result["stored"][0]["originalGN"] == 2
assert result["stored"][0]["originalTC"] == 100
assert result["stored"][0]["difference"] == 50
assert result["stored"][0]["originalSource"] == "file"
assert not result["stored"][1]["NewItem"]
assert result["stored"][2]["NewItem"]
assert result["explicit"][0]["originalTC"] == 200
assert result["explicit"][0]["difference"] == -50
assert result["explicit"][1]["NewItem"]
assert result["unchanged"] == 100
def test_original_numeric_fields_reject_invalid_saved_values():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();p.tables.EstMainDat=[{GN:1,GN_:'invalid'}];
try {EstModel.validate(p);return false;}catch(error){return error.message;}
})()""",
)
assert "GN_" in result
def test_baseline_keeps_current_inputs_once_and_preserves_working_binaries():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();
p.tables.CostMainDat=[{Index:1,Code:'C',LineData:'current',LineData_:'previous'}];
p.tables.CadMainDat=[{Code:'D',Data:{binary:'QUJD'}}];
p.tables.StuffcDat=[{Code:'M',InputTC:100}];p.originalArchive='original bytes';
const q=EstBusiness.baseline(p),again=EstBusiness.baseline(q);
return {p,q,again};
})()""",
)
for item in [result["q"], result["again"]]:
assert item["originalArchive"] == "original bytes"
assert item["tables"]["CadMainDat"][0]["Data"]["binary"] == "QUJD"
assert "CadMainDat" not in item["baseline"]["tables"]
assert "StuffcDat" not in item["baseline"]["tables"]
assert item["baseline"]["tables"]["CostMainDat"][0]["LineData"] == "current"
assert "LineData_" not in item["baseline"]["tables"]["CostMainDat"][0]
assert result["p"]["tables"]["CostMainDat"][0]["LineData_"] == "previous"
def test_large_original_baseline_stays_within_the_save_request_limit():
import os
from pathlib import Path
import pytest
archive = Path(
os.environ.get("EST_REA_MAIN", "C:/Dasansoft/Est Plus 2/Data/ProjectData/main.estx")
)
if not archive.is_file():
pytest.skip("Original main.estx is not installed")
result = run("baseline-size", str(archive))
assert result["archivePreserved"]
assert result["bytes"] < 256 * 1024 * 1024