Modules / Business partner
Customers & suppliers (shared)
One record per company or person you do business with, shared by every other module — so sales, invoicing and purchasing all mean the same customer.
People ask for it as: “one customer list”, “our customers and suppliers”, “the same client in sales and accounting”
Who uses it
| Role | Does | Sees |
|---|---|---|
| Whoever keeps the customer list clean | merges duplicates, fixes names, links subsidiaries to parents | every partner |
| Other modules (sales, invoicing, purchasing) | create-or-reuse a partner, read partners and families | every partner |
| The system | derives ids and enforces the sole-writer rule on every write | every partner, through its own service account |
What it has to do
Reference user stories. Each has a test written into it — the given / when / then — which becomes an automated check once it is built. Expect to keep some, change some and drop some.
Things people do
BP-01As the person who keeps the customer list, I want one record per real company however many modules use it, so that reports agree.
Given sales and invoicing both refer to Acme · when I open Acme · then I see one partner with two roles (customer, and supplier if we also buy from them)
Whoever keeps the customer list clean · reference: implemented
BP-03As the data steward, I want to link subsidiaries to their parent, so that group-level reporting is possible.
Given Acme UK and Acme DE · when I link both to Acme Group as subsidiary_of · then the family walk returns both; a framework-agreement link does not leak into the ownership tree
Whoever keeps the customer list clean · reference: implemented (typed edges)
Things that happen on their own
BP-02whenever any module creates a partnerThe system derives the partner id from a stable business key, so that importing the same company twice creates it once.
Given a partner with key acme.com · when it is created twice, by two modules · then one row, the same id both times
· reference: implemented (bp_id = blake2b(bp_key), 56-bit)
Things that cross into other modules
BP-04when sales converts a new companySales promotes a company into the shared list instead of keeping its own.
Given a CRM organization row · when promote is called · then a partner exists (created or reused) with the customer role
· owned by business-partner · reference: implemented (POST /partners/promote)
What it keeps
| Thing | Meaning | Identified by | Shared? |
|---|---|---|---|
| business partner | a company or person you do business with | bp_id derived from bp_key (a domain, a legacy customer number, a VAT id) | SHARED — this module is the sole writer |
| bp role | what the partner is to us — customer, supplier, carrier | bp_idrole | SHARED — sole writer |
| bp edge | a typed relationship between partners (subsidiary_of, framework_governs) | srcdstrel | SHARED — sole writer |
DataK3 accepts foreign keys but does not enforce them. "Only this module writes partners" IS the integrity mechanism — other modules call its routes, never its tables.
Questions that shape your version
Read these out and let the customer correct one — people rarely recall a policy on demand and can always react to one. Standard is a named authority you can look up and hold us to. Commonly seen is exactly that: arrangements we have met, with no survey behind them. A customer doing none of them is not doing it wrong.
- What identifies a customer for you — a website, a customer number, a tax id?
- b2b services
- a customer number they already use in their old system
- vat registered trade
- the tax id, because it is what the invoice must carry anyway
- warning
- a web domain looks stable and is not — it moves on a rename or acquisition
- Do you buy from any of the companies you sell to?
- Do you deal with groups of companies?
What you can change yourself
These are settings, not code: someone in your team changes them on a screen, without a developer and without a release.
- the roles a partner can have to you — customer, supplier, carrier, and any of your own
For builders
- Module spec — this page, for agents
- The lifecycle and the design template
- erp-business-partner — partners, roles, typed family edges; FastAPI
Known gaps in the reference
- no merge-duplicates route
- no screen — API only
- invoicing's column named bp_id holds what this module calls a bp_KEY, not a bp_id, so the cross-module join needs a CAST. This module is right; the naming downstream is not — design/ar-bp-id-change-request.md
- promote reads CRM's organizations row when there is one; the module installs first, so send legal_name in the body on a bucket with no CRM
- no tests of its own: what is verified runs through the four-module suite in code/crm-suite-app/v1/tests/test_suite.py