G Gaia

Switch factory

CYTO-11 — Balakong Jaya CTSB CYTO-29 CTSB CYTOWELL-54 — Taming Jaya PIC/S CTWSB CYTOCNGZ-602 — Guangzhou CTCNGZ

Only factories you are granted appear here (Filament tenant menu).

Leo Lim

leo@navi.my · Owner / Admin

Profile settings Change password

Theme

Sign out
RetailSellR&DMakeBuyStockPlanItemsGMPOpsAdminInsights
← Retail sales / New

New sale

will post as RS-26-042 · staff quick entry — big targets, no menus to hunt through

1 · Customer

Recent:

Best payer, never haggles · usual cadence ~weekly (from CRM book — same customers table, channel-scoped).

2 · Lines

price auto-fills from what this customer last paid

3 · Payment

Sale total MYR

line(s) sold below floor — included in the total above; the owner gets a notification, nothing blocks this sale.

Draft RS-26-042 saved — resume anytime before confirming; nothing is posted until Confirm. View sales list →
On confirm: posts FEFO retail_sale stock movements (FG lots spent, batch trace intact) · updates this customer's price memory · joins the July consolidated e-invoice at month end (B2B-flagged customers export individually instead).

Project Gaia — static mockup in Filament v3 idiom. No backend. Retail module; customer data shared with OEM underneath (channel-flagged), screens separated. Quick-sale form is the "monkey-proof" flow: memory fills the price, the floor does the arguing.

New retail sale — field manifest

FieldFilament componentTypeSourceFK target
PageRetailSaleResource CreateRecord (custom, big-target layout)formcreates: retail_sales + retail_sale_lines; next number from the RS- series (separate from OEM SO series)
CustomerSelect + recent-chips shortcutFK nullablecustomers scoped to channel ∈ {retail, both}; chips = this user's most recent sales; Walk-in ⇒ customer_id null (still consolidates, no price memory)customers
+ New customer (slide-over)CreateAction slideOver(), inline from the Select — trimmed to till-speed fieldsformcreates: customers (display_name, phone, area nullable) — same table Sell → Customers writes; channel is hard-set to retail here (not a field, hidden) and terms/b2b_einvoice/notes stay at column defaults; on create the new customer is selected for this sale (create-inline-then-sell). Promoting a kedai to B2B (channel=both, b2b_einvoice) happens later in Retail → Customers, not at the tillcustomers
LinesRepeater (product, unit, qty, price)rowsretail_sale_lines (variant_id, qty_base, unit_price); staff enters qty in ctn/box, the UI converts to base pcs live ("= N pcs/boxes" hint) and only the base qty is stored — no separate UoM column on the line; stock hint = live FG on-hand at current factoryvariants / batches
Pack conversionhard-coded per item, not user-entereditem dataSTC30: 1 ctn = 24 boxes (confirm pack size), 1 box = 15 sachets — sold by box or ctn only, sachets never sold loose. Double Root Coffee: 1 ctn = 30 pcs — sold by ctn or pcs. Same hard-coded UoM rule set used across Buy/Make/Sell; retail entry just reuses ititems.pack_uom_factor
Price autofillTextInput, afterStateUpdated querymoney, derived defaultquery: last price this customer paid for this variant (OEM SO lines + retail sale lines feed ONE memory); sub-line shows last paid · band · floor livesale history / variant_prices
Floor flagValidation + policy (soft, retail-only)ruleprice < variant_prices.floor_price ⇒ line flags red and writes a notification row for the owner; the sale still posts and Confirm stays enabled. Retail is deliberately soft — kedai haggling is normal and staff need to close the sale. OEM keeps the hard block + approval_requests queue; that table is OEM-only and is never written from this screenvariant_prices / notifications
PaymentToggleButtons (large)enum cash / transfer / unpaidretail_sales.payment_status; Unpaid sets due_date = today + customer terms and feeds the follow-up list
ConfirmCreateAction — always enabledactionposts FEFO retail_sale stock movements (qty_base spent, batch trace intact); updates price memory; below-floor lines additionally write a notification for the owner; sale joins consolidation_runs for the month (unless customer B2B-flagged → individual QBO export)stock_movements / consolidation_runs / notifications
Print receipt (success panel)Action, opens print viewstubsame print-preview pattern as print-packing-slip.html's retail variant (RS-26-038 example) — not wired in this mockup

Engine notes

  • Shared-data doctrine: ONE customers table (channel enum oem / retail / both) and ONE variant_prices table — data shared, views scoped. What separates Retail from Sell: screens, numbering series (RS- vs SO-), CRM shape (book vs pipeline), e-invoicing path (consolidated vs per-order).
  • Price memory autofill is the speed trick: staff confirm what the customer paid last time instead of asking the boss; the band sub-line gives negotiation room at a glance.
  • Floor is soft in Retail: a below-floor line flags red and tells staff the boss will be notified, but Confirm stays enabled — the till never stalls waiting on an approval. OEM keeps the hard block via approval_requests; that table isn't touched from this screen.
  • UoM: staff always enter ctn/box, never raw pcs — the pack conversion is fixed per item (not editable at the till) and the live "= N pcs/boxes" hint converts before anything is stored. FEFO lot picking is still engine-side at confirm/ship time; this screen no longer previews lots per line.
  • Save draft just parks the form for later — Confirm posts nothing until tapped.

Customer

Not saved

The one required field.

Helps sort the delivery run — not required.

That's it — this is a till-speed form. Channel defaults to retail (hidden here, not a field); no terms, e-invoice toggle, or notes at point of sale. No price fields either — price memory builds itself from confirmed sales, and the floor lives in variant_prices (owner-edited in Sell → Price lists). Promoting this kedai to B2B (channel=both, per-sale e-invoice) happens later in Retail → Customers, not at the till.