MADATA / DEV documentation développeur English madata.africa

Écrire des données

Écrire tient en trois méthodes. Toute la difficulté est ailleurs : dans les champs relationnels, dans les actions métier qui font avancer un document, et dans le fait de ne pas créer deux fois la même chose.

01create, write, unlink

pythonles trois methodes
# creer : renvoie l'id cree
partner_id = models.execute_kw(DB, uid, CLE, 'res.partner', 'create', [{
    'name': 'Ets Kouassi & Freres',
    'email': '[email protected]',
    'phone': '+225 07 00 00 00 00',
    'city': 'Abidjan',
    'customer_rank': 1,
}])

# modifier : renvoie True, et accepte une liste d'ids
models.execute_kw(DB, uid, CLE, 'res.partner', 'write',
    [[partner_id], {'city': 'Bouake'}])

# supprimer
models.execute_kw(DB, uid, CLE, 'res.partner', 'unlink', [[partner_id]])
Créer par lot

create accepte une liste de dictionnaires et renvoie la liste des id créés, dans l’ordre. Cent contacts en un appel au lieu de cent appels : c’est le même code, en dix fois plus rapide.

Supprimer échoue souvent, et c’est normal

Un enregistrement référencé ailleurs refuse d’être supprimé : une facture comptabilisée, un article déjà vendu. L’espace protège la cohérence de vos comptes. Le bon geste est l’archivage : write(ids, {'active': False}), qui masque sans rompre l’historique.

02Champs relationnels

Un many2one s’écrit avec un id. Les one2many et many2many, eux, s’écrivent par commandes : des triplets qui disent quoi faire de la liste.

CommandeEffet
(0, 0, {valeurs})Créer une ligne et la rattacher. La forme la plus fréquente.
(1, id, {valeurs})Modifier une ligne existante.
(2, id, 0)Détacher la ligne et la supprimer.
(3, id, 0)Détacher sans supprimer.
(4, id, 0)Rattacher un enregistrement existant.
(5, 0, 0)Tout détacher.
(6, 0, [ids])Remplacer la liste entière par ces identifiants.
pythonun devis et ses lignes, en un seul create
order_id = models.execute_kw(DB, uid, CLE, 'sale.order', 'create', [{
    'partner_id': partner_id,
    'order_line': [
        (0, 0, {'product_id': 512, 'product_uom_qty': 3, 'price_unit': 15000}),
        (0, 0, {'product_id': 731, 'product_uom_qty': 1}),
    ],
}])

Une ligne créée sans price_unit prend le prix de la liste de prix applicable au client. C’est presque toujours ce que vous voulez : ne forcez le prix que lorsque vous en avez une raison.

pythonremplacer les etiquettes d’un contact
models.execute_kw(DB, uid, CLE, 'res.partner', 'write',
    [[partner_id], {'category_id': [(6, 0, [3, 7])]}])

03Valeurs par défaut

default_get donne les valeurs que l’interface proposerait à la création. Utile quand un modèle a des champs obligatoires dont vous ignorez la valeur attendue.

pythonce que l’ecran aurait pre-rempli
defauts = models.execute_kw(DB, uid, CLE, 'sale.order', 'default_get',
    [['company_id', 'currency_id', 'pricelist_id', 'warehouse_id']])
L’API ne rejoue pas les écrans

Les recalculs déclenchés par une saisie à l’écran ne s’appliquent pas automatiquement à un create par API. Renseignez explicitement ce dont vous avez besoin, ou relisez l’enregistrement après création pour constater ce que l’espace a calculé de lui-même.

04Les actions métier

Un document n’avance pas en écrivant son state. Chaque bouton de l’interface correspond à une méthode, qui fait tout ce qu’il faut : numérotation, écritures comptables, mouvements de stock, contrôles. Appelez la méthode, jamais le champ.

