client platform ERP-first migration

Yuzu Platform Plan

Yuzu Marketing Sdn Bhd on FTEG Business OS (sme-erp, tenant ymsb). 9981 Yuzu is their ecommerce + events channel — not a second company OS.

1. Legal entity vs brand

LayerWhatSystem
CompanyYuzu Marketing Sdn Bhd (1406927-K)Business OS + fteg-store buyer ymsb
Brand9981 Yuzu — fruit alcohol, online + expo9981 ecommerce (9981beelongz)
B2B customersBars, hotels, distributorsCRM clients (erp_clients, segment b2b)
B2C shoppersOnline + Loyverse walk-in9981 users / orders — not every row in CRM

2. Architecture

fteg-store (billing, portal, metrics)
       │
       ├── sme-erp / ymsb  ← company OS (CRM, quotes, invoices, catalogue master)
       │       └── launcher → 9981 BO
       │
       └── 9981 Yuzu       ← sales channels (web, Loyverse expo, events)
               └── stock / Loyverse ops (until ERP inventory freeze)
Build ERP first. 9981 is already live — keep using it for B2C checkout, events, Loyverse. Do not start Ecommerce 2.0 / fteg-commerce work while YMSB still needs company-OS depth (pricing live, purchasing, stock visibility). 9981 stays frozen-legacy: no new price columns, no rewrite in place.

ymsb-erp transitional shell is frozen — use ymsb.sme-erp.fteg.dev.

3. Data ownership (canonical)

DataMasterNotes
B2B clientsBusiness OS CRMQuotations, invoices, e-Invoice B2B
Supplierserp_supplierslive Import: ymsb:import-suppliers-from-9981
Trade SKUserp_skuslive Import: trade:import-skus-from-9981
Price bookERP erp_price_tiers + erp_sku_priceslive One matrix for B2C + B2B. Ecommerce does not own prices.
Storefront / PDP / promos9981 (legacy)Display + promo only. Sell price = ERP tier. 9981 stays frozen; new shop is Ecommerce 2.0.
B2C orders9981 orderssource: online-ticket or loyverse
Stock ops (today)9981 + Google Sheets + LoyverseERP v1 is a thin move log only. Shop/POS still issue from 9981 / Sheets.
Inventory (target)ERP lot ledgerOne stock book for all channels. Shops issue from ERP — they do not JIT-procure. See §8.

4. Sales channels

ChannelHow sale entersorders.source
Online ecommerce9981 storefront checkoutonline-ticket
Expo / boothLoyverse POS → sync → gg_orders. Planned replacement: FTEG POS (do not dual-run)loyverse
Social / manual BOStaff order in 9981 BO (WhatsApp, IG, FB)manual + platform
MarketplaceShopee / Lazada (Sheets or API → orders)platform + external_id
B2B wholesaleSales team → Business OS quote → invoice or logged-in trade reorder (later)ERP (not a second public shop)

Expo linkage: event_lists.event_id + Loyverse Cashier Event Binding. Guide: /srv/www/9981beelongz/docs/EVENT_SALES_SETUP_GUIDE.md

Identity model: Most B2C sales are anonymous transactions (expo cash, marketplaces) — not CRM clients. See Sales Channels & Identity.

5. Pricing — one book, both channels

Do not split the price book between ERP and ecommerce. If staff cannot see every sell price in one glance, the model is wrong. Glance view: Business OS → Procurement → SKU prices (SKU × B2C tiers × B2B tiers).
LayerOwnsDoes not own
ERPTiers per channel (b2c / b2b), per-SKU amounts, client price_tier_idPDP copy, promos, cart UX
EcommerceWho is buying → which tier → charge that cellA second price list
9981 legacyproduct_skus.price as a copy of ERP retail (optional trade:sync-skus-to-9981)Member vs gold vs distributor (loyalty tiers are points, not sell price)
Yuzu Marketing siteBrochure + become-a-partner leadCheckout / cart — ymsbcorporate.fteg.dev stays CMS

