partners_partners; every product and variant is a row in products_products; and every business document is a header with lines that point at those rows. Each business row also carries company_id, which is how one database keeps every company’s data apart.
The master data
A sales order to a customer with a separate delivery dock points at three partner rows: the buyer (
partner_id), who is billed (partner_invoice_id) and where it goes (partner_shipping_id).
Documents: header and lines
accounts_account_moves, told apart by move_type.
How modules link up
- Sales to stock: confirming an order creates a delivery and its stock moves, each pointing back at the order line.
- Sales to accounts: creating an invoice writes an account move linked to the order.
- Purchases: the same pattern, for receipts and vendor bills.
- Manufacturing to stock: raw-material and finished-goods moves point at the manufacturing order.
- Projects to timesheets: time is stored as analytic records with the project and task.
Company separation and shared rows
CompanyScope shows a row when its company_id is one of the user’s companies, or when it is empty. An empty company_id means shared by everyone, which is meant for reference data such as countries, currencies and units of measure. Partners and products belong to the company that creates them.
Two ways a row can still end up shared by mistake:
- It was created outside a signed-in web request (a queue job, an artisan command, a seeder), where there is no current company. Set
company_idyourself in that code. - A visitor registered on the customer portal (
/customer), which is not yet aware of companies.
Known gaps
No duplicate detection
No duplicate detection
Two partner rows for the same real company (“Zara Garments” and “ZARA GARMENTS PVT LTD”) are not caught or merged. GSTIN, email and phone are indexed but not unique, and product barcodes are not unique either. Search by GSTIN and phone before adding a customer.
Text where an ID belongs, in some ARUKZ tables
Text where an ID belongs, in some ARUKZ tables
Several ARUKZ columns store a name instead of a link, for example the auditor on a compliance audit, the vendor on a vendor scorecard, the job worker on a subcontract challan and the vendor on a fleet log. They work, but reports cannot join on them. New code stores the ID.
Parallel lists
Parallel lists
CRM leads and CRM accounts keep their own copy of a customer’s name, email and phone instead of using partners, and there is no step that turns a lead into a customer. Fixed assets overlap maintenance equipment, and procurement RFQs overlap purchase orders.
Money without a currency in ARUKZ tables
Money without a currency in ARUKZ tables
ARUKZ amounts are
decimal(15,2) with no currency column; the core tables use decimal(15,4) with currency_id.Some codes are unique across all companies
Some codes are unique across all companies
A few ARUKZ codes (asset codes, ticket numbers, document numbers and others) are unique across the whole platform rather than per company. New code takes numbers from
SequenceService::next('<key>', $companyId) and makes them unique per company.Rules for new tables
- Store the ID (
foreignId(...)->constrained()), never a copied name; read the name through the relationship when you print. - Put
company_idon every business table and use theBelongsToCompanytrait on the model. - Make codes unique per company:
unique(['company_id', 'code']).