Files
Aislo/resources/tester/test_est_documents.py
T

139 lines
6.0 KiB
Python

from test_est_rea_parity import run
def test_progress_and_escalation_print_use_selected_calculations():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();p.options.SourceRefresh=false;p.options.EscDesignTotal=1000;
p.tables.EstMainDat=[{PkID:1,Code:'R',Name:'work',GN:10,GU:100,AGU:100,EscActual:3},{LinkIndex:54}];
p.progress=[{id:'one',name:'first',inputs:{'1':{Gum:2}}}];
const q=EstProgress.lock(EstNative.calculate(p),'one');
q.tables.EstMainDat[0].GU=200;
q.progress.push({id:'two',name:'second',inputs:{'1':{Gum:3}}});
return {locked:EstForms.data('progress-summary',q,{roundId:'one'}),
draft:EstForms.data('progress-summary',q,{roundId:'two'}),
escalation:EstForms.data('escalation-summary',p),
empty:EstForms.data('progress-summary',EstNative.empty())};
})()""",
)
assert result["locked"]["rows"][0]["GumTC"] == 200
assert result["locked"]["rows"][0]["GU"] == 100
assert result["draft"]["rows"][0]["GumTC"] == 600
assert result["draft"]["rows"][0]["TotalTC"] == 800
assert result["escalation"]["rows"][0]["EscExcluded"] == 3
assert result["escalation"]["rows"][0]["EscApplied"] == 7
assert result["empty"]["errors"]
def test_resource_report_separates_each_work_item_without_changing_aggregate():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();p.options.SourceRefresh=false;
p.tables.StuffcDat=[{Code:'M',Name:'material',InputTC:100,ValueIndex:4}];
p.tables.EstMainDat=[{PkID:1,Code:'M',LinkIndex:5,GN:2,GU:100},{PkID:2,Code:'M',LinkIndex:5,GN:3,GU:100},{LinkIndex:54}];
return {all:EstResources.calculate(p),split:EstResources.calculate(p,true)};
})()""",
)
assert len(result["all"]["rows"]) == 1
assert len(result["split"]["rows"]) == 2
assert (
sum(r["quantity"] for r in result["split"]["rows"])
== result["all"]["rows"][0]["quantity"]
== 5
)
assert (
sum(r["amount"] for r in result["split"]["rows"])
== result["all"]["rows"][0]["amount"]
== 500
)
def test_schedule_dates_ranges_and_weighted_user_progress():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();p.schedule=[
{id:'a',Code:'A',Name:'first',start:'2026-11-01',end:'2026-11-05',weight:100,progress:25},
{id:'b',Code:'B',Name:'second',start:'2026-11-06',end:'2026-11-10',weight:300,progress:50}];
const valid=EstSchedule.calculate(p),bad=[];
for(const change of [{start:'2026-02-30'},{progress:101},{weight:-1},{end:'2026-10-01'}]) {
const q=structuredClone(p);Object.assign(q.schedule[0],change);
try{EstModel.validate(q);bad.push(false)}catch{bad.push(true)}
}
return {valid,bad};
})()""",
)
assert result["valid"]["progress"] == 43.75
assert result["valid"]["rows"][0]["width"] == 50
assert result["valid"]["rows"][1]["left"] == 50
assert result["bad"] == [True] * 4
def test_schedule_import_and_update_weights_preserve_user_schedule():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();
p.tables.EstMainDat=[{Code:'A',Name:'one',TC:100}, {Code:'B',Name:'two',TC:300},
{Code:'HEADER',Level:1,TC:999},{Code:'TOTAL',LinkIndex:54,TC:400}];
p.schedule=[{id:'old',Code:'A',Name:'custom',start:'2026-01-01',end:'2026-01-10',weight:1,progress:40},
{id:'manual',Code:'other',Name:'manual',start:'',end:'',weight:5,progress:20}];
let id=0;const added=EstSchedule.fromEstimate(p,()=>String(++id));p.schedule.push(...added);
p.tables.EstMainDat[0].TC=200;p.schedule=EstSchedule.refreshWeights(p);
let duplicate='';p.tables.EstMainDat.push({Code:'A',TC:20});
try{EstSchedule.fromEstimate(p,()=>String(++id))}catch(e){duplicate=e.message}
return {added,rows:p.schedule,duplicate};
})()""",
)
assert [row["Code"] for row in result["added"]] == ["B"]
assert [row["weight"] for row in result["rows"]] == [200, 5, 300]
assert result["rows"][0]["Name"] == "custom"
assert result["rows"][0]["start"] == "2026-01-01"
assert result["rows"][0]["progress"] == 40
assert result["duplicate"]
def test_schedule_imports_distinct_rows_with_repeated_or_empty_codes():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();p.tables.EstMainDat=[
{PkID:1,Code:'X',Name:'one',TC:100},{PkID:2,Code:'X',Name:'two',TC:200},
{PkID:3,Code:null,Name:'three',TC:300}];
let id=0;p.schedule=EstSchedule.fromEstimate(p,()=>String(++id));
p.schedule[0].start='2026-01-01';p.schedule[0].progress=50;
p.tables.EstMainDat.reverse();p.tables.EstMainDat[0].TC=400;
const updated=EstSchedule.refreshWeights(p);
return {rows:p.schedule,updated,again:EstSchedule.fromEstimate(p,()=>String(++id))};
})()""",
)
assert [row["SourceKey"] for row in result["rows"]] == ["id:1", "id:2", "id:3"]
assert [row["weight"] for row in result["updated"]] == [100, 200, 400]
assert result["updated"][0]["start"] == "2026-01-01"
assert result["updated"][0]["progress"] == 50
assert result["again"] == []
def test_quantity_print_includes_manual_and_sheet_formulas_and_errors():
result = run(
"math",
"""(()=>{
const p=EstNative.empty();p.quantities=[
{Code:'A',Name:'first',formula:'2',TargetCode:'X'},
{Code:'B',Name:'second',formula:'[A]*3',TargetCode:'X'},
{Code:'C',Name:'bad',formula:'[missing]',TargetCode:'X'}];
p.quantitySheets=[{Code:'Q',Name:'sheet',options:{},rows:[
{LineData:'4+5 = \\x120\\x12',markers:['r1']}],
items:[{Code:'r1',Name:'sheet item',GN:0,TargetCode:'Y'}]}];
return EstForms.data(14,p);
})()""",
)
assert [row["GN"] for row in result["rows"]] == [2, 6, None, 9]
assert result["rows"][1]["formula"] == "[A]*3"
assert "4+5" in result["rows"][3]["formula"]
assert result["rows"][3]["TargetCode"] == "Y"
assert result["rows"][2]["Error"]
assert result["errors"]