Default tiers (per tenant): B2C retail (default) + member; B2B reseller + distributor (default). Add more (gold, hotel, …) in Price tiers. Client with no assigned tier uses the channel default.

BuyerTier used
Guest / not logged inB2C default (retail)
9981 / shop memberB2C member (or assigned gold, etc.)
Hotel / reseller / distributorThat CRM client’s B2B tier

One ecommerce, two channels — not two shops. Same SKU, same stock. Public cart = B2C. Logged-in /trade (Ecommerce 2.0) = B2B self-order at the client’s ERP tier. Sales can still quote in ERP for negotiated deals.

Promos / vouchers discount the tier price. Shopee and Loyverse keep their own channel prices; they are not the web shop book.

6. B2B commercial flow

  1. Partner applies on Yuzu Marketing site → Inbox (or BD promotes a 9981 member)
  2. CRM → Convert to client (b2b, TIN/BRN) + assign price tier
  3. Sales → Quotation (SKU picker uses client tier — done) or later: client logs in and reorders
  4. Accept → Sales order → payment → invoice → e-Invoice
  5. Fulfilment handoff → 9981 BO order (manual today; reference QT-… in remark)

7. ERP-first roadmap

PhaseStatusDeliverable
0doneBusiness OS live for YMSB; 9981 unchanged
1adoneerp_suppliers + BO + import from 9981
1bdoneerp_skus + trade pricing + import from 9981
2doneSKU picker on quotations
3doneProve B2B flow in ERP (quote → order → invoice → e-Invoice)
4donetrade:sync-skus-to-9981 — preview by default. Never writes 0; drops > 20% need --force. ERP retail was refreshed from live 9981 (import). Do not push stale ERP onto the shop.
5doneERP dashboard: online vs Loyverse GMV (read-only from 9981)
6doneymsb:fulfilment-handoff CLI + Sales order → 9981 handoff page
7donePromote 9981 member → Inbox / pipeline (one-way; company name required; no auto-merge)
8doneERP price tiers + SKU matrix + client assignment. Live on YMSB sqlite (4 default tiers, SKU prices backfilled). 9981 still charges product_skus.price until sync.
9–11, 13–28donePurchasing + stock glance + quote economics + follow-up (quotes / POs / invoices). PO tables and supplier email live.
29donePurchasing module complete (no stock ledger). Draft → sent → received/cancelled. PDF, email, copy, draft-from-glance, due/overdue, spend, receive confirm. Closed POs are read-only.
30doneClient price list PDF (that client’s tier) + SKU matrix CSV. Last pricing deliverable before inventory.
12doneInventory v1 — signed moves (opening / receive / adjust). Does not write 9981. Sales still 9981.
12bdoneUOM packs — one stock SKU in base units; CTN/BTL on erp_sku_uoms. Carton is not a second SKU. Stock shows bottles + cartons. Still no sale-out / lots.
12cdoneLocationserp_warehouses (HQ / expo / consignment). Moves + PO receive scoped; transfer_out/in. Light WH, not WMS.
12ddoneLot ledgererp_sku_ins / erp_sku_outs / erp_sku_balances. On-hand from balances; FIFO issue; PO receive creates costed lots. Moves stay audit.
12edoneB2B sale issue — accept quotation → FIFO sale outs from default sellable WH. Hard-block if ERP short. 9981 unchanged.
12fdoneEvent load-in — expo locations per concurrent event; multi-SKU transfer HQ↔event (not a sale). Return leftovers the same way.
12gdoneChannel / POS sale issuePOST /api/v1/channel/sales with warehouse + external_id; FIFO out; open-bottle write-off preset. Glass = POS revenue only.
12hdoneEvent ↔ location linkerp_warehouses.external_event_id = 9981 event_lists.event_id. Channel API accepts event_id or warehouse_code. Same event can carry tickets + POS; ERP only issues bottle stock.
12idoneB2B reverse-out — cancel sales order restores lots at same WH/cost (sale_reverse). Idempotent. 9981 unchanged.
12jdoneSoft reserve — mark-sent holds lots (erp_sku_reserves + lot reserve_qty). Reject releases; accept converts to FIFO sale. Hard-block send if available short.
12kdoneMonthly close / COGS — outs × lot cost for YYYY-MM; snapshot on erp_inventory_closes; closed month blocks new lot moves. Sheets still ops until cutover.
12lbuilt9981 → ERP issue bridge (ERP-side only)ymsb:bridge-9981-sales reads 9981 DB paid orders and issues via channel service. Zero 9981 code changes; never writes 9981. Unmapped lines skipped. Loyverse only as 9981 orders.source when present.
pausedEcommerce 2.0 — not the current track. Resume only after ERP purchasing is usable and BD is quoting from the price book.

