Models & fields
A MADATA workspace is a set of models. Every
screen of the interface shows one or more of them. Learning to read that map
is learning to write any call: the rest is just search_read and
write.
01Model, field, record
- Model
- A business table, named with dots:
res.partner(contacts),sale.order(sales orders),account.move(accounting documents). - Field
- A column:
name,email,amount_total. The technical name is stable; the displayed label depends on the language. - Record
- A row, identified by an integer
id, unique per model and per workspace.
In the workspace, turn on developer mode from Settings, then hover a field: a tooltip gives the model name and the field name. It is the fastest and safest route.
02The most used models
| Area | Model | Content |
|---|---|---|
| Contacts | res.partner | Customers, vendors, contacts. customer_rank > 0 for customers, supplier_rank > 0 for vendors. |
| Contacts | res.company | The companies of the workspace. Read only. |
| Catalogue | product.template | The product sheet as it is entered. |
| Catalogue | product.product | The variant actually sold and stocked. That is what document lines point at. |
| Sales | sale.order · sale.order.line | Quotations and orders, and their lines. |
| Sales | account.move · account.move.line | Customer invoices, vendor bills, credit notes and journal entries. One model for all of it, told apart by move_type. |
| Purchases | purchase.order · purchase.order.line | Requests for quotation and purchase orders. |
| Inventory | stock.quant | Quantities actually on hand, per location. |
| Inventory | stock.picking · stock.move | Receipts, deliveries, internal transfers. |
| Accounting | account.account · account.journal | Chart of accounts and journals. |
| Accounting | account.payment | Customer and vendor payments. |
| Point of sale | pos.order · pos.session | Tickets and till sessions. |
| Human resources | hr.employee · hr.contract | Employees and contracts. |
| Projects | project.project · project.task | Projects and tasks. |
| CRM | crm.lead | Leads and opportunities. |
The list depends on the applications installed on the workspace: one
without a point of sale has no pos.order. To know what really
exists, ask the workspace itself.
03Discover instead of guessing
Two methods are enough to explore an unknown workspace.
models_found = models.execute_kw(DB, uid, KEY, 'ir.model', 'search_read',
[[['model', 'like', 'sale.']]],
{'fields': ['model', 'name'], 'order': 'model'})
for m in models_found:
print(m['model'], '\t', m['name'])
fields = models.execute_kw(DB, uid, KEY, 'sale.order', 'fields_get', [],
{'attributes': ['string', 'type', 'required', 'readonly', 'relation',
'selection', 'help']})
print(fields['state'])
# {'type': 'selection', 'string': 'Status', 'required': True,
# 'selection': [['draft', 'Quotation'], ['sent', 'Quotation sent'],
# ['sale', 'Sales Order'], ['cancel', 'Cancelled']], ...}
fields_get is the source of truth: it describes the
workspace as installed, with its custom fields and its real
selection values. General documentation cannot do better.
Called with no argument,
fields_get returns the whole model, which is large. Pass
attributes to ask only for what you need, and cache the result
for the duration of your run.
04External identifiers
A numeric id only means something inside one workspace. To
point at a record in a stable way, especially from a third party system,
there are external identifiers: a
module.name pair stored in ir.model.data.
ref = models.execute_kw(DB, uid, KEY, 'ir.model.data', 'search_read',
[[['module', '=', 'my_shop'], ['name', '=', 'customer_4271']]],
{'fields': ['res_id', 'model'], 'limit': 1})
partner_id = ref[0]['res_id'] if ref else None
This is the most reliable way to make an import replayable: instead of recreating a contact on every run, you look up its external identifier and update the existing one. Without it, a retry after an incident doubles your data.
05Multi company
A workspace can hold several companies. Every affected record has a
company_id, and every call runs within the companies that are
active for the user.
invoices = models.execute_kw(DB, uid, KEY, 'account.move', 'search_read',
[[['move_type', '=', 'out_invoice'], ['state', '=', 'posted']]],
{'fields': ['name', 'partner_id', 'amount_total'],
'context': {'allowed_company_ids': [2]}})
Without allowed_company_ids,
the call uses the user default companies. A "wrong" total is almost always a
total taken over a different scope than the one you were looking at. Set it
as soon as the workspace has more than one company: it is one line, and it
saves hours of reconciliation.
06What belongs to the portal
Users, companies, installed applications and the subscription are managed in the MADATA portal, which is their source of truth and bills accordingly. Creating or changing them straight through the API puts the workspace and your subscription out of step. Read them if you need to; write them from the portal.