import {straightCopingFormulas} from './coping-formulas.ts'; import {FormulaBook,cellKey,type Overrides} from './formula.ts'; import {template,templateHash} from './template-data.ts'; import {DETAIL,INPUTS,extraNumbers,specifications} from './schema.ts'; export type ExportRequest={project:{name:string;bridge:string};pier:string;overrides:Overrides;specs:Record;reviewed:boolean;copingMode?:'straight';templateHash:string;planPng?:string;solplateMode?:'model'}; export function exportEdits(request:ExportRequest){ if(request.templateHash!==templateHash||!request.reviewed)throw new Error('원본 산식 적용 확인 후 다시 계산하세요.'); const edits:Record>={},evaluation:Overrides={...request.overrides}; if(request.solplateMode!=='model'){delete evaluation[cellKey(DETAIL,'W64')];delete evaluation[cellKey(DETAIL,'K64')];} const value=(sheet:string,cell:string,v:string|number|null)=>{(edits[sheet]??={})[cell]={value:v};evaluation[cellKey(sheet,cell)]=v??0;}; for(let r=18;r<=46;r++){ const raw=request.overrides[cellKey(INPUTS,'N'+r)];if(raw!==null&&(typeof raw!=='number'||!Number.isFinite(raw)))throw new Error('입력값 누락 N'+r); value(INPUTS,'N'+r,request.copingMode==='straight'&&r===30?null:raw as number|null); } for(const v of extraNumbers){const n=request.overrides[cellKey(DETAIL,v.cell)];if(typeof n!=='number'||!Number.isFinite(n)||n<0)throw new Error(v.name+' 입력을 확인하세요.');value(DETAIL,v.cell,n);} for(const v of specifications){const s=request.specs[v.key];if(typeof s!=='string'||!s.trim())throw new Error(v.name+'을 확인하세요.');value(DETAIL,v.cell,s);} if(request.solplateMode==='model'){ const ton=request.overrides[cellKey(DETAIL,'W64')]; if(typeof ton!=='number'||!Number.isFinite(ton)||ton<0)throw new Error('모델 솔플레이트 중량을 확인하세요.'); value(DETAIL,'W64',ton); value(DETAIL,'K64','3D 모델 판별 체적 합계 × 7.85 (앱 재생성 값)'); } for(let i=2;i<=6;i++){ const sheet='3.3 수량산출_P'+i; for(const [cell,c]of Object.entries(template[sheet]))if(typeof c.value==='number'||typeof c.value==='string'&&c.value.startsWith('='))value(sheet,cell,null); value(sheet,'F2','(미사용)');value(sheet,'BG5','미사용'); const col=['P','R','T','V','X'][i-2];for(let r=18;r<=46;r++)value(INPUTS,col+r,null);value(INPUTS,col+'17','미사용'); value('1. 수량집계',['Q','T','W','Z','AC'][i-2]+'4','미사용'); } // The example sheet must stay a source example, not follow the active pier's straight R. for(let r=6;r<=34;r++){const c=template['3.3 수량산출_ex)']['BG'+r];if(c&&typeof c.cached==='number')value('3.3 수량산출_ex)','BG'+r,c.cached);} value(INPUTS,'N17',request.pier);value(DETAIL,'F2','('+request.pier+')');value(DETAIL,'BG5',request.pier);value('1. 수량집계','N4',request.pier); value('표지및목차','A6',request.project.name);value('표지및목차','A14',request.project.bridge+' · '+request.pier);value('표지및목차','A24',new Date().toISOString().slice(0,10)); const formulas=request.copingMode==='straight'?straightCopingFormulas:{}; for(const [key,formula]of Object.entries(formulas)){const pos=key.lastIndexOf('!');(edits[key.slice(0,pos)]??={})[key.slice(pos+1)]={formula};} const book=new FormulaBook(evaluation,template,formulas); for(const [sheet,cells]of Object.entries(template))for(const [cell,c]of Object.entries(cells)){ if(typeof c.value!=='string'||(!c.value.startsWith('=')||c.value.length===1)||edits[sheet]?.[cell]&&Object.hasOwn(edits[sheet][cell],'value'))continue; const n=book.cell(sheet,cell);if(typeof n!=='number'||!Number.isFinite(n))throw new Error('출력 수식 검증 실패 '+sheet+'!'+cell); (edits[sheet]??={})[cell]={...edits[sheet]?.[cell],cached:n}; } if(request.planPng&&(!/^[A-Za-z0-9+/=]+$/.test(request.planPng)||request.planPng.length>1500000))throw new Error('평면도 이미지 형식을 확인하세요.'); return {templateHash,edits,planPng:request.planPng}; }