Product direction: Global Core + Localization
Updated
How the product is split: a business function is global, country compliance is a localization. What each contour does today — process map.
← Business processes · Module overview
The principle#
A business function is global. Country compliance is a localization. The UI comes from metadata. The price is the contract and the rules, with an explanation. Documents are the Form Engine. AI goes through tools over a structured model.
ZuloOne is a multi-country ERP. The country packages in the product are
Saudi Arabia (LocalizationSaudiArabia) and Ukraine
(LocalizationUkraine). A new country is a new model in the Customization
layer. Global Core is not rewritten for that country.
Global Core is what already sits in the Base and Solution layers: sales,
stores, purchasing, tax as an engine, the ledger, pricing, contracts. The
platform (src/ZuloOne.Core) generates tables, API and UI from metadata.
Country application code does not go there.
How it is built#
| The idea | How it is done |
|---|---|
| Global Core | The models Common … Sales / Tax / Accounting / Inventory |
| A country package | LocalizationSaudiArabia (layer 3); the template is 20-Localization |
| The tax engine | The Tax module (codes, rates, rules, the ledger, the return) |
| The ledger | Accounting; each document posts its own legs |
| Prices | IPricingService in Inventory — not a country provider. Live: ResolveSaleAsync, overlays on the contract, PriceExplanation on the line |
| The contract | SalesContract belongs to the outlet (Outlet is mandatory, Customer is stamped from the outlet). Currency, settlement kind, payment term, DeliveryTerm (Incoterms), price type, limit, date window |
| Commands, not free CRUD | A document command changes the subtype and runs the postings |
| Forms | MetaPrintForm + an Zr layout (LayoutJson). Assistant tools: list_print_forms / get_print_form / set_print_form_layout / preview_print_form (Zr JSON, not PDF) |
Extension without if (country) |
Chain of Command, the <Document>.<Model> extension, tax rules as data |
Forbidden in Core / Sales / Tax / Pricing:
if country == "Ukraine" … else if country == "Saudi Arabia" …
A new law is a seed and rules in the country package; a new field on the
invoice is a package extension; HTTPS and the signature are a host connector /
Job, not a workspace script.
Lifecycles#
The survey drew "canonical" chains. On the product the axis is already a different one — do not break it for the textbook.
Sales. Optionally a lead (SalesLead) → a quotation (SalesQuotation in
CRM) → the order (Draft → Submitted → Confirmed) → the realization
(Reserved … Issued) → payment as a separate document. There is no
Opportunity object; lead and quotation do not replace the order axis.
Details — sales and purchasing ·
lead ·
quotation.
Purchasing. PurchaseOrder Draft → Ordered → Received → VendorPayment.
The purchase credit note (PurchaseCreditNote) reverses input VAT.
SupplierInvoice books only the difference from the receipt — process
map §8.
Stores. Receipt / put-away / picking / transfer / stock count / adjustment; the balance by cell. Lots, serial numbers and shelf life are not a stock contour. The barcode on the print form is there.
Ukraine package. Tax regimes on the legal entity, input credit, 15% above
the limit, matching the exemption to the regime change, exporting the return.
Social insurance (ЄСВ) is HR rates. The tax invoice (податкова накладна) goes
through an operator (the M.E.Doc MakeDoc/ToGov adapter — accountant's PC,
M.E.Doc). PrivatBank Autoclient v3 imports
statements (customer-side setup); there
is no outgoing payment from this channel. Diia and the State Tax Service (ДПС)
cabinet are not in the package — Diia.
The package.
Saudi package. TaxDocument plus a mock; TLV QR in the core; parties'
master data (LegalEntity.TaxRegistrationNumber, CRN from the KSA package,
Address.Country/District); atomic ICV (IKeyedCounterService); UBL; XML
hash and QR on the host (1–6 always; 7–9 with a PEM CSID). CSR+OTP+Production
CSID onboarding on the host. The docker stand sends to the stub, not to
gw-fatoora. Six compliance sample kinds (IZatcaComplianceSamples) on the
stub. QR is embedded in the UBL; XAdES-BES when CertificateDer is there
(C14N 1.0). E/O categories with VATEX on TaxCategory.LocalizationSaudiArabia.
A Standard invoice cannot be handed to the buyer before Cleared.
ZATCA.
Mobile. One application (agent / store / manager roles), UI from metadata,
offline-first, commands. The client is the sibling repository zuloone.mobile.
The office Field desk (a widget plus Dispatch) reads the same VisitRoute /
AgentVisit / AgentGpsLog.
ABC/XYZ. Global Core analytics in Sales: profiles, bands, IAbcClassifier,
the AbcClassification log.
User · business ·
developer.
What the product does not do#
- Three mobile applications, or Flutter screens with hardcoded fields.
IPricingProvider/ITaxProvideras a switch on the country.- A chart of accounts that lives only in the localization — the engine and the CoA are in Accounting; the country package seeds the statutory chart and the return's mapping.
- A rewrite of the Tax, Accounting, or Pricing engines — country packages extend them.
- A hardcoded price priority 1..7 — the list of steps is data.