fteg-pos SKU pos P0 documented P1 foundation next first tenant 9981yuzu

FTEG POS

FTEG-owned in-person sales product. First client is 9981Yuzu (Yuzu Marketing Sdn Bhd), replacing Loyverse for expo and counter checkout. A working prototype is milestone P2. Production readiness and Loyverse migration are demonstrated separately. Do not switch live operations without explicit authorization.

Decision (14 Sep 2026)
FTEG POS is a new sellable product, not a module inside 9981 ecommerce and not a Next.js greenfield. Use the existing FTEG stack: Laravel modular monolith + pkg-pos engine + bo-starter BO, same pattern as Reservation and Social Content. 9981’s live Loyverse sync stays authoritative until a documented cutover.
Do not delete Loyverse data, cancel Loyverse subscriptions, take real card charges, or switch booth/counter operations off Loyverse without written authorization. During any pilot, one system only may record live sales.

Contents

  1. Product identity
  2. Three layers
  3. 9981 Loyverse study
  4. What already exists (preserve)
  5. Architecture
  6. n8n boards (whole POS)
  7. Screen flows & mocks
  8. Data model
  9. Money, tax, inventory
  10. Core POS
  11. Back office
  12. Offline and hardware limits
  13. Loyverse migration
  14. Security and operations
  15. Interface
  16. Discovery questions
  17. Track P milestones
  18. Verified vs not claimed
  19. Verification and handoff

1. Product identity

FieldValue
ProductFTEG POS
SKUpos (fteg-store later; not sold yet)
OwnerFTEG Technology Sdn Bhd — repository, hosting, DB, service accounts
First tenant9981Yuzu — brand of Yuzu Marketing Sdn Bhd
ReplacesLoyverse POS at expo / booth / counter (~3 years of use)
Does not replace9981 web shop, member app, YMSB ERP, marketplace channels
Currency defaultMYR — configurable per company; never hard-coded as the only currency
Timezone defaultAsia/Kuala_Lumpur — configurable; business-day reports use this clock
TaxConfigurable. Do not infer SST, service charge, or e-Invoice duty from this plan
Progress log/srv/www/fteg-products/pos/docs/PROGRESS.md

POS is a transaction system (receipts, cash, shifts), not a CRM. Expo walk-ups stay anonymous unless they already have a 9981 member or a wholesale account. See Yuzu sales channels.

2. Three layers

LayerPathRole
Product /srv/www/fteg-products/pos Multi-tenant host — register UI, merchant BO, APIs. First tenant: 9981Yuzu demo outlet.
Engine /srv/www/pkg-pos Composer library — catalog, cart, tender, receipts, shifts, inventory ledger, migration, permissions.
Commercial /srv/www/hubs/fteg-erp + fteg-store Later: catalog SKU, subscribe, provision tenant. Not in P0–P2.
fteg-pos (product app)
  ├── Tenant: 9981yuzu   ← first client (YMSB brand)
  ├── Tenant: demo       ← fictional catalog for development
  └── Future tenants     ← other FTEG POS subscribers

Consumers / siblings (do not merge codebases)
  ├── 9981beelongz       ← live ecommerce + current Loyverse sync (source for migration)
  ├── ymsb Business OS   ← bottle stock book of record; POS may later POST channel sales
  └── fteg-commerce      ← web shop future; not the till

3. 9981 Loyverse study

Studied from 9981beelongz (14 Sep 2026). n8n boards: fteg-pos-map.html. Screen mocks: fteg-pos-flows.html.

TodayWhat FTEG POS must do
Loyverse iPad is the live till. 9981 only syncs after the sale (15 min receipts). Own the till. Do not dual-run with Loyverse.
No expo-vs-store mode in code. Event List + cashier-day binding sets event_id. Choose event / sale type on open shift.
Bottles → inventory logs; cocktails/shots/OTR pours → cocktail inventory; Gachapon RM99 allocated later in BO. Same split. Gachapon prize is a BO screen, not checkout.
Cash, non-integrated card, DuitNow/QR/OCBC — recorded, not captured. Keep that model in P2. Label recorded vs captured.
Walk-in default. Loyverse points not used as 9981 loyalty. Lucky draw only if a member is attached. Walk-in default. Find by phone or name; attach only with phone / email / member id. No loyalty replay on import.
Cancelled / REFUND receipts stored then mostly excluded. No clean refund pipeline. Native linked refunds (P4). Imported sales read-only until D12.
Loyverse stock unused. Sheets / gg_inventory after the event. Thermal receipt is a PDF. POS inventory ledger for native sales. Print hardware is P5.