GesteModèleMéthode
Confirmer un devissale.orderaction_confirm
Facturer une commandesale.order_create_invoices
Comptabiliser une factureaccount.moveaction_post
Annuler une pièce comptableaccount.movebutton_draft puis button_cancel
Confirmer une commande d’achatpurchase.orderbutton_confirm
Valider un transfert de stockstock.pickingbutton_validate
Ne touchez jamais à state

write(id, {'state': 'posted'}) semble marcher : le document change d’état à l’écran. Mais aucune écriture n’a été générée, aucun numéro attribué, aucun stock bougé. Vous obtenez une facture comptabilisée qui n’existe pas en comptabilité, et le déséquilibre ne se voit qu’au bilan.

05Un parcours complet

Du devis au paiement encaissé, en cinq appels.

pythondevis, commande, facture, paiement
# 1. le devis
order_id = models.execute_kw(DB, uid, CLE, 'sale.order', 'create', [{
    'partner_id': partner_id,
    'order_line': [(0, 0, {'product_id': 512, 'product_uom_qty': 3})],
}])

# 2. confirmation : le devis devient bon de commande
models.execute_kw(DB, uid, CLE, 'sale.order', 'action_confirm', [[order_id]])

# 3. facturation
models.execute_kw(DB, uid, CLE, 'sale.order', '_create_invoices', [[order_id]])

[commande] = models.execute_kw(DB, uid, CLE, 'sale.order', 'read',
    [[order_id]], {'fields': ['invoice_ids']})
facture_id = commande['invoice_ids'][0]

# 4. comptabilisation : numerotation + ecritures
models.execute_kw(DB, uid, CLE, 'account.move', 'action_post', [[facture_id]])

# 5. reglement, par l'assistant d'enregistrement de paiement
wizard_id = models.execute_kw(DB, uid, CLE, 'account.payment.register', 'create',
    [{'payment_date': '2026-09-17', 'journal_id': 7}],
    {'context': {'active_model': 'account.move', 'active_ids': [facture_id]}})

models.execute_kw(DB, uid, CLE, 'account.payment.register',
    'action_create_payments', [[wizard_id]],
    {'context': {'active_model': 'account.move', 'active_ids': [facture_id]}})
Les assistants s’appellent comme des modèles

Une fenêtre qui s’ouvre par-dessus un écran est un modèle comme un autre : on le crée avec ses valeurs, puis on appelle sa méthode de validation. Le context lui dit sur quoi travailler : active_model et active_ids. C’est le même mécanisme pour la facturation groupée, les avoirs ou les relances.

06Ne pas créer deux fois

Un programme qui tourne toutes les heures finit par retomber sur une ligne déjà traitée : réseau coupé, relance manuelle, redémarrage. Sans précaution, chaque reprise duplique.

pythonchercher avant de creer
ref = f"BOUTIQUE-{commande_externe['id']}"

existant = models.execute_kw(DB, uid, CLE, 'sale.order', 'search',
    [[['client_order_ref', '=', ref]]], {'limit': 1})

if existant:
    order_id = existant[0]
else:
    order_id = models.execute_kw(DB, uid, CLE, 'sale.order', 'create',
        [{'partner_id': partner_id, 'client_order_ref': ref,
          'order_line': lignes}])

Portez la référence du système d’origine dans un champ que vous pouvez chercher : client_order_ref sur une commande, ref sur un contact ou une pièce comptable. Ou passez par un identifiant externe, qui sert exactement à cela.

07Ce que l’API ne fera pas pour vous

  • Pas de transaction sur plusieurs appels. Chaque appel est validé seul. Une suite d’appels interrompue laisse un état intermédiaire : prévoyez la reprise plutôt que d’espérer l’atomicité.
  • Pas d’écriture sur les ressources facturées. Utilisateurs, sociétés, applications et abonnement se gèrent dans le portail.
  • Pas de contournement des droits. Un utilisateur sans droit d’écriture sur un modèle ne le forcera pas par l’API.