// 원본 견적서작성(mnuEstiMatePrn → TfrmEstiMatePrn 0x10c1994, 그리기 0x10c4768).
// 설정은 공사마다 project.estimatePrint(원본 EstiPrintInfo.einf 세 구역), 공급자정보는 원본과 같은 INI(.einf)로 내보내기·불러오기
// (불러오기는 EstiPrnCompanyInfo·EstiPrnInfo 두 구역만). 행: 레벨 0 = 전 행, N = Level ≥ N 공종 머리 + LinkIndex 54·55·58.
// 열: 품명(Pr+Name)·규격·수량·단위·단가·공급가액·비고(선택). 1쪽만 머리·공급자·합계금액, 1쪽 F_PageCount·이후 L_PageCount 줄.
// 합계금액 = 원가계산서를 아래에서 위로 훑어 Checked 이고 금액 있는 첫 행(없으면 도급액 55 행) — 「일금: …원정 (₩…원정)」.
const EstEstimatePrint = (() => {
const num = (value) => Number(value) || 0;
const defaults = {
Title_Name: "견 적 서",
Project_Name: "",
Order_name: "",
Esti_Date: "",
Reg_Num: "",
ComPany_Name: "",
Ceo_Name: "",
Addr: "",
Uptae: "",
Jongmog: "",
Tel: "",
Company_Show: true,
F_PageCount: 27,
L_PageCount: 36,
Aligment: 0,
Bigo_Show: false,
Level: 0,
};
const sections = {
EstiProjectInfo: ["Title_Name", "Project_Name", "Order_name", "Esti_Date"],
EstiPrnCompanyInfo: [
"Reg_Num",
"ComPany_Name",
"Ceo_Name",
"Addr",
"Uptae",
"Jongmog",
"Tel",
"Company_Show",
],
EstiPrnInfo: ["F_PageCount", "L_PageCount", "Aligment", "Bigo_Show"],
};
function settings(project) {
const today = new Date();
return {
...defaults,
Project_Name: project.name || "",
Esti_Date: `${today.getFullYear()}년 ${String(today.getMonth() + 1).padStart(2, "0")}월 ${String(today.getDate()).padStart(2, "0")}일`,
...(project.estimatePrint || {}),
};
}
function rows(project, level) {
const list = project.tables.EstMainDat || [];
return level > 0
? list.filter((r) => num(r.Level) >= level || [54, 55, 58].includes(num(r.LinkIndex)))
: list;
}
function total(project) {
const wonga = project.tables.Wonga || [];
for (let i = wonga.length - 1; i >= 0; i--)
if (wonga[i].Checked === true && num(wonga[i].Cost)) return num(wonga[i].Cost);
return num(wonga.find((r) => num(r.LinkIndex) === 55)?.Cost ?? project.calculation?.total);
}
function totalText(value) {
return ` 일금:${EstDocs.korean(value)}원정 (₩${Math.round(value).toLocaleString("en-US")}원정)`;
}
function pages(list, first, rest) {
if (!(first > 0 && rest > 0)) throw Error("쪽당 줄 수는 1 이상입니다.");
const out = [list.slice(0, first)];
for (let at = first; at < list.length; at += rest) out.push(list.slice(at, at + rest));
return out;
}
// 원본 TIniFile 형식(.einf)
function toIni(s, keys = Object.keys(sections)) {
return keys
.map(
(section) =>
`[${section}]\r\n${sections[section].map((key) => `${key}=${typeof s[key] === "boolean" ? (s[key] ? "1" : "0") : (s[key] ?? "")}`).join("\r\n")}`,
)
.join("\r\n\r\n");
}
function fromIni(text, keys = ["EstiPrnCompanyInfo", "EstiPrnInfo"]) {
const out = {};
let section = "";
for (const line of String(text).split(/\r?\n/)) {
const head = line.match(/^\s*\[([^\]]+)\]\s*$/);
if (head) {
section = head[1];
continue;
}
const pair = line.match(/^\s*([^=]+?)\s*=(.*)$/);
if (!pair || !keys.includes(section) || !sections[section].includes(pair[1])) continue;
const key = pair[1],
value = pair[2].trim(),
base = defaults[key];
out[key] =
typeof base === "boolean"
? ["1", "true", "-1"].includes(value.toLowerCase())
: typeof base === "number"
? Number(value) || 0
: value;
}
return out;
}
function html(project, s) {
const esc = (v) =>
String(v ?? "").replace(
/[&<>"]/g,
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c],
);
const fmt = (v) => (num(v) ? num(v).toLocaleString("ko-KR", { maximumFractionDigits: 4 }) : "");
const lines = (text) => esc(text).split("|").join("
");
const align = num(s.Aligment) ? "center" : "left";
const head = [
"품 명",
"규 격",
"수 량",
"단위",
"단 가",
"공급가액",
...(s.Bigo_Show ? ["비 고"] : []),
];
const table = (list) =>
`
| ${h} | `).join("")}||||||
|---|---|---|---|---|---|---|
| ${esc(`${r.Pr ?? ""}${r.Name ?? ""}`)} | ${esc(r.Size)} | ${fmt(r.GN)} | ${esc(r.Unite)} | ${fmt(r.TU)} | ${fmt(r.TC)} | ${s.Bigo_Show ? `${esc(typeof r.Bigo === "string" ? r.Bigo : "")} | ` : ""}
| 공 급 자 | 등록번호 | ${esc(s.Reg_Num)} |
|---|---|---|
| ${k} | ${esc(v)} |
공사명: ${lines(s.Project_Name)}
${lines(s.Order_name)} 귀하
${esc(s.Esti_Date)}
아래와 같이 견적합니다
합계금액 ${esc(totalText(sum))}
` : "" }${table(list)}