9981 admin Loyverse POS menu is audit/export, not a till. Event P&L stays on Event List during P2.

4. What already exists (preserve)

9981 already runs a Loyverse integration. FTEG POS must reuse knowledge and data, not rewrite it in place.

Existing assetKeep as
9981beelongz loyverse_* tables, API sync, raw JSON Immutable source / archive until cutover. Do not stop the live sync.
LoyversePosOrderImportService → 9981 orders Operational bridge for today’s expo P&L and gg_orders. POS import is a separate pipeline.
config/loyverse_sku_mapping.php + F&B SKU seeds Catalog mapping input for migration. Do not invent missing SKUs.
Cashier ↔ event bindings, receipt-prefix mapping Event attribution model. POS must support sale-type / outlet / event the same way.
Payment-type map (cash, non-integrated card, DuitNow, bank transfer) Proves 9981 records externally processed card/QR — it does not charge cards.
Member ↔ Loyverse customer sync Evaluate in P4. Do not award loyalty twice on historical import.
YMSB ERP POST /api/v1/channel/sales Bottle stock issue after POS is live. Glass/cocktail remains POS revenue only.

5. Architecture

4.1 Stack (decided)

ChoiceWhy
Laravel 12 + PHP 8.2 + bo-starterEvery other FTEG product. laravel-ops deploy, permissions, commits.
pkg-pos engineShared validation and money math. Host stays thin.
PostgreSQL preferred; MySQL acceptable if the first host already uses itExact numeric types; migrations via Laravel.
Blade + Livewire (or a small JS island) for P1–P2 till UIShip a touch-friendly register without a second runtime. Revisit a dedicated SPA only if till latency requires it.
Modular monolithNo microservices. POS, BO, and migration share one app and one DB per tenant host.
Not Next.js / not a new Node POSWould fork ops, auth, and MyInvois from the rest of FTEG.

4.2 Tenancy

Data is scoped company → brand → outlet → register → staff membership. Server-side checks on every write. A 9981 cashier cannot see another tenant. A cashier assigned to Outlet A cannot close Outlet B’s shift.

4.3 Payments (recording vs integrating)

Recording that the guest paid by DuitNow / terminal / cash is not a payment-provider integration. FTEG POS will not claim to process or verify QR or card payments until a real provider (e.g. Fiuu terminal or DuitNow QR) is wired with callbacks, retries, pending states, and idempotent notifications. Never store raw card numbers or PAN/CVV.

4.4 e-Invoice

Malaysian e-Invoice is out of P1–P3. When required, integrate via the existing myinvois-cert-mgr / pkg-myinvois path already used by 9981 and FTEG ERP. Verify current LHDN rules before coding. Do not hard-code tax treatment in this product.

6. Data model (P1 target)

company
  └── brand
        └── outlet
              └── register
staff_membership (company/outlet + role: owner | manager | cashier)

category / product / variant / modifier / barcode
outlet_product (availability, price override)
customer (optional; do not match on name alone)
loyalty_ledger (auditable; import = no re-award)

sale (idempotency_key, client_txn_id, source: native|imported, status)
  └── sale_line (description/price/tax/discount/modifiers SNAPSHOT)
  └── payment (method, amount, external_ref, recorded vs captured)
  └── refund → original sale (linked, never mutate finalized sale)

shift (open float, pay-in/out, expected, counted, discrepancy)
inventory_movement (ledger: sale, refund, receipt, transfer, count, waste, adjust)
  └── never applied for historical imported sales

migration_batch / migration_row (source file checksum, source id, errors)
audit_log (refunds, discounts, permissions, stock, config)

Sale states: opentenderingcompleted | voided (pre-finalize, permissioned) | completedrefunded / partially_refunded via linked refunds. Finalized rows are immutable. Corrections are new linked records.

