Files
Aislo/tools/stmate-test/kanjub_calc.js
T
eomsangdonandClaude Opus 5.5 5847580670 feat(stmate-test): REA 보강 — 실행·계약·변경 차수·잔여 계산 원본 규칙 · 장부 91.1%
- 지식DB REA 분석 32 실행계약 · 34 변경차수 잔여계산 작업자료(정적분석 · 원본 실행·수정 없음)
- 실행: 중기 실행단가 합계·성분 비례·끝전·차액 성분 · 실행단가·수량 할증 · 계약 변환 옵션·적용제외 · 기성 공사 낙찰율 100%
- 변경: 차수 목록·이어 셈 · 보험료 3모드(변경 2차 이후) · 방법 A/B 5항목 · 퇴직공제·건설기계·표준시장단가·노임 환산 갈래·JCD
- 시험 693 통과 · 브라우저 3건 화면 전수 오류 0 · 총공사비 그대로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 09:29:53 +09:00

517 lines
21 KiB
JavaScript
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.
/* -*- coding: utf-8 -*- */
/**
* 원가계산서(제잡비) 계산 (kanjub_calc.js) — STMATE Calc_Kanjub 네이티브 순서
* 직접비 → 간접노무비 → 보험 → 안전 → 경비 → (추가1) → 일반관리비 → 이윤 → 이윤 뒤 가산(추가2)
* → 공사보험 → 작업설 감액 → 부가세 → 세후 가산(추가3) → 도급 절사 → 관급 → 사급 → (추가4) → 총공사비
* 근거: 지식DB STmate/old/20_분석 06 · 11 · 12 · 20(KAN_ORD) · 25 · 35
* REA 18(보험·안전·수공 경비·변경 차액) · REA 20(일반관리비·이윤 절사·공사보험·부가세·관급·추가금액 순서)
* 설정: cfg.keys = PRJLST.SMT [n_KANJUB] 원본 키(차수 접두 뗌) · cfg.rates/fixed/scheme 등 옛 칸이 있으면 그 값이 앞섬
* 정답 대조: resources/tester/test_stmate_kanjub_stc.py · test_stmate_kanjub_opts.py
*/
const StmateKanjubFactory = (h) => {
const { num, roundDown, roundUp, roundHalfUp, roundBanker } = h;
// 설정 읽개·항목 표·결과 묶음 — kanjub_parts.js
const P = (globalThis.STMATE_KANJUB_PARTS || require("./kanjub_parts.js"))(h);
const { keyOf, LABEL, S8, reader, addList, finish } = P;
const NATIVE = P.NATIVE; // 원본 차수 규칙표(REA 34)
function calcKanjub(data, boqRes, origKanjubRes = null, stageCfg = null) {
const cfg = stageCfg || (data && data.kanjubConfig) || {};
const o = reader(cfg, data),
slot = num(cfg.slot, 1),
adds = addList(cfg, slot);
const addSum = (g, vatOnly = false) =>
adds
.filter((a) => a.stage === g && !(g === 1 && a.addt) && (!vatOnly || a.vat))
.reduce((s, a) => s + num(a.amount), 0);
const B = {
M: num(boqRes.directM),
L: num(boqRes.directL),
S: num(boqRes.directS),
govAll: num(boqRes.govCombinedRaw),
govFee: num(boqRes.govFeeTotal),
};
const run1 = (m, up) => runOnce(o, cfg, B, origKanjubRes, adds, addSum, m, up);
let r = run1(o.k("C_IYUN_M"), o.k("B_BUGA_UP") ? 1 : 0);
// 총액 절사(I_TOT_JEOL) — 총공사비를 자리 절사한 목표에 맞도록 이윤보정액(C_IYUN_M)을 1원씩 조정 · 못 맞추면 부가세 +1(B_BUGA_UP)
const tj = o.k("I_TOT_JEOL");
if (tj > 0 && !o.k("I_JEOLSA")) {
let up = o.k("B_BUGA_UP") ? 1 : 0;
const first = run1(0, up),
target = roundDown(first.grand, -tj),
diff = first.grand - target;
const m0 = o.k("I_BUGA") === 0 ? roundHalfUp(diff / 1.1, 0) : diff;
let m = m0,
seen = new Set();
r = run1(m, up);
for (let i = 0; i < 60 && r.grand !== target; i++) {
seen.add(m);
m += r.grand > target ? 1 : -1;
if (seen.has(m)) {
if (up) break;
((up = 1), (m = m0), (seen = new Set()));
}
r = run1(m, up);
}
Object.assign(r, { target, diffTotal: diff, bugaUp: up });
}
return finish(o, cfg, r, origKanjubRes);
}
// 한 번 계산(이윤보정액 m · 부가세 +1 up 고정)
function runOnce(o, cfg, B, prev, adds, addSum, iyunM, bugaUp) {
const J = o.k("I_KAN_JEOLSA"),
tr = (v) => roundDown(v, -J),
ctx = o.ctx,
actl = o.actl;
const std = ctx === 0 || ctx === 1 || ctx === 2 || ctx === 5,
su = ctx === 3 || ctx === 4;
const rows = {},
keysOut = {};
// 항목 한 줄 — 후보(cand) · D_ 고정 · 옛 변경 계산방식(revModes)
// revModes 방법A/B 는 원본에 없는 옛 갈래 — 원본 당초액 잇기는 건강·연금·장기요양·퇴직공제·안전 5항목뿐(REA 34 · Main 0x46f1ac·0x46f270)
const put = (code, base, rate, cand, desc, tpl, group) => {
const fx = o.fix(code);
let amount = fx != null ? fx : cand;
const mode = o.revModes[code];
const pr = prev && prev.itemMap && prev.itemMap[code];
if (fx == null && pr && mode && mode !== "recalculated") {
const ob = num(pr.baseAmt);
amount =
mode === "carry_original"
? pr.amount
: mode === "proportional"
? pr.amount + tr(pr.amount * (ob > 0 ? (base - ob) / ob : 0))
: mode === "delta_rate"
? pr.amount + tr(((base - ob) * rate) / 100)
: amount;
}
const row = { key: code, code, label: LABEL[code] || code, group: group || "exp" };
Object.assign(row, {
formula: desc,
baseDesc: desc,
baseAmt: base,
base,
rate,
ratePct: rate,
});
Object.assign(row, { cand, amount, fixed: fx != null, tpl: tpl || "" });
if (pr) Object.assign(row, { origAmt: pr.amount, deltaAmt: amount - pr.amount });
row.modeUsed = prev ? mode || "recalculated" : "원설계";
rows[code] = row;
keysOut["C_" + keyOf(code)] = amount;
keysOut["S_" + keyOf(code)] = cand;
return amount;
};
// 율 항목 — 후보 = ROUNDDOWN(밑수×율/100, −간접비 절사자리) · 식 템플릿과 그 변수(25번 §2.1)를 함께 남김
const pctItem = (code, base, desc, tplBase, vars) => {
const r = o.R(code),
amt = put(
code,
base,
r,
tr((base * r) / 100),
desc,
`ROUNDDOWN(${tplBase}*[RAT_1]/100,-[J])`,
);
rows[code].tplVars = Object.assign({ RAT_1: r, J }, vars);
return amt;
};
// 0) 직접비 틀 — 노무비 가산(DOSEOH) · 사급 위치(I_POS_SA=3 재료비 포함) · 실적 대체(CAL_ACTL)
let M = B.M,
L = B.L,
S = B.S;
if (o.R("DOSEOH")) L += pctItem("DOSEOH", L, "직접노무비", "[LAMT]", { LAMT: L });
const posSa = o.k("I_POS_SA"),
sagup = o.k("C_SAGUP");
let Kres = sagup;
if (posSa === 3) ((M += sagup), (Kres = 0));
let T = M + L + S,
c98 = null;
if (actl) {
const DM = o.R("R_DM"),
DL = o.R("R_DL");
if (o.k("I_MTYP") === 1) S = T - L - M;
else if (o.k("I_LTYP")) {
M = roundDown(T * DM, 0);
if (M + L > T) M = T - L;
S = T - M - L;
} else {
M = roundDown(T * DM, 0);
L = roundDown(T * DL, 0);
S = (1 - DM - DL) * T;
L = T * o.R("R_L");
c98 = T * o.R("R_DML");
}
}
const c90 = L + M,
hol = o.k("C_EIS_HOLLAB"),
U = std ? o.k("C_NBUGAM") : 0;
let expAcc = actl ? 0 : S,
Lp = L;
// 1) 간접노무비 — 일반은 노무비, 실적은 간접공사비(I_LTYP≠0 이면 노무비 밑수에도)
const kanno = pctItem("KANNO", L, "직접노무비", "[LAMT]", { LAMT: L });
if (!actl) Lp += kanno;
else ((expAcc += kanno), o.k("I_LTYP") && (Lp += kanno));
if (su) expAcc += put("S_PUMJIL", 0, 0, o.k("C_S_PUMJIL"), "별도내역");
// 2) 산재·고용 = (노무비 + 고용개선 주휴노무비) × 율
expAcc += pctItem("SANBO", Lp + hol, "직접노무비+간접노무비", "([L_AMT][+LAB])", {
L_AMT: Lp,
LAB: hol,
});
expAcc += pctItem("KOBO", Lp + hol, "직접노무비+간접노무비", "([L_AMT][+LAB])", {
L_AMT: Lp,
LAB: hol,
});
// 3) 건강·연금·장기요양·퇴직공제 — 변경 차수 당초액 + 증감(I_BF_CH_BO) · 직접입력 유지(I_COST_BO) · REA 18
// 문 = PCLASS 7 · 계열 > 1(Main 0x4645a3 · 0x46a226 — 그 밖은 I_BF_CH_BO 0) · 직전 계열 = 계열 − 1(0x473ade)
// 1 = 당초액 + ROUNDDOWN(직노 증감 × 율) · 2 = 당초액 + ROUNDDOWN(직노 증감 × 당초액 ÷ 당초 직노 · 당초 직노 ≥ 1)
const lab = L + hol,
bfch = o.k("I_BF_CH_BO"),
costBo = o.k("I_COST_BO");
const series = prev ? Math.max(2, num(cfg.slot, 2)) : 1;
const gate = !!prev && (o.pclass < 0 || NATIVE.chgGate(o.pclass, series));
const chg = gate && (bfch === 1 || bfch === 2);
const prevBase = prev ? num(prev.dL) + num(prev.hollab) : 0;
const prevAmt = (c) => (prev && prev.itemMap[c] ? num(prev.itemMap[c].amount) : 0);
const hp = (code, sel) => {
const base = o.k(sel) ? o.k("C_SANGSI") : lab,
r = o.R(code);
if (costBo === 1 || costBo === 2) return put(code, base, r, o.k("C_" + code), "직접입력");
if (chg) {
const old = prevAmt(code),
d =
bfch === 2 && prevBase >= 1
? ((lab - prevBase) * old) / prevBase
: ((lab - prevBase) * r) / 100;
return put(code, base, r, old + tr(d), "당초액 + 직노증감", "[C_AMT_B]+ROUNDDOWN(...)");
}
return pctItem(code, base, o.k(sel) ? "상시근로자노무비" : "직접노무비", "[LAMT]", {
LAMT: base,
});
};
const kunbo = hp("KUNBO", "I_KUNBO");
expAcc += kunbo;
const oldKun = prevAmt("KUNBO");
expAcc +=
chg && bfch === 2 && oldKun >= 1
? put(
"SLVBO",
kunbo,
o.R("SLVBO"),
prevAmt("SLVBO") + tr(((kunbo - oldKun) * prevAmt("SLVBO")) / oldKun),
"당초액 + 증감(비율)",
)
: pctItem("SLVBO", kunbo, "건강보험료", "[C_KUNBO]", { C_KUNBO: kunbo });
expAcc += hp("YUNBO", "I_YUNBO");
expAcc +=
costBo === 2
? put("TBUGUM", lab, o.R("TBUGUM"), o.k("C_TBUGUM"), "직접입력")
: gate && bfch === 2 && prevBase >= 1
? put(
"TBUGUM",
lab,
o.R("TBUGUM"),
prevAmt("TBUGUM") + tr(((lab - prevBase) * prevAmt("TBUGUM")) / prevBase),
"당초액 + 증감(비율)",
)
: pctItem("TBUGUM", lab, "직접노무비", "[LAMT]", { LAMT: lab });
// 4) 산업안전보건관리비 — 관급 대상액(C_KN_SA 8갈래) · 후보 3개 · 1.2배 비교 · 변경 비율(I_ANJUNAUTO=2) · REA 18
const gD = o.k("B_KWANGUP_D") ? B.govAll : o.k("C_KWANGUP_D"),
gK = o.k("C_KWANGUP_K");
let jodal = o.k("B_T_JODAL") ? B.govFee : o.k("C_T_JODAL");
if (jodal > gD) jodal = gD;
const gAll = gD + gK,
gA = gAll - gK - jodal,
knT = o.k("I_KN_SA_TYPE");
const kDiv = posSa === 1 ? Kres : Kres / 1.1;
const knSa =
[gA / 1.1, gA, kDiv, Kres, gA / 1.1 + Kres, gA + kDiv, gA / 1.1 + kDiv, gA + Kres][knT] ?? 0;
const busan = actl ? 0 : o.k("C_BUSAN"),
a0 = roundDown(T * o.R("R_MxA"), 0);
const b0 =
o.k("I_MTYP") === 1
? actl
? T
: c90 + busan
: actl && o.k("I_LTYP") === 1 && ((c90 > a0 && o.k("I_MXA") === 0) || o.k("I_MXA") === 1)
? a0
: c90 + busan;
const Dn = std ? o.k("C_T_ANJUN_N") : 0,
selK = [2, 5, 6].includes(knT) ? kDiv : Kres;
let c190 = ((b0 + U - Dn) * o.R("ANJUN1")) / 100 + o.G("C_ANJUN1_G"),
c198 = ((b0 + U + knSa - Dn) * o.R("ANJUN2")) / 100 + o.G("C_ANJUN2_G"),
c1a0 = ((b0 + U + selK - Dn) * o.R("ANJUN")) / 100 + o.k("C_ANJUN_G");
if (ctx !== 4 && o.k("I_AN_DN_TYPE") !== 1)
[c190, c198, c1a0] = [c190, c198, c1a0].map((v) => roundDown(v, 0));
const ak = o.k("I_ANJUN_K"),
auto = o.k("I_ANJUNAUTO");
const l18 = ak === 1 ? c198 : ak === 2 ? c1a0 : c190;
let l10 = c190,
cap = o.k("I_ANJUN_B") === 1,
capAuto = false;
if (auto === 0 && (ak === 1 || ak === 2)) l10 = ak === 1 ? c198 : c1a0;
else if (cfg.anjunCap12 !== false && l10 !== 0 && 1.2 * l10 < l18) cap = capAuto = true;
const anjBasis = b0 + U + knSa - Dn;
let anjCand = cap ? tr(1.2 * l10) : tr(l18),
anjDesc = cap
? "(직노+직재+간재)×율 + 기초액의 1.2배"
: "(직노+직재+간재+관급재/1.1)×율 + 기초액";
// 안전관리비 변경 = I_ANJUNAUTO 2 · 같은 문 · 당초 밑수 ≥ 1(Main 0x46c9f1~0x46caaa)
if (gate && auto === 2 && num(prev.anjunBaseRaw) >= 1) {
const pa = prevAmt("ANJUN"),
pb = num(prev.anjunBaseRaw);
anjCand = pa + tr(((anjBasis - pb) * pa) / pb);
anjDesc = "당초금액 + 안전관리비 증감액";
}
expAcc += put("ANJUN", roundHalfUp(anjBasis, 0), o.R("ANJUN"), anjCand, anjDesc);
if (su) expAcc += put("S_ANJUN_B", 0, 0, o.k("C_S_ANJUN_B"), "별도내역");
const anjB = o.k("C_ANJUN_B"),
pumB = o.k("C_PUMJIL_B");
expAcc += put("ANJUN_B", 0, 0, anjB, "별도내역") + put("PUMJIL_B", 0, 0, pumB, "별도내역");
// 5) 기타경비(일반) · 수공 세부경비 8 — 공통 밑수(재료비+노무비 · 실적은 직접공사비 또는 조정계수)
const b98 = actl && !o.k("I_LTYP") && c98 != null ? c98 : c90 + kanno;
const a8 = roundDown(o.R("R_MxK") * T, 0),
gL = actl && o.k("I_LTYP") === 1;
const bKita =
o.k("I_MTYP") === 1
? actl
? T
: b98
: gL && ((b98 > a8 && o.k("I_MXK") === 0) || o.k("I_MXK") === 1)
? a8
: b98;
const kitaDesc = actl && o.k("I_MTYP") === 1 ? "직접공사비" : "직접노무비+간접노무비+재료비";
if (std)
expAcc += pctItem("KITA", bKita + U + hol, kitaDesc, "([M_AMT]+[L_AMT][+LAB])", {
M_AMT: bKita + U,
L_AMT: 0,
LAB: hol,
});
if (su)
for (const c of S8)
expAcc += pctItem(c, bKita, kitaDesc, "([L_AMT]+[M_AMT])", { L_AMT: bKita, M_AMT: 0 });
// 6) 공사이행보증 · 하도급 · 환경 · 건설기계 — 밑수 직접공사비(+안전품질 별도·비과세)
const F = o.k("I_ANPUM") === 1 ? anjB + pumB : 0,
tBase = T + F + U;
const ks = tr(
(o.k("C_KONGSU_A") + ((T + F + U - o.k("C_KONGSU_MA")) * o.R("KONGSU")) / 100) *
o.k("Y_KONGSU"),
);
expAcc += put("KONGSU", T, o.R("KONGSU"), ks, "[기초금액+(직접공사비-기준금액)×율]×공기");
const t3 = "재료비 + 직접노무비 + 산출경비";
expAcc += pctItem("HADOSU", tBase, t3, "[T_AMT]", { T_AMT: tBase });
expAcc += pctItem("HWAN", tBase, t3, "[T_AMT]", { T_AMT: tBase });
expAcc += pctItem("EQIP", tBase, t3, "[T_AMT]", { T_AMT: tBase });
expAcc += pctItem("BUDM_SM", Lp + hol, "노무비", "[LAMT]", { LAMT: Lp + hol });
expAcc += pctItem("BUDM_IC", Lp + hol, "노무비", "[LAMT]", { LAMT: Lp + hol });
// 고용개선지원비(서울 5형식) — 주휴수당·사회보험·장려금 각 절사 후 합 · 이윤 뒤 가산 · REA 20
let eisAdd = 0;
if (ctx === 5) {
const i1 = o.R("EIS_HOLIR1"),
i2 = o.R("EIS_HOLIR2");
const hpay = roundDown(((((L * i1) / 100) * i2) / 100) * 1.4, 0);
const keep = (c) => num(o.keys["D_" + c]) === 1;
const soc = keep("EIS_SOCINS")
? o.k("C_EIS_SOCINS")
: roundDown(((L * (o.R("KUNBO") + o.R("YUNBO"))) / 100) * (o.R("EIS_SOCIR2") / 100), 0);
const inc = keep("EIS_INCENT")
? o.k("C_EIS_INCENT")
: roundDown(((((L * i1) / 100) * i2) / 100) * (o.R("EIS_INCENT") / 100), 0);
keysOut.C_EIS_HOLPAY = hpay;
const sup = put(
"EIS_SUPPAY",
L,
0,
tr(hpay + soc + inc),
"주휴수당+사회보험+장려금",
"",
"post",
);
eisAdd = sup + (keep("EIS_SOCINS") ? put("EIS_SOCINS", L, 0, soc, "", "", "post") : 0);
eisAdd += keep("EIS_INCENT") ? put("EIS_INCENT", L, 0, inc, "", "", "post") : 0;
Lp += hol;
}
// 순공사원가(헬퍼 합) — 일반 = 재료비 + 노무비(직+간) + 경비 · 실적 = 직접공사비 + 간접공사비
const add1 = addSum(1);
expAcc += add1;
const total30 = actl ? T + expAcc : M + Lp + expAcc;
const anjunp = put(
"ANJUNP",
total30 + knSa,
o.R("ANJUNP"),
tr(((total30 + knSa) * o.R("ANJUNP")) / 100),
"(순공사원가+관급)×율",
"",
"post",
);
// 7) 일반관리비 — 일반 계열은 비과세자재·관리품목자재 포함
const ilbanBase = std ? total30 + o.k("C_NBUGAM") + o.k("C_T_K_MAT") : total30;
const ilban = put(
"ILBAN",
ilbanBase,
o.R("ILBAN"),
tr((ilbanBase * o.R("ILBAN")) / 100),
"순공사원가",
"",
"ilban",
);
const A = total30 + ilban;
// 8) 이윤 — 일반 (계−재료비)×율 · 실적 계×율 · 원 단위 절사 · 이윤보정액 차감 · 공급가액 절사(I_JEOLSA)
const rIyun = o.R("IYUN"),
P = actl ? roundDown((A * rIyun) / 100, 0) : roundDown(((A - M) * rIyun) / 100, 0);
const fixedAdds =
anjunp +
put("PSAMT_B", 0, 0, o.k("C_PSAMT_B"), "별도", "", "post") +
put("EXCEPT_B", 0, 0, o.k("C_EXCEPT_B"), "별도", "", "post") +
(std && posSa === 1 ? Kres : 0) +
eisAdd +
addSum(2);
const k4 = (o.R("S_SIGONG") / 100) * o.R("S_SIGONG_1"),
jeol = o.k("I_JEOLSA");
const sig = (x) =>
ctx === 4 ? (o.fix("S_SIGONG") ?? roundDown((A + P - x + Kres) * k4, 0)) : 0;
let mAdj = iyunM,
sigong = 0,
RT;
if (!jeol) {
sigong = ctx === 4 ? (o.fix("S_SIGONG") ?? roundDown((A + (P - mAdj) + Kres) * k4, 0)) : 0;
RT = A + (P - mAdj) + fixedAdds + sigong;
} else {
const s0 = sig(0),
gross = A + P + fixedAdds + s0,
target = roundDown(gross, -jeol),
delta = gross - target;
if (ctx !== 4) ((mAdj = delta), (sigong = s0));
else {
let x = roundBanker(delta / (1 + k4) + 0.5, 0);
if (x + s0 - sig(x) !== delta) x = roundDown(delta / (1 + k4), 0);
sigong = x + s0 - sig(x) === delta ? sig(x) : s0 - (delta - x);
mAdj = x;
}
RT = A + (P - mAdj) + fixedAdds + sigong;
}
if (ctx === 4)
put(
"S_SIGONG",
A + P + Kres,
o.R("S_SIGONG"),
sigong,
"(순공사원가+일반관리비+이윤+사급비)×율×계수",
"",
"post",
);
const iyunFinal = P - mAdj,
supply308 = RT;
keysOut.C_IYUN_M = mAdj;
// 9) 공사손해보험 — 대상액 C_KONGSA_J 8갈래(B_KONGSA=1 이면 저장값) · 공급가액 절사 자리
const G23 = posSa === 2 || posSa === 3 ? 1 : 0;
const kjCase = [
gA / 1.1 + supply308,
gA + supply308,
supply308 + (G23 * Kres) / 1.1,
supply308 + G23 * Kres,
gA / 1.1 + supply308 + G23 * Kres,
gA + supply308 + (G23 * Kres) / 1.1,
gA / 1.1 + supply308 + (G23 * Kres) / 1.1,
gA + supply308 + G23 * Kres,
];
const kongsaJ = o.k("B_KONGSA")
? o.k("C_KONGSA_J")
: knT > 7
? supply308
: roundDown(kjCase[knT], 0);
keysOut.C_KONGSA_J = kongsaJ;
keysOut.C_KN_SA = knSa;
RT += put(
"KONGSA",
kongsaJ,
o.R("KONGSA"),
roundDown((kongsaJ * o.R("KONGSA")) / 100, -jeol),
"공사손해보험 대상액",
"",
"post",
);
RT -= put("BUSAN_B", 0, 0, o.k("C_BUSAN_B"), "작업설 등", "", "post");
// 10) 부가세(I_BUGA · REA 20) — 0 공급가액 · 1 재료비 · 2 없음 · 3 산림조합(직접비−노무비+안전품질+과세 추가) · 4 3에서 면세품 뺌
const buga = o.k("I_BUGA"),
up = bugaUp ? 1 : 0,
E = addSum(2, true);
const forest = T - L + F + E - (buga === 4 ? o.k("C_BUGA_M") : 0);
const vat =
buga === 0
? roundDown(0.1 * RT, 0) + up
: buga === 1
? roundDown(0.1 * M, 0) + up
: buga === 3 || buga === 4
? roundDown(forest * 0.1, 0) + up
: 0;
const supplyFinal = RT;
RT += vat;
// 11) 세후 가산 · 도급 절사 · 관급(도급자설치·관급자설치 따로 자리·절상/반올림/절사) · 사급 · 추가4
RT += put("PURCHAS", 0, 0, o.k("C_PURCHAS"), "", "", "post");
if (std)
for (const c of ["T_PEGI", "T_KOLJAE", "NBUGAM"])
RT += put(c, 0, 0, o.k("C_" + c), "", "", "post");
if (std && posSa === 2) RT += Kres;
const add3 = addSum(3);
RT += add3;
const contractRaw = RT;
if (o.k("I_DOG_JEOL") > 0) RT = roundDown(RT, -o.k("I_DOG_JEOL"));
const contractFinal = RT;
const rnd = (v, digits, updn) =>
updn === 1
? roundBanker(v, -digits)
: updn === 2
? roundDown(v, -digits)
: roundUp(v, -digits);
const comb = o.k("I_KWN_COMB");
const govFinal = rnd(comb === 0 ? gD : gAll, o.k("I_KWN_JEOLSA"), o.k("I_KWN_UPDN"));
const govK = comb === 0 ? rnd(gK, o.k("I_KWN_K_JEOLSA"), o.k("I_KWN_K_UPDN")) : 0;
RT += govFinal + govK;
Object.assign(keysOut, { C_UPDNKWN: govFinal, C_KWANGUP: gAll, C_KWANGUP_D: gD });
Object.assign(keysOut, { C_KWANGUP_K: gK, C_T_JODAL: jodal });
for (const c of [
"DOSEOH",
"S_SIGONG",
"EIS_SUPPAY",
"EIS_SOCINS",
"EIS_INCENT",
"EIS_HOLPAY",
...S8,
])
if (keysOut["C_" + c] == null) keysOut["C_" + c] = o.fix(c) ?? 0;
const sagAfter = std && (posSa === 1 || posSa === 2) ? 0 : Kres;
RT += sagAfter;
RT += addSum(4);
const anjunCase = { c190, c198, c1a0, l10, l18, cap, capAuto, b0, U, Dn, knSa };
return Object.assign(
{ rows, keysOut, M, L, S, T, Lp, hol, kanno, expAcc, add1, total30, ilbanBase, ilban, A, P },
{ mAdj, iyunFinal, fixedAdds, sigong, supply308, kongsaJ, supplyFinal, vat, contractRaw },
{ contractFinal, govFinal, govK, sagAfter, gD, gK, jodal, knSa, anjBasis, anjunCase },
{ grand: RT, add3, rIyun, ctx, actl, adds },
);
}
Object.assign(calcKanjub, { evalTpl: P.evalTpl, LABEL: P.LABEL, ORD_CODE: P.ORD_CODE });
Object.assign(calcKanjub, { KAN_ORD: P.KAN_ORD, keyOf, codeOf: P.codeOf });
return calcKanjub;
};
if (typeof window !== "undefined") window.STMATE_KANJUB_FACTORY = StmateKanjubFactory;
if (typeof module !== "undefined" && module.exports) module.exports = StmateKanjubFactory;