> ## Documentation Index
> Fetch the complete documentation index at: https://docs.erp.arukz.com/llms.txt
> Use this file to discover all available pages before exploring further.

# The ARUKZ ERP data model

> How ARUKZ ERP stores parties, products, companies and documents: one partner table for every party, header and lines, and company_id on every row.

ARUKZ ERP keeps each fact once and points at it by ID. Every customer, vendor, contact and address is a row in one table, `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

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
erDiagram
    COMPANIES ||--|| PARTNERS : "partner_id (the company's own contact card)"
    COMPANIES ||--o{ COMPANIES : "parent_id (branches)"
    PARTNERS ||--o{ PARTNERS : "parent_id (contacts and addresses)"
    USERS }o--|| PARTNERS : "partner_id"
    EMPLOYEES }o--|| USERS : "user_id (unique)"
    PRODUCTS ||--o{ PRODUCTS : "parent_id (variants)"
    PRODUCTS }o--|| UNIT_OF_MEASURES : "uom_id, uom_po_id"
```

| Table | Holds | Worth knowing |
| - | - | - |
| `partners_partners` | Every party: companies, people and addresses | `account_type` is `company`, `individual` or `address`; `customer_rank` and `supplier_rank` let one partner be both customer and vendor |
| `companies` | Each customer company (the workspace) and its branches | Each company gets a matching partner row so it can appear on documents; the sync runs one way, company to partner |
| `users` | Logins | Separate from employees on purpose: a seasonal operator can be an employee without a login |
| `employees_employees` | HR records | `user_id` links an employee to a login when both exist |
| `products_products` | Products and their variants | Variants point at their template with `parent_id` |

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

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
sales_orders                   partner_id, company_id, currency_id, user_id
  sales_order_lines            order_id, product_id, product_uom_id, price_unit, product_uom_qty
inventories_operations         partner_id, sale or purchase order link
  inventories_moves            product_id, order line link, location_id, location_dest_id
accounts_account_moves         partner_id, journal_id, move_type (out_invoice, in_invoice, ...)
  accounts_account_move_lines  account_id, product_id, order line links
```

Because every line points at its order line and every order at its partner, you can walk from any invoice line back to the order, the delivery and the customer without matching names. Invoices, bills, credit notes and journal entries are all rows in `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_id` yourself in that code.
* A visitor registered on the customer portal (`/customer`), which is not yet aware of companies.

## Known gaps

> [accordion] 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.

> [accordion] 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.

> [accordion] 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.

> [accordion] 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`.

> [accordion] 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_id` on every business table and use the `BelongsToCompany` trait on the model.
* Make codes unique per company: `unique(['company_id', 'code'])`.

[Build a custom plugin](https://docs.erp.arukz.com/developers/custom-plugin.md) shows all three in a worked example.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.