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 customers

One customers table, two views: every customer row carries a channel flag (oem / retail / both). This screen shows retail + both; Sell → Customers shows oem + both. Same data underneath — a kedai that grows into an OEM buyer just flips to both and keeps its whole history.
6 customers in the retail view
Name Phone Area Usual products Orders First order Last order Owing Status
OEM customers (Roy KL Trading, Yes Global, Al Insan Sempurna, …) live in Sell → Customers — same customers table underneath, different view scope: that screen filters channel ∈ {oem, both}, this one channel ∈ {retail, both}. Herbs & Health PJ appears in both views.

Project Gaia — static mockup in Filament v3 idiom. No backend. Retail module; customer data shared with OEM underneath (channel-flagged), screens separated.

Retail customers — field manifest

FieldFilament componentTypeSourceFK target
Name / Phone / AreaTextColumnstoredstored: customers — ONE table shared with OEM (Sell → Customers reads the same rows). No retail_customers table existscustomers
Channelmanifest-only — no column or filter on this screenenum oem / retail / bothstored: customers.channel. THIS resource scopes channel ∈ {retail, both}; Sell → Customers scopes channel ∈ {oem, both}. channel=both also flips e-invoicing to per-sale B2B — set at creation (below), just not browsable as a column/filter here since a 5-customer book doesn't need one
OwingTextColumn, derivedmoneysum of retail_sales.total where payment_status = unpaid for the customer; red when past due_date, amber when still within terms — same figure as Retail (Today strip) and Retail → CRMretail_sales
Usual productsTextColumn, derivedderivedmost-frequent variants from this customer's sale-line history (shared engine with the CRM book)retail_sale_lines
Orders / First / Last orderTextColumn, derivedaggregatecount / min(date) / max(date) over retail_sales for the customerretail_sales
StatusBadge, derivedflagActive, or DUE TO REORDER when now > last_order + 1.5× cadence — same derived rule as Retail CRM, no status field stored
+ Customer (slide-over)CreateAction slideOver()formcreates: customers (display_name, phone, area, channel enum oem/retail/both, terms, b2b_einvoice, notes) — same table Sell → Customers writes; creation here defaults channel=retail. No price fields: price memory builds from confirmed sales, floor lives in variant_pricescustomers

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).
  • Promoting a retail customer to OEM = set channel to both. History, price memory and cadence carry over — nothing is copied or re-entered.
  • Walk-in is a pseudo-customer (null FK on the sale): counted in sales, excluded from the book, no price memory kept.

Customer

Not saved

The one required field.

ONE customers table shared with OEM. both = also visible in Sell → Customers and gets a per-sale B2B e-invoice instead of joining the monthly consolidation. A kedai that grows into OEM just flips to both and keeps its history.

Routes e-invoicing per sale via QBO. Auto-on (and locked) when channel = both.

No price fields here — price memory builds itself from confirmed sales; the floor lives in variant_prices (owner-edited in Sell → Price lists).