MADATA / DEV developer docs Français madata.africa

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.
Finding the model from the screen

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

AreaModelContent
Contactsres.partnerCustomers, vendors, contacts. customer_rank > 0 for customers, supplier_rank > 0 for vendors.
Contactsres.companyThe companies of the workspace. Read only.
Catalogueproduct.templateThe product sheet as it is entered.
Catalogueproduct.productThe variant actually sold and stocked. That is what document lines point at.
Salessale.order · sale.order.lineQuotations and orders, and their lines.
Salesaccount.move · account.move.lineCustomer invoices, vendor bills, credit notes and journal entries. One model for all of it, told apart by move_type.
Purchasespurchase.order · purchase.order.lineRequests for quotation and purchase orders.
Inventorystock.quantQuantities actually on hand, per location.
Inventorystock.picking · stock.moveReceipts, deliveries, internal transfers.
Accountingaccount.account · account.journalChart of accounts and journals.
Accountingaccount.paymentCustomer and vendor payments.
Point of salepos.order · pos.sessionTickets and till sessions.
Human resourceshr.employee · hr.contractEmployees and contracts.
Projectsproject.project · project.taskProjects and tasks.
CRMcrm.leadLeads 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.

pythonlist the available models
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'])
pythonlist the fields of a model
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.

Keep it at hand

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.

pythonfind a record by external identifier
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.

pythonread within one company
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]}})
The company trap

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.