1306ab7cd06611221aed7fafc29aff95aa7652a1
odoo/api-reference.md
| ... | ... | @@ -149,7 +149,7 @@ Tous les modèles supportent : `search`, `search_read`, `search_count`, `read`, |
| 149 | 149 | |
| 150 | 150 | | Modèle | Description | |
| 151 | 151 | |--------|-------------| |
| 152 | -| `sale.order` | **Devis et commandes clients**. Cycle : `draft` (devis) → `sent` (envoyé) → `sale` (commandé) → `done` (livré) → `cancel`. ⚠️ 7 champs obligatoires pour `create` : `name`, `partner_id`, `partner_invoice_id`, `partner_shipping_id`, `company_id`, `date_order`, `picking_policy`. Stocker la référence externe BilletWeb dans `client_order_ref` | |
|
| 152 | +| `sale.order` | **Devis et commandes clients**. Cycle : `draft` (devis) → `sent` (envoyé) → `sale` (commandé) → `done` (livré) → `cancel`. ⚠️ 7 champs obligatoires pour `create` : `name`, `partner_id`, `partner_invoice_id`, `partner_shipping_id`, `company_id`, `date_order`, `picking_policy`. Utiliser `client_order_ref` pour stocker une référence externe | |
|
| 153 | 153 | | `sale.order.line` | Lignes de commande. Contient le produit (`product_id`), la quantité (`product_uom_qty`), le prix unitaire (`price_unit`), la remise (`discount`), les taxes. Peut être lié à un billet événement (`event_ticket_id`) et des inscriptions (`registration_ids`) | |
| 154 | 154 | | `sale.order.template` | Modèles de devis réutilisables. Prédéfinit un panier de produits/services pour créer des devis en un clic. Contient des lignes via `sale.order.template.line` | |
| 155 | 155 | | `sale.order.template.line` | Lignes des modèles de devis. Mêmes champs que `sale.order.line` mais liées à un template plutôt qu'à une commande réelle | |
| ... | ... | @@ -282,8 +282,8 @@ Tous les modèles supportent : `search`, `search_read`, `search_count`, `read`, |
| 282 | 282 | | Modèle | Description | |
| 283 | 283 | |--------|-------------| |
| 284 | 284 | | `event.event` | **Événement** : nom, dates, lieu, places max, responsable. Contient billets (`event_ticket_ids`) et inscriptions (`registration_ids`). ⚠️ Désactiver `auto_confirm` pour éviter l'envoi d'emails en masse à chaque inscription | |
| 285 | -| `event.event.ticket` | **Type de billet**. Lié à un produit (`product_id`) — la vente du produit génère l'inscription. Définit le prix, le nombre de places disponibles. Essentiel pour le flux BilletWeb → Odoo | |
|
| 286 | -| `event.registration` | **Inscription / Participant**. Nom, email, code-barres (`barcode` pour QR BilletWeb), état (`draft`/`open`/`done`/`cancel`), billet, commande liée. ⚠️ `description` n'existe pas → utiliser `barcode` pour les références externes | |
|
| 285 | +| `event.event.ticket` | **Type de billet**. Lié à un produit (`product_id`) — la vente du produit génère automatiquement l'inscription. Définit le prix et le nombre de places disponibles. Utilisé pour la billetterie et les inscriptions en ligne | |
|
| 286 | +| `event.registration` | **Inscription / Participant**. Nom, email, code-barres (`barcode` pour QR code externe), état (`draft`/`open`/`done`/`cancel`), billet, commande liée. ⚠️ `description` n'existe pas → utiliser `barcode` pour les références externes | |
|
| 287 | 287 | | `event.type` | Catégories d'événements : Conférence, Salon, Formation, Concert... Paramètres par défaut (durée, modèle email) | |
| 288 | 288 | | `event.tag` | Étiquettes événement pour filtrage et recherche : "Annuel", "Scolaire", "Entreprise" | |
| 289 | 289 | | `event.mail` | Emails planifiés liés à un événement (quel template, envoyé quand : X jours avant/après). Utilise `mail.template` | |
| ... | ... | @@ -501,7 +501,7 @@ Tous les modèles supportent : `search`, `search_read`, `search_count`, `read`, |
| 501 | 501 | | `event_id` | m2o | Événement | |
| 502 | 502 | | `name` | char | Nom participant | |
| 503 | 503 | | `email` | char | Email | |
| 504 | -| `barcode` | char | QR/Barcode BilletWeb | |
|
| 504 | +| `barcode` | char | QR/Barcode externe | |
|
| 505 | 505 | | `state` | selection | `draft`/`open`/`done`/`cancel` | |
| 506 | 506 | | `event_ticket_id` | m2o | Type billet | |
| 507 | 507 | | `sale_order_id` | m2o | Commande liée | |
| ... | ... | @@ -511,25 +511,67 @@ Tous les modèles supportent : `search`, `search_read`, `search_count`, `read`, |
| 511 | 511 | |
| 512 | 512 | --- |
| 513 | 513 | |
| 514 | -## 5. Flux BilletWeb → Odoo |
|
| 514 | +## 5. Patterns d'intégration |
|
| 515 | 515 | |
| 516 | -Ordre : `res.partner` → `product.product` → `event.event.ticket` → `sale.order` → `sale.order.line` → `event.registration` → lien `registration_ids`. |
|
| 516 | +### 5.1 Créer une commande avec inscription événement |
|
| 517 | 517 | |
| 518 | -### Anti-doublon |
|
| 519 | 518 | ```python |
| 520 | -existing = odoo_call("event.registration", "search_read", [[]], {"fields": ["barcode"]}) |
|
| 521 | -done = {r["barcode"] for r in existing if r.get("barcode")} |
|
| 522 | -if barcode in done: continue |
|
| 519 | +# Ordre : partenaire → commande → ligne → inscription → lien |
|
| 520 | +pid = odoo_call("res.partner", "create", [{"name": "Client", "email": "x@y.com", "customer_rank": 1}]) |
|
| 521 | +so = odoo_call("sale.order", "create", [{"name": "CMD-001", "partner_id": pid, "partner_invoice_id": pid, "partner_shipping_id": pid, "company_id": 1, "date_order": "2026-01-01 10:00:00", "picking_policy": "direct"}]) |
|
| 522 | +line = odoo_call("sale.order.line", "create", [{"order_id": so, "product_id": prod_id, "product_uom_qty": 1, "price_unit": 11.00}]) |
|
| 523 | +reg = odoo_call("event.registration", "create", [{"event_id": 1, "name": "Client", "email": "x@y.com", "barcode": "1234567890", "state": "open"}]) |
|
| 524 | +odoo_call("sale.order.line", "write", [[line], {"registration_ids": [(6, 0, [reg])]}]) |
|
| 525 | +``` |
|
| 526 | + |
|
| 527 | +### 5.2 Anti-doublon générique |
|
| 528 | + |
|
| 529 | +```python |
|
| 530 | +existing = odoo_call("model.name", "search_read", [[]], {"fields": ["field_key"]}) |
|
| 531 | +done = {r["field_key"] for r in existing if r.get("field_key")} |
|
| 532 | +if key in done: continue |
|
| 523 | 533 | ``` |
| 524 | 534 | |
| 525 | -### Désactiver les emails (6 étapes) |
|
| 535 | +### 5.3 Pagination |
|
| 536 | + |
|
| 537 | +```python |
|
| 538 | +all_records, offset = [], 0 |
|
| 539 | +while True: |
|
| 540 | + batch = odoo_call("model.name", "search_read", |
|
| 541 | + [[["field", "=", value]]], |
|
| 542 | + {"fields": ["id", "name"], "offset": offset, "limit": 100}) |
|
| 543 | + if not batch: break |
|
| 544 | + all_records.extend(batch) |
|
| 545 | + offset += 100 |
|
| 546 | +``` |
|
| 547 | + |
|
| 548 | +### 5.4 Désactiver les emails (avant import massif) |
|
| 549 | + |
|
| 526 | 550 | ```python |
| 527 | -# 1. mail.mail → unlink stuck |
|
| 528 | -# 2. ir.mail_server → active=False |
|
| 529 | -# 3. fetchmail.server → active=False |
|
| 530 | -# 4. mail.template → auto_delete=True |
|
| 531 | -# 5. ir.cron → active=False (jobs mail/email/send) |
|
| 532 | -# 6. event.event → auto_confirm=False |
|
| 551 | +# 1. Vider la file d'attente |
|
| 552 | +for mid in odoo_call("mail.mail", "search", [[["state", "in", ["outgoing", "exception"]]]]): |
|
| 553 | + odoo_call("mail.mail", "unlink", [[mid]]) |
|
| 554 | + |
|
| 555 | +# 2. Désactiver serveur SMTP |
|
| 556 | +for s in odoo_call("ir.mail_server", "search_read", [], {"fields": ["id"]}): |
|
| 557 | + odoo_call("ir.mail_server", "write", [[s["id"]], {"active": False}]) |
|
| 558 | + |
|
| 559 | +# 3. Désactiver serveur entrant |
|
| 560 | +for s in odoo_call("fetchmail.server", "search_read", [], {"fields": ["id"]}): |
|
| 561 | + odoo_call("fetchmail.server", "write", [[s["id"]], {"active": False}]) |
|
| 562 | + |
|
| 563 | +# 4. Désactiver templates |
|
| 564 | +for t in odoo_call("mail.template", "search_read", [], {"fields": ["id"]}): |
|
| 565 | + odoo_call("mail.template", "write", [[t["id"]], {"auto_delete": True}]) |
|
| 566 | + |
|
| 567 | +# 5. Désactiver cron jobs email |
|
| 568 | +for c in odoo_call("ir.cron", "search_read", [], {"fields": ["id", "name"]}): |
|
| 569 | + if any(kw in c["name"].lower() for kw in ["mail", "email", "send", "notification"]): |
|
| 570 | + odoo_call("ir.cron", "write", [[c["id"]], {"active": False}]) |
|
| 571 | + |
|
| 572 | +# 6. Désactiver confirmation auto |
|
| 573 | +for ev in event_ids: |
|
| 574 | + odoo_call("event.event", "write", [[ev], {"auto_confirm": False}]) |
|
| 533 | 575 | ``` |
| 534 | 576 | |
| 535 | 577 | --- |
| ... | ... | @@ -561,7 +603,7 @@ odoo_call("account.tax", "create", [{"name": "8.5% S", "amount": 8.5, "type_tax_ |
| 561 | 603 | | 6 | auto_confirm envoie emails | Désactiver avant import | |
| 562 | 604 | | 7 | `unlink` échoue | `{"active": False}` | |
| 563 | 605 | | 8 | `search_read` limit=80 | `limit=0` pour tout | |
| 564 | -| 9 | BilletWeb 403 | `User-Agent: Mozilla/5.0` | |
|
| 606 | +| 9 | API externe 403 | Vérifier `User-Agent` et les headers HTTP requis par l'API tierce | |
|
| 565 | 607 | | 10 | `sale.order` 7 champs requis | name, partner_id ×3, company_id, date_order, picking_policy | |
| 566 | 608 | |
| 567 | 609 | --- |
| ... | ... | @@ -582,13 +624,33 @@ odoo_call("account.tax", "create", [{"name": "8.5% S", "amount": 8.5, "type_tax_ |
| 582 | 624 | |
| 583 | 625 | ### Créer un client |
| 584 | 626 | ```python |
| 585 | -pid = odoo_call("res.partner", "create", [{"name": "Jean", "email": "j@mail.com", "customer_rank": 1}]) |
|
| 627 | +pid = odoo_call("res.partner", "create", [{"name": "Dupont", "email": "d@mail.com", "customer_rank": 1}]) |
|
| 586 | 628 | ``` |
| 587 | 629 | |
| 588 | -### Créer une commande + ligne + inscription |
|
| 630 | +### Créer un produit |
|
| 589 | 631 | ```python |
| 590 | -so = odoo_call("sale.order", "create", [{"name": "CMD-001", "partner_id": pid, "partner_invoice_id": pid, "partner_shipping_id": pid, "company_id": 1, "date_order": "2026-05-29 10:00:00", "picking_policy": "direct"}]) |
|
| 591 | -line = odoo_call("sale.order.line", "create", [{"order_id": so, "product_id": 13, "product_uom_qty": 1, "price_unit": 11.00}]) |
|
| 592 | -reg = odoo_call("event.registration", "create", [{"event_id": 1, "name": "Jean", "email": "j@mail.com", "barcode": "1234567890", "state": "open"}]) |
|
| 593 | -odoo_call("sale.order.line", "write", [[line], {"registration_ids": [(6, 0, [reg])]}]) |
|
| 632 | +prod_id = odoo_call("product.product", "create", [{"name": "Service Conseil", "type": "service", "list_price": 100.00, "sale_ok": True}]) |
|
| 633 | +``` |
|
| 634 | + |
|
| 635 | +### Créer une commande complète |
|
| 636 | +```python |
|
| 637 | +so = odoo_call("sale.order", "create", [{"name": "CMD-001", "partner_id": pid, "partner_invoice_id": pid, "partner_shipping_id": pid, "company_id": 1, "date_order": "2026-01-01 10:00:00", "picking_policy": "direct", "state": "draft"}]) |
|
| 638 | +line = odoo_call("sale.order.line", "create", [{"order_id": so, "product_id": prod_id, "product_uom_qty": 2, "price_unit": 100.00}]) |
|
| 639 | +odoo_call("sale.order", "write", [[so], {"state": "sale"}]) # confirmer |
|
| 640 | +``` |
|
| 641 | + |
|
| 642 | +### Créer une facture depuis une commande |
|
| 643 | +```python |
|
| 644 | +# Odoo gère automatiquement la création de facture via le bouton "Créer une facture" |
|
| 645 | +# En API, on peut créer manuellement : |
|
| 646 | +invoice_id = odoo_call("account.move", "create", [{"move_type": "out_invoice", "partner_id": pid, "invoice_date": "2026-01-01", "invoice_line_ids": [(0, 0, {"product_id": prod_id, "quantity": 2, "price_unit": 100.00})]}]) |
|
| 647 | +odoo_call("account.move", "write", [[invoice_id], {"state": "posted"}]) |
|
| 648 | +``` |
|
| 649 | + |
|
| 650 | +### Rechercher avec filtres |
|
| 651 | +```python |
|
| 652 | +# Commandes confirmées > 100€ du mois |
|
| 653 | +orders = odoo_call("sale.order", "search_read", |
|
| 654 | + [[["state", "=", "sale"], ["amount_total", ">", 100], ["date_order", ">=", "2026-01-01"]]], |
|
| 655 | + {"fields": ["name", "partner_id", "amount_total"], "order": "date_order desc", "limit": 50}) |
|
| 594 | 656 | ``` |