BOI: machinery import duty and import VAT, PVC duty relief
Deploy test site / deploy (push) Successful in 35s
Deploy test site / deploy (push) Successful in 35s
User, October 2026: take the BOI duty-free import and VAT exemption into consideration. - boi.md: import VAT goes with the BOI duty exemption (Revenue Department answer on the BOI OSOS FAQ, checked October 2026); what it is worth: ~2.75M USD of imported machinery at the start fleet (block midpoints) would otherwise carry ~5 % duty (Thai MFN averages 4.1 % on HS 8477, 0 to 10 % across HS 84; estimate until the HS codes are set), ~0.14M USD, and 7 % import VAT, ~0.2M paid at the border and recovered later as input tax, mostly as a refund claim since exports are zero-rated. - total-investment.md: the blocks are vendor prices without duty or import VAT, so the headline already assumes promotion. - Fleet calculator: BOI promoted / not promoted switch and export share. Not promoted adds the machinery duty to capex and depreciation, shows the import VAT as cash at the border, and charges duty on imported PVC compound; promoted exempts both, with no compound duty on exported records and 90 % off for domestic sales (sections 36(1) and 30). A note under the KPIs shows what promotion saves either way. Defaults: payback 3.1 years with BOI, 3.2 without; adverse case operating result -31k with BOI, -68k without. CIT and BOI CIT exemptions still not modelled.
This commit is contained in:
1 parent
1a368287f8
commit
8032e0ea47
4 files changed
+68
-11
No files matched your search
@@ -236,6 +236,12 @@ document.documentElement.setAttribute("data-theme",t);})();
|
||||
<button data-v="EUR">EUR</button>
|
||||
</div>
|
||||
<p class="hint" id="priceHint"></p>
|
||||
<div class="row" style="margin-top:12px"><span>BOI promotion</span></div>
|
||||
<div class="seg" id="boi">
|
||||
<button data-v="on">Promoted</button>
|
||||
<button data-v="off">Not promoted</button>
|
||||
</div>
|
||||
<label class="row"><span>Records exported, %<br><small>exports: no duty on their raw material (BOI), VAT zero-rated</small></span><input type="number" id="exportShare" min="0" max="100" step="5"></label>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
@@ -264,6 +270,7 @@ document.documentElement.setAttribute("data-theme",t);})();
|
||||
<section class="card">
|
||||
<h2>Money a year</h2>
|
||||
<div class="kpis" id="ecoK"></div>
|
||||
<div id="boiNote"></div>
|
||||
<div class="bar" id="ecoBar" style="height:28px"></div>
|
||||
<div class="legend" id="ecoLeg"></div>
|
||||
<div class="tscroll" style="margin-top:12px"><table id="ecoT"></table></div>
|
||||
@@ -334,6 +341,14 @@ document.documentElement.setAttribute("data-theme",t);})();
|
||||
the plant. Lacquer blanks (70 EUR each) and the cutting engineer (50,000 THB a month) are team
|
||||
figures; shipping cartons have no figure on file and are an estimate. Missing as the revenue model
|
||||
lists: indirect labour, freight, inventory, effluent, commission, styli, extra galvanics units' capex.</p>
|
||||
<p><b>BOI.</b> The capex blocks in total-investment.md are vendor prices without Thai import duty or
|
||||
VAT, so they already assume BOI promotion: machinery imported under it pays no duty (Investment
|
||||
Promotion Act section 28) and, with the duty, no import VAT (Revenue Department, BOI OSOS FAQ).
|
||||
"Not promoted" adds the duty on the imported machinery to the capex and shows the import VAT as cash
|
||||
paid at the border, reclaimable as input tax. Raw material: no duty on compound for exported
|
||||
records, 90 % off for domestic sales. Revenue is net of VAT (exports zero-rated, 7 % on domestic
|
||||
sales passed on). Corporate income tax and BOI's CIT exemptions (solar, automation, R&D) are not
|
||||
modelled.</p>
|
||||
<p><b>Labour.</b> Base salaries per role (revenue-model.md, its loaded rates divided by 1.3; the cutting
|
||||
engineer a team figure), plus the employer's 5 % social security up to the 17,500 THB ceiling, 25 %
|
||||
for overtime, bonus and PPE, and the company's health care for every member of staff, office
|
||||
@@ -355,7 +370,7 @@ document.documentElement.setAttribute("data-theme",t);})();
|
||||
const S0 = {
|
||||
mode: "target", target: 1000000, spec: 20, grow: "classic", dMin: 2, cMin: 1,
|
||||
dFleet: 2, cFleet: 1, shifts: 2, hrs: 8, days: 300, eff: 65, rateD: 3, rateC: 2, specF: 100,
|
||||
priceStd: 3.8, premSpec: 1.15, titleFee: 1155, cur: "USD",
|
||||
priceStd: 3.8, premSpec: 1.15, titleFee: 1155, cur: "USD", boi: "on", exportShare: 70,
|
||||
};
|
||||
// [group, id, label, value, unit, kind, source]
|
||||
const AS = [];
|
||||
@@ -433,6 +448,14 @@ grp("Money", [
|
||||
["capexC", "Capex per Classic beyond one", 375000, "USD", "doc", "total-investment.md: ~290 to 460k, no Newbilt price on file"],
|
||||
["deprYears", "Depreciation period", 12.5, "years", "doc", "revenue-model.md: 10 to 15 years"],
|
||||
]);
|
||||
grp("BOI and imports", [
|
||||
["impMach", "Imported machinery in the start-fleet capex", 2750000, "USD", "doc", "total-investment.md block midpoints at vendor prices: presses 1.0M, galvanics 485k, Sibert 250k, lathe system 435k, metrology 580k; added presses count as imported"],
|
||||
["dutyMach", "Import duty on that machinery without BOI", 5, "%", "est", "Estimate: Thai MFN averages 4.1 % on HS 8477 plastics machinery (customs-compliance.ai, March 2026), 0 to 10 % across HS 84; set per line once the HS codes are known"],
|
||||
["vatRate", "VAT", 7, "%", "doc", "Thai VAT; with BOI duty exemption the import VAT is exempt too (Revenue Department, BOI OSOS FAQ; boi.md)"],
|
||||
["pvcImp", "Share of PVC compound imported", 100, "%", "est", "Estimate: record-grade compound from the specialist suppliers"],
|
||||
["dutyPVC", "Import duty on PVC compound without BOI", 5, "%", "est", "Estimate; no Thai tariff line checked for record compound yet"],
|
||||
["boiRawRed", "BOI duty reduction on raw material for domestic sales", 90, "%", "doc", "boi.md: exempt for export production (section 36(1)), up to 90 % off for domestic sales (section 30)"],
|
||||
]);
|
||||
grp("Materials and packaging", [
|
||||
["pvcPrice", "PVC compound", 2.3, "USD/kg", "doc", "revenue-model.md: 1.75 to 2.90 USD/kg record grade"],
|
||||
["labelCost", "Labels, a pair", 0.06, "USD", "doc", "revenue-model.md: 0.03 to 0.09"],
|
||||
@@ -597,13 +620,19 @@ function headsOf(r) {
|
||||
};
|
||||
}
|
||||
// Money a year in USD. Lines: [group, label, USD, basis].
|
||||
function econ(r, b) {
|
||||
function econ(r, b, boiOn = S.boi !== "off") {
|
||||
const fx = A.thbUsd > 0 ? A.thbUsd : 33, t2u = v => v / fx;
|
||||
const X = r.X, pressed = X * (1 + A.reject / 100), st = X - r.sp, L = [];
|
||||
const add = (g, l, v, s) => L.push([g, l, v, s]);
|
||||
const revStd = st * S.priceStd, revSpec = r.sp * (S.priceStd + S.premSpec), revTitle = r.titles * S.titleFee;
|
||||
const rev = revStd + revSpec + revTitle;
|
||||
add("mat", "PVC compound", r.compound * 1000 * A.pvcPrice, `${f0(r.compound)} t at ${f2(A.pvcPrice)} USD/kg incl. rejects; trim back as regrind`);
|
||||
const expSh = Math.min(100, Math.max(0, S.exportShare)) / 100;
|
||||
const pvcImported = r.compound * 1000 * A.pvcPrice * A.pvcImp / 100;
|
||||
const pvcDuty = pvcImported * A.dutyPVC / 100 * (boiOn ? (1 - expSh) * (1 - A.boiRawRed / 100) : 1);
|
||||
add("mat", "Import duty on PVC compound", pvcDuty, boiOn
|
||||
? `BOI: none on the ${pct(expSh)} exported, ${f0(A.boiRawRed)} % off the rest`
|
||||
: `${f1(A.dutyPVC)} % on ${pct(A.pvcImp / 100)} imported compound, no BOI`);
|
||||
add("mat", "Labels", pressed * A.labelCost, "a printed pair per pressed record");
|
||||
add("pack", "Inner sleeves", X * A.sleeveCost, "");
|
||||
add("pack", "Jackets (printed cartons)", X * A.jacketCost, "local carton partners");
|
||||
@@ -638,9 +667,12 @@ function econ(r, b) {
|
||||
`yearly for everyone, plus risk-factor tests for ${ppl(h.exposed)} on the press floor, in galvanics and maintenance`);
|
||||
const sum = gs => L.filter(l => gs.includes(l[0])).reduce((a, l) => a + l[2], 0);
|
||||
const direct = L.reduce((a, l) => a + l[2], 0), contrib = rev - direct, ebitda = contrib - A.overheads;
|
||||
const capex = A.capexBase + (r.D - 2) * A.capexD + (r.C - 1) * A.capexC, depr = capex / Math.max(1, A.deprYears);
|
||||
const fleetDelta = (r.D - 2) * A.capexD + (r.C - 1) * A.capexC;
|
||||
const imp = Math.max(0, A.impMach + fleetDelta);
|
||||
const dutyM = boiOn ? 0 : imp * A.dutyMach / 100, vatM = boiOn ? 0 : (imp + dutyM) * A.vatRate / 100;
|
||||
const capex = A.capexBase + fleetDelta + dutyM, depr = capex / Math.max(1, A.deprYears);
|
||||
return { rev, revStd, revSpec, revTitle, L, sum, direct, contrib, ebitda, capex, depr, op: ebitda - depr,
|
||||
payback: ebitda > 0 ? capex / ebitda : Infinity, heads: h };
|
||||
payback: ebitda > 0 ? capex / ebitda : Infinity, heads: h, imp, dutyM, vatM, pvcDuty, boiOn };
|
||||
}
|
||||
function scenario() {
|
||||
const b = basis(S);
|
||||
@@ -680,7 +712,7 @@ function render() {
|
||||
$("specV").textContent = S.spec + " %";
|
||||
$("targetBox").hidden = S.mode !== "target";
|
||||
$("fleetBox").hidden = S.mode !== "fleet";
|
||||
for (const g of ["mode", "grow", "cur"]) for (const bt of $(g).children) bt.classList.toggle("on", bt.dataset.v === S[g]);
|
||||
for (const g of ["mode", "grow", "cur", "boi"]) for (const bt of $(g).children) bt.classList.toggle("on", bt.dataset.v === S[g]);
|
||||
const cs = caseNow();
|
||||
for (const bt of $("case").children) bt.classList.toggle("on", bt.dataset.v === cs);
|
||||
|
||||
@@ -738,6 +770,11 @@ function render() {
|
||||
kpi(money(e.op), "operating profit after depreciation") +
|
||||
kpi(perRec(e.direct), "direct cost per record") +
|
||||
kpi(isFinite(e.payback) ? f1(e.payback) + " years" : "never", `payback on ${money(e.capex)} capex`);
|
||||
const eOn = econ(r, b, true), eOff = econ(r, b, false);
|
||||
const pb = x => isFinite(x.payback) ? f1(x.payback) + " years" : "none";
|
||||
$("boiNote").innerHTML = e.boiOn
|
||||
? `<div class="flag ok">BOI promotion: no import duty (~${money(eOff.dutyM)}) and no import VAT (~${money(eOff.vatM)}) on ${money(eOn.imp)} of imported machinery, and ${money(eOff.pvcDuty - eOn.pvcDuty)} a year less duty on PVC compound. Without promotion the capex would be ${money(eOff.capex)} and the payback ${pb(eOff)}.</div>`
|
||||
: `<div class="flag">Not promoted: ${money(eOff.dutyM)} import duty on ${money(eOff.imp)} of imported machinery goes into the capex and its depreciation; ${money(eOff.vatM)} import VAT is paid at the border and only comes back later as input tax (exports are zero-rated, so mostly as a refund claim); ${money(eOff.pvcDuty)} a year duty on PVC compound. With BOI the payback would be ${pb(eOn)}.</div>`;
|
||||
const SEG = [["PVC and labels", e.sum(["mat"]), "#b07d48"], ["Packaging", e.sum(["pack"]), "#c9a24a"],
|
||||
["Lacquers and plating", e.sum(["metal"]), "#8e98a6"], ["Energy", e.sum(["energy"]), "#e2462a"],
|
||||
["Labour", e.sum(["lab"]), "#2f7de1"], ["Maintenance", e.sum(["maint"]), "#7a6fd6"],
|
||||
@@ -764,7 +801,7 @@ function render() {
|
||||
PR("Contribution", e.contrib, e.rev > 0 ? pct(e.contrib / e.rev) + " of revenue" : "", true);
|
||||
PR("Overheads", A.overheads, "admin, sales, insurance, facility, IT, fees, marketing");
|
||||
PR("EBITDA", e.ebitda, e.rev > 0 ? pct(e.ebitda / e.rev) + " of revenue" : "", true);
|
||||
PR("Depreciation", e.depr, `${money(e.capex)} capex over ${f1(A.deprYears)} years`);
|
||||
PR("Depreciation", e.depr, `${money(e.capex)} capex${e.dutyM > 0 ? ` incl. ${money(e.dutyM)} import duty` : ""} over ${f1(A.deprYears)} years`);
|
||||
PR("Operating profit", e.op, isFinite(e.payback) ? `payback ${f1(e.payback)} years on EBITDA` : "no payback", true);
|
||||
$("ecoT").innerHTML = `<thead><tr><th>Line</th><th class="n">A year</th><th class="n">A record</th><th>Basis</th></tr></thead><tbody>${P.join("")}</tbody>`;
|
||||
|
||||
@@ -976,7 +1013,7 @@ function drawPark(r, b) {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- inputs and state
|
||||
const NUM = ["target", "dMin", "cMin", "dFleet", "cFleet", "shifts", "hrs", "days", "eff", "rateD", "rateC", "specF", "priceStd", "premSpec", "titleFee"];
|
||||
const NUM = ["target", "dMin", "cMin", "dFleet", "cFleet", "shifts", "hrs", "days", "eff", "rateD", "rateC", "specF", "priceStd", "premSpec", "titleFee", "exportShare"];
|
||||
function syncInputs() {
|
||||
for (const k of NUM) $(k).value = S[k];
|
||||
$("targetR").value = S.target;
|
||||
@@ -1009,6 +1046,7 @@ function readHash() {
|
||||
if (!["target", "fleet"].includes(S.mode)) S.mode = "target";
|
||||
if (!["classic", "duplex"].includes(S.grow)) S.grow = "classic";
|
||||
if (!["USD", "THB", "EUR"].includes(S.cur)) S.cur = "USD";
|
||||
if (!["on", "off"].includes(S.boi)) S.boi = "on";
|
||||
}
|
||||
function num(el, fallback) { const v = parseFloat(el.value); return isFinite(v) ? v : fallback; }
|
||||
|
||||
@@ -1022,7 +1060,7 @@ $("case").addEventListener("click", e => {
|
||||
const v = e.target.dataset && e.target.dataset.v; if (!v || !CASES[v]) return;
|
||||
Object.assign(S, CASES[v].S); Object.assign(A, CASES[v].A); syncInputs(); render();
|
||||
});
|
||||
for (const g of ["mode", "grow", "cur"]) $(g).addEventListener("click", e => {
|
||||
for (const g of ["mode", "grow", "cur", "boi"]) $(g).addEventListener("click", e => {
|
||||
const v = e.target.dataset && e.target.dataset.v; if (!v) return;
|
||||
if (g === "mode" && v === "fleet" && S.mode === "target") { const { r } = scenario(); S.dFleet = r.D; S.cFleet = r.C; syncInputs(); }
|
||||
S[g] = v; render();
|
||||
|
||||
Reference in new issue
Block a user