LTT Engine Room · Internal

PrimaHQ Claims Build Spec

Hand-off spec for the PrimaHQ claims app.
v1
24-Jun-26
PrimaHQClaims_BuildSpecHandoff.md
LTT Outsourced CFO Sdn. Bhd.

PrimaHQ Claims — Build Spec & Handoff

Purpose: everything needed to recreate this expense-claim app for another client, from scratch, in a new chat. Product name shown in app: "PrimaHQ Claims" · tagline "From paper receipt to posted expense." Prepared by: LTT Outsourced CFO Sdn. Bhd. · current build = v9 (single self-contained HTML).


1. What the app is

A single self-contained .html file (double-click to run offline; no server) that lets a bookkeeper: 1. Hold a client's whole-year OCR'd expense line items. 2. Re-classify them (category + account code + type), exclude non-claim items. 3. Brand and export per-month expense-claim reports (client logo + details) with the scanned receipts appended. 4. Export a full-year accounting-import file (Bukku) and a PDF/HTML, plus a Google-Sheets master for bulk editing.

Tech: plain HTML/CSS/JS. CDN libs: SheetJS (xlsx read/write), jsPDF + jspdf-autotable (direct PDF). Poppins font + Noto/KaiTi for Chinese. Data persists in the browser via localStorage (key bumped each version, e.g. primahq_v9). No backend.


2. Inputs to collect from the new client (before building)

  1. Scanned receipts for the year — usually monthly PDFs/zip of receipts (each month a folder or a bundle PDF). Many are scanned images (need OCR), mixed English/Malay/Chinese + handwriting.
  2. A classification workbook (the human-reviewed source of truth) — an .xlsx with 3 tabs:
  3. Claims to submit (not via bank) — the items that go into the claim.
  4. Excluded - bank-paid & dup — already paid via bank / duplicates (kept out).
  5. Excluded - utilities & rental — handled separately (kept out). Columns used: Reference No., Date, Supplier / Payee, Description, Original Category, Acct Code, Account (PrimaHQ classification), Amount (RM), Original Ref, Status, Source File, Page(s).
  6. Client branding — company legal name, registration/SSM no., TIN, full address, and a logo (PNG/JPG). The report theme colour is auto-derived from the logo.

If there's no reviewed workbook yet, you OCR the receipts first (Section 6) to produce a draft dataset, then the client reviews/splits it into the 3 tabs.


3. Data model

Each expense line ("row") object:

{ id, client, clientName, date (YYYY-MM-DD true doc date), month (YYYY-MM),
  supplier, desc, category (= account name), ref, amount (number),
  note, file (relative path), pages [ints], type, inc }

Exclusion logic: isExcluded(row) = inc==='out' ? true : inc==='in' ? false : category.excluded. Excluded rows never enter reports or the Bukku export.

Clients map: { code: {name, reg, tin, addr, tel, email, logo(dataURI), spine(hex)} }. spine = the report accent colour (auto-set from the logo's dominant colour).


4. Classification rules (confirmed with client — apply consistently)


5. App structure (tabs)

  1. Dashboard (front page): KPI cards per entity (claimable total/bills, excluded total), claimable-by-month table, claimable-by-account table, and a red "Accounts without an account code — reassign these" list.
  2. Expenses: claimable items only, editable inline (date, supplier, desc, category dropdown, type, ref, amount, In/Out). Sortable column headers. Page-count + 👁 receipt preview modal. Filters: month/category/type/search. Buttons: download/import master xlsx, "Build report (filtered month)".
  3. Excluded: excluded items, grouped by reason — Paid via bank / duplicate and Utilities & rental — each with subtotal. Export listing / report.
  4. Export: full-year Bukku workbook; per-month branded reports — browser print, Download .html, Export .pdf (no print dialog, via jsPDF); load receipts pack; save/load project JSON.
  5. Settings (last): Clients (details + logo upload + accent colour: from-logo / LTT palette / custom) and Categories (account name, code, Bukku name, Excluded checkbox; add/rename/delete).

Top bar: app name + an Entity dropdown to switch between clients/entities.


6. Report & export formats


7. How the app is assembled (build pipeline)

The HTML is built from a template (app2_template.html) with four placeholders that get string-replaced at build time: - __DATASET__ → JSON array of all line items (all entities). - __SEEDCATS__ → JSON array of categories {name,code,bukku,excluded}. - __CLIENTDEFAULTS__ → JSON of clients {code:{name,reg,tin,addr,tel,email,logo,spine}} (logo embedded as a base64 data URI). - __LTTLOGO__ → LTT app-mark data URI (top bar only; reports use client logos).

Receipts are NOT embedded in the app (too large). They're delivered as a separate receipts pack JSON: { "<file>|<page>": "data:image/jpeg;base64,..." }, loaded at runtime on the Export tab. Keys must equal each row's file + "|" + page.

Filenames (client's convention): app = LTHExpIQ_EntityName(<EntityCodes>)-v<N>-<yymmdd>.<hhmmss>.html; receipts pack = LTHExpIQ_Receipts(<EntityCode>)-v<N>-<yymmdd>.json.


8. Visual identity (LTT SOP001)

Charcoal #323232 text (never pure black), graphite #3D424A table headers, white canvas, hairline rules, generous whitespace, tabular numerals, Poppins + KaiTi. For the app chrome the LTT crimson #C62F58 is the accent; for client reports the accent = the client logo's dominant colour (auto-extracted). Logos embedded as data URIs so files stay self-contained.


9. Known constraints / gotchas (important when rebuilding here)


10. Step-by-step recipe for a NEW client

  1. Collect the 3-tab classification workbook + receipts (zips/PDFs) + logo + company details.
  2. Parse the workbook → build the dataset (rows with id/date/supplier/desc/category/code/type/inc/file/pages). Claims tab → inc:"in"; the two excluded tabs → inc:"out". Capture distinct accounts → seed categories (name+code).
  3. Embed the logo (trim white, base64) and set client details + spine (auto from logo).
  4. Inject dataset/seedcats/clientdefaults/logo into the template → write the app .html (via bash heredoc) → node --check.
  5. Generate the receipts pack from the receipt source, keyed file|page, in batches.
  6. Hand over: the app .html, the receipts pack .json, and a Google-Sheets master .xlsx.
  7. To add more entities later, append to clientdefaults + dataset and rebuild.

11. Current project asset inventory (this client)

Tadika figures (sanity): claims 498 items / RM106,139.24; excluded 68 items / RM83,503.68. JC Prima still on the original OCR classification (44 rows) pending its workbook. Taska empty pending its workbook.

ler.lttcfo.ai/appclaim