8. Inventory target — full warehouse OS (not JIT)

Infinity JIT is only for a reseller shop that holds ≈0 and buys from the master at checkout. Yuzu / 9981 / future Ecommerce 2.0 hold stock. ERP must behave like Infinity master (lot ledger + warehouses + UOM). Every channel (web, POS, B2B, event, marketplace) issues from that book. Do not copy Infinity’s sub/agent procure for Yuzu shops.

8.1 UOM — one stock SKU, many sell packs

Do not create a second inventory SKU when you want to sell bottles from a carton. That doubles stock and needs a pack-break every time someone opens a box.

Infinity model (locked for ERP):

LayerWhat it isExample
Inventory SKUOne stock item, counted in base unitGOD-NIG-001 on-hand = bottles
sku_uomsPack sizes on that SKUBTL = 1, CTN = 6
Sellable (PSKU / offer)How the shop sells it; uom_qty = bottles takenBottle offer → 1; Carton offer → 6

Pack-break (out 1 carton-SKU, in 6 bottle-SKU) only if the carton is a different physical item that must stay sealed (gift set, bonded case). Default: do not.

ERP today: erp_skus.unit defaults to bottle (1:1 with 9981). Target adds pack UOMs + sellable offers on the same erp_sku.

8.2 Feature set shops will migrate onto

Featurev1 nowTarget (covers Infinity + gg_inventory_logs)
Lot ledger + cost on inerp_sku_ins/outs/balances (12d)In lot; out consumes a lot (FIFO); closings later
Locationserp_warehouses + move/PO WH (12c)Warehouse / entity (HQ, expo, consignment) + more kinds as needed
ReceivePO receivePO / supplier invoice, partial GRN
IssueB2B accept (HQ) + channel API by WH (12e/12g)Web, POS, B2B, event, marketplace — all post out
TransferWH → WH (transfer_out + transfer_in)WH → WH with lot awareness
Adjust / write-offadjust + open-bottle presets (12g)Broken, stolen, defect, tasting, internal, CN, consignment return
Reserve / pendingsoft hold on sent quotes (12j)Hold on lot; pending order does not write cache
UOM / packserp_sku_uoms (12b)§8.1 — one stock SKU, many sell packs
Close + COGSmonthly close snapshot (12k)Monthly close; COGS = out × lot cost
Channel APIPOST /api/v1/channel/sales (12g/12h) — ready for callersERP-side job/command reads 9981 and issues (12l). No 9981 patches. Source tag = 9981 order source.

Until cutover: 9981 still owns live B2C stock qty; ERP ledger mirrors via pull bridge. After cutover: ERP is truth; 9981 reads available / stops local deduct (that cutover would touch 9981 — separate phase).

9. Access

SurfaceURL
Business OS (YMSB)ymsb.sme-erp.fteg.dev
9981 storefront9981wine.fteg.dev
9981 BO9981yuzu-bo152310.fteg.dev
YMSB corporateymsbcorporate.fteg.dev
Legacy ymsb-erpymsb-erp-bo.fteg.dev (frozen)

10. Code paths

11. Related docs

Yuzu Platform · Loading version…