Idempotency: unique (company_id, idempotency_key) and unique (company_id, client_txn_id). Double-tap and retry return the original sale. Network failure must not create a second receipt.

7. Money, tax, inventory

RuleDecision
StorageInteger sen (MYR minor units). Display as 2 decimal places.
ArithmeticInteger only in PHP. Percentage discounts: compute in sen, define half-up vs banker’s rounding in config before P2 ships.
Line snapshotEach sale line stores name, SKU, unit price, qty, tax, discount, modifiers at sale time. Later catalog edits do not rewrite history.
Weighted itemsQty stored as decimal mass; money still integer sen. Enable only when the catalog has weighted products.
Tax / SST / service charge / roundingConfigurable per company. Unset until discovery answers. Tests will lock the chosen rule.
InventoryMovement ledger. Sale deducts; refund restocks only when the refund reason says so. Historical imports never deduct current stock.
Opening stockVerified count at cutover. Not reconstructed from three years of receipts.
Recipes / purchasing / transfersOut of scope until 9981 confirms they need them. Expo cocktails today are POS SKUs, not a recipe engine.

8. Core POS (P2+)

9. Back office

Demo / sample products stay visibly fictional until real 9981 catalog data is supplied or migrated.

10. Offline and hardware limits

P1–P2 are online-only. Offline checkout is P5 after the workflow is agreed and tested on real devices. Browser storage is not a durable ledger. We will not promise zero data loss from IndexedDB / localStorage.
ConstraintImplication
Browser tab / PWA storage Cleared by OS, full disk, or another staff member on a shared iPad. Unsynced sales can vanish. Show unsynced count; do not claim crash-safe durability.
Printers / cash drawers Browsers cannot talk to most ESC/POS printers or RJ11 drawers reliably. Options: (a) cloud print / vendor SDK, (b) local device service on the till, (c) native wrapper (Capacitor — FTEG already has app-wrapper). Decision deferred to P5 after we see the hardware list.
Scanners USB/HID wedge keyboards work in a web till today. Camera scan is fallback only.
Multiple offline registers Can oversell the same SKU. Documented risk; stock is eventually consistent. Managers accept or we require online for stock-tracked items.
Offline payments / refunds Cash only unless a provider supports offline. Refunds against unsynced sales are forbidden.
Clock / price / permission conflicts Server clock and server price book win on sync. Offline permission cache expires. Receipt numbers allocated in a per-register range or repaired on sync — choose in P5.

11. Loyverse migration

10.1 Official sources (reviewed 14 Sep 2026)

Do not assume three years of receipts are API-reachable until the 9981 Loyverse subscription is confirmed. Official Loyverse help: Exporting data. API: https://api.loyverse.com/v1.0 (Bearer token; cursor pages; limit ≤ 250; ~300 req / 300 s). Receipt line items live on the receipt object — there is no separate line-item endpoint.

SourceOfficial capabilityClass
Item list CSV Full catalog; no images 1 — operational catalog (images are a separate requirement)
Customers CSV / API Customers + points if Loyalty is used 1 — if used; match on Loyverse customer id / phone / email, never name alone
Receipts CSV + Receipts by item CSV Back Office export for a selected period 2 — read-only historical sales when detail is complete
API GET /receipts Default window is about 31 days unless Unlimited Sales History is subscribed; older requests can return HTTP 402 2 — only the window we can actually pull. 9981 already stores pulled receipts + raw_data
Sales summary / by item / by payment / taxes / shifts reports Aggregates 3 — reconciliation only. Never reconstruct receipts from totals
Advanced Inventory docs (PO, transfer, count, production) Per-document CSV/PDF if the add-on is used 1 or 3 — unknown until we confirm the 9981 subscription
Item images Not in CSV; no official bulk image export 4 — re-upload or skip
9981 loyverse_receipts / items / customers Already synced locally with source ids and raw JSON 2 — primary historical extract for periods already pulled; still reconcile to Loyverse

Class key: 1 operational · 2 read-only historical · 3 archive / recon only · 4 unavailable or manual.

10.2 Importer rules

10.3 Reconciliation (per outlet and period)

Compare source vs destination: receipt/refund counts, gross/net (definitions written on the report), discounts, taxes, payment totals, stock and loyalty if in scope. Explain every difference. Do not mark migration complete with unexplained gaps. Synthetic fixtures are labelled FIXTURE. Never fabricate missing years.

10.4 Files to request from 9981 when P3 starts

12. Security and operations

13. Interface

Touch-first till: large targets, readable totals, obvious payment and sync status. Destructive money actions need a confirm step and a role check. Keyboard and HID scanner support. Contrast must stay readable on a booth iPad in daylight. Brand colours and logo are provisional until 9981 supplies assets. Sample items in the demo outlet stay fictional (e.g. “Demo Yuzu Soda (FIXTURE)”).

14. Discovery — ask before production decisions

Provisional defaults (MYR, KL timezone, online-only, cash + recorded external pay) are enough to build P1–P2. These answers change schema or hardware:

#QuestionWhy it blocks
D1Business type and actual checkout steps at expo vs any standing counterSale types, held orders, modifiers
D2Number of outlets, tills, concurrent cashiersTenancy, shift model, offline risk
D3Devices and OS (iPad / Android / Windows)PWA vs Capacitor vs local print service
D4Receipt printers, kitchen printers, cash drawers, scanners (make/model)P5 hardware path
D5Loyverse plan: Unlimited Sales History, Loyalty, Advanced Inventory, current stores/devicesHow much history is importable
D6Variants, modifiers, bundles, recipes, stock-tracked vs open-bottleCatalog + inventory scope
D7Cash / QR / card workflow today (which app, which bank)Recorded vs integrated pay
D8Loyalty, refund, discount, tax, rounding, service charge rulesMoney tests; statutory risk
D9Required offline durationWhether P5 is in the first pilot
D10Accounting + MyInvois need for POS receiptsDo not guess LHDN obligations
D11Hosting, budget, who owns the Loyverse token after cutoverOps and data retention
D12How to handle returns on imported Loyverse receiptsMust be written before enabling those refunds

15. Track P — delivery

  1. P0 — Discovery and product registration this document Architecture, assumptions, platform-map node, progress log. Runnable app is P1, not claimed here.
  2. P1 — Runnable foundation next Laravel host + pkg-pos skeleton, schema migrations, auth roles, demo company/brand/outlet/register, fictional catalog, local setup README, .env.example. Automated tests for money helpers and tenancy.
  3. P2 — Complete online checkout Catalog → cart → recorded cash payment → receipt → sales history → stock movement → shift reconciliation. Double-tap / retry tests pass.
  4. P3 — Migration proof Staging, dry-run, import, duplicate prevention, historical reporting, reconciliation on representative exports + 9981 loyverse_* extract. Synthetic fixtures labelled. Gaps explained, not invented.
  5. P4 — Operational features Refunds, permissions, customers/loyalty, inventory workflows, reports the business actually needs (not a recipe module by default).
  6. P5 — Offline and hardware Only the agreed offline scope. Verify on real 9981 devices and printers. Document remaining storage-loss risk.
  7. P6 — Pilot and cutover Backup + restore test, staff acceptance, limited pilot, final import, recon, written rollback. One authoritative sales system during the pilot. Loyverse devices synced; cutover boundary recorded; final stock and balances captured. Original exports kept. Loyverse left intact until authorization to decommission.

16. Verified vs not claimed

ItemStatus
Product exists on the FTEG platform map as fteg-posverified
Written architecture, migration classes, Track Pverified
9981 Loyverse usage study + till/BO wireframesdocumented — mocks are not a working till
Runnable POS applicationnot built
Cash checkout / receipts / shiftsnot built
QR / card payment capturenot integrated — will record external pay only until a provider is wired
Offline checkoutout of scope until P5
Printer / drawerunverified — needs device list
Loyverse 3-year history completeunproven — API window + export coverage TBD (D5)
SST / e-Invoice on POSnot inferred
Live cutoverforbidden without authorization

17. Verification and handoff (required before any “done” claim)

Tests that must exist by the milestone that introduces the behaviour:

Handoff pack: working app, setup + .env.example, migrations + demo seed, migration guide + recon report, hardware findings, deploy/backup/restore/cutover notes, and this verified/blocker list kept current in fteg-products/pos/docs/PROGRESS.md.

References