Deploy test site / deploy (push) Successful in 54s
Solar (user, 17 Oct 2026: "use as much roof space as possible") - PV on every roof and canopy: ~640 kWp on the Payap scenario plot (factory roof ~523, canopies ~54, flat office roof ~65 kWp around the AC units); ~300 kWp on the Mueang Chi hall. Calculator: pvKWp 640, new pvSelf 85 % (zero export, hourly sketch), PV cost per kWp times the array, office-roof PV in the iso view, roofs drawn full. - 12.8M to 16.6M THB at 20 to 26 THB/W; CSV baseline 34.37M to 56.34M; launch plant ~168M / 231M / 294M THB with contingency; Plan B ~216M (label ~215M), ~39M above Plan A; Plan A unchanged (it leases the array), its solar lever ~17.5M. PV payback ~4.2 to 5.9 years. - Permits (rooftop solar no factory licence since 28 Dec 2024, ERC exemption under 1,000 kVA, PEA zero export) and PPA discounts sourced in sustainability.md. BOI correction - Announcement 3/2568 (in force 1 July 2025) ended BOI promotion for solar installations; only the battery may qualify (12M THB per MW cap). Removed the "BOI solar measure" from boi.md, total-investment, revenue-model, sustainability, site-candidates and both plan versions; Plan B's case for owning the array is the PPA bill it avoids (~0.07M USD a year). Open source (user, 17 Oct 2026) - digital-factory.md "Open source throughout, and working with other plants": everything we run and build is open source on NixOS, our code published (licence to decide), vendor firmware the stated exception; later the stack and factory-automation services for other pressing plants, not in the revenue. - Business plan section 5.6 plus an executive-summary bullet, reading guide and not-modelled mentions; draft 0.3 rebuilt (A 60, B 61 pages), renders re-made with the roofs full.
47 lines
1.6 KiB
Python
47 lines
1.6 KiB
Python
"""Build the CNX Vinyl business plan .docx, in its two versions (October 2026).
|
|
|
|
python build_plan.py -> ../CNX-Vinyl-Business-Plan-A.docx and -B.docx
|
|
python build_plan.py A -> ../CNX-Vinyl-Business-Plan-A.docx
|
|
python build_plan.py B out.docx -> Plan B to a custom path
|
|
|
|
Plan A (~180M THB) and Plan B (~215M THB) share every chapter; the content modules
|
|
switch the plan-specific text with plan_variant.pick(). Images are resolved relative
|
|
to docs/ (the parent of business-plan/).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
sys.path.insert(0, str(HERE))
|
|
|
|
from plan_lib import Renderer # noqa: E402
|
|
import plan_variant # noqa: E402
|
|
import content_a, content_b, content_c # noqa: E402
|
|
|
|
DOCS = HERE.parent.parent # .../docs
|
|
VERSION = "draft 0.3, October 2026"
|
|
|
|
|
|
def out_path(plan: str) -> Path:
|
|
return HERE.parent / f"CNX-Vinyl-Business-Plan-{plan}.docx"
|
|
|
|
|
|
def main(plan: str = "A", out: Path | None = None) -> Path:
|
|
plan_variant.PLAN = plan
|
|
out = out or out_path(plan)
|
|
r = Renderer(DOCS, header_text=f"CNX Vinyl / Poy Records: Business plan, {plan_variant.label()}, {VERSION}",
|
|
footer_text="Confidential")
|
|
r.render(content_a.blocks() + content_b.blocks() + content_c.blocks())
|
|
r.save(out)
|
|
return out
|
|
|
|
|
|
if __name__ == "__main__":
|
|
args = sys.argv[1:]
|
|
plans = [args.pop(0).upper()] if args and args[0].upper() in ("A", "B") else ["A", "B"]
|
|
target = Path(args[0]) if args else None
|
|
for p in plans:
|
|
print(main(p, target if len(plans) == 1 else None))
|