23 KiB
23 KiB
Spécifications du module batch_picking_create_one_bill
Objectif
Créer un module Odoo 18.0 permettant de générer une seule facture fournisseur (vendor bill) consolidée pour tous les bons de commande (Purchase Orders) associés à un batch picking, en utilisant la méthode standard de création de factures d'Odoo basée sur les réceptions.
Fonctionnalités principales
1. Modification du comportement des Batch Pickings
Héritage du modèle stock.picking.batch
Champs ajoutés
# Dans stock_picking_batch.py
class StockPickingBatch(models.Model):
_inherit = 'stock.picking.batch'
zero_quantity_default = fields.Boolean(
string='Quantités à zéro par défaut',
default=True,
help='Initialiser les quantités à zéro lors de la création du batch'
)
invoice_count = fields.Integer(
compute='_compute_invoice_count',
string='Nombre de factures'
)
invoice_ids = fields.Many2many(
'account.move',
string='Factures associées',
compute='_compute_invoice_ids',
store=True
)
Surcharge des méthodes
Initialisation des quantités
def _prepare_move_line_vals(self, **kwargs):
vals = super()._prepare_move_line_vals(**kwargs)
if self.zero_quantity_default:
vals['quantity'] = 0.0
return vals
Calcul des factures associées
@api.depends('picking_ids', 'picking_ids.purchase_id.invoice_ids')
def _compute_invoice_ids(self):
for batch in self:
invoices = batch.picking_ids.mapped('purchase_id.invoice_ids')
batch.invoice_ids = invoices
batch.invoice_count = len(invoices)
Actions et boutons
def action_view_invoices(self):
self.ensure_one()
action = self.env['ir.actions.act_window']._for_xml_id(
'account.action_move_in_invoice_type'
)
if self.invoice_count == 1:
action['views'] = [(False, 'form')]
action['res_id'] = self.invoice_ids.id
else:
action['domain'] = [('id', 'in', self.invoice_ids.ids)]
return action
Modification des vues
Vue formulaire du batch picking
<record id="view_picking_batch_form_inherit" model="ir.ui.view">
<field name="name">stock.picking.batch.form.inherit</field>
<field name="model">stock.picking.batch</field>
<field name="inherit_id" ref="stock.view_picking_batch_form"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<button name="%(action_create_batch_bill_wizard)d"
string="Créer Facture"
type="action"
class="btn-primary"
attrs="{'invisible': [('state', '!=', 'done')]}"/>
</xpath>
<xpath expr="//div[@name='button_box']" position="inside">
<button class="oe_stat_button"
name="action_view_invoices"
type="object"
icon="fa-pencil-square-o">
<field name="invoice_count" widget="statinfo" string="Factures"/>
</button>
</xpath>
<xpath expr="//group[@name='group_misc']" position="inside">
<field name="zero_quantity_default"/>
</xpath>
</field>
</record>
Gestion des mouvements de stock
Modification du comportement des move lines
def _update_move_lines_values(self, moves):
res = super()._update_move_lines_values(moves)
if self.zero_quantity_default:
for move in moves:
for line in move.move_line_ids:
line.qty_done = 0.0
return res
Validation des quantités
def _check_received_quantities(self):
self.ensure_one()
for move in self.move_lines:
if move.quantity_done > move.product_uom_qty:
raise ValidationError(
_('La quantité reçue ne peut pas être supérieure '
'à la quantité commandée pour le produit %s')
% move.product_id.display_name
)
2. Critères de regroupement
- Regroupement uniquement des PO du même fournisseur
- Facturation basée uniquement sur les quantités réellement reçues dans le batch picking
- Les lignes de PO non reçues ne seront pas incluses dans la facture
2. Processus de création de facture
- Un bouton dédié sera ajouté sur le batch picking après sa validation
- Pour chaque PO du batch :
- Utilisation de la méthode standard d'Odoo
action_create_invoicepour créer les factures basées sur les réceptions - Les factures sont créées selon la politique 'Sur réception' (On received quantities)
- Création automatique de factures partielles pour les quantités reçues
- Utilisation de la méthode standard d'Odoo
- Fusion automatique de toutes les factures créées en une seule facture
- Regroupement des lignes par produit
- Conservation des liens avec les PO d'origine
- Suppression des factures individuelles après fusion
3. Gestion des cas particuliers
- Vérification des quantités reçues vs commandées
- Validation que toutes les réceptions sont bien effectuées avant la création de la facture
- Gestion des écarts entre les quantités commandées et reçues
4. Sécurité et droits d'accès
- Restriction aux utilisateurs du groupe 'Invoicing user'
- Vérification des droits d'accès sur les documents liés (PO, réceptions)
5. Interface utilisateur
- Ajout d'un bouton sur la vue form du batch picking
- Rapports spécifiques pour le suivi des factures groupées [À PRÉCISER]
- Messages de confirmation/erreur clairs pour l'utilisateur
6. Aspects techniques
- Pas de règles spécifiques pour la numérotation des factures (utilisation du système standard)
- Intégration avec les modules stock_picking_batch et purchase
- Respect des règles de facturation basées sur les réceptions
Workflow
- Création et traitement normal du batch picking
- Validation du batch picking
- Utilisation du nouveau bouton pour créer la facture groupée
- Pour chaque PO impliqué dans le batch picking :
- Utilisation de la méthode standard
action_create_invoiced'Odoo - Création automatique des factures basées sur les quantités reçues
- Les quantités non reçues restent en attente de facturation
- Utilisation de la méthode standard
- Fusion automatique de toutes les factures créées en une seule facture fournisseur
- Regroupement des lignes par produit
- Maintien des références aux PO d'origine
- Conservation des taxes et comptes analytiques
- Suppression des factures individuelles après fusion réussie
- Traitement normal de la facture fusionnée
Impact sur les processus existants
- Les utilisateurs devront entrer manuellement les quantités reçues au lieu de les ajuster
- Réduction des erreurs de réception dues aux quantités pré-remplies
- Meilleure traçabilité des quantités réellement reçues vs commandées
Détails techniques d'implémentation
1. Modification du comportement des Batch Pickings
- Héritage du modèle
stock.picking.batch - Surcharge de la méthode de création pour initialiser les quantités à 0
- Modification des champs de quantité dans
stock.move.line
2. Gestion des factures
Wizard de création de facture (create_merged_bill.py)
1. Modèles
Modèle principal : batch.picking.create.bill.wizard
- Champs principaux :
batch_id: Many2one vers stock.picking.batch (readonly)- Batch picking source pour la création de la facture
purchase_order_ids: Many2many vers purchase.order (computed, readonly)- Liste des PO associés au batch
- Calculé automatiquement depuis le batch
partner_id: Many2one vers res.partner (computed, readonly)- Fournisseur commun à tous les PO
- Vérification d'unicité du fournisseur
invoice_ids: Many2many vers account.move (computed)- Factures créées par le processus standard d'Odoo
- Utilisé temporairement avant la fusion
merged_invoice_id: Many2one vers account.move- Facture finale fusionnée
preview_available: Boolean (computed)- Indique si la prévisualisation est possible
Modèle temporaire : batch.picking.create.bill.line.preview
- Champs :
wizard_id: Many2one vers le wizard principalproduct_id: Many2one vers product.productdescription: Char (nom du produit + description)quantity: Float (quantité reçue)uom_id: Many2one vers uom.uomprice_unit: Floatprice_subtotal: Floatpurchase_line_ids: Many2many vers purchase.order.linepicking_ids: Many2many vers stock.picking
2. Fonctions principales
Calculs et vérifications
-
_compute_purchase_orders():def _compute_purchase_orders(self): for wizard in self: pickings = wizard.batch_id.picking_ids purchase_orders = pickings.mapped('purchase_id') wizard.purchase_order_ids = purchase_orders -
_compute_partner():def _compute_partner(self): for wizard in self: partners = wizard.purchase_order_ids.mapped('partner_id') if len(partners) != 1: raise ValidationError(_('Tous les PO doivent avoir le même fournisseur')) wizard.partner_id = partners
Préparation des données
-
_prepare_invoice_lines():- Regroupe les lignes par produit
- Calcule les quantités reçues
- Vérifie la cohérence des prix
- Génère les lignes de facture
-
_prepare_preview_lines():- Crée les lignes de prévisualisation
- Affiche les détails des réceptions
- Calcule les sous-totaux
Actions
action_create_bill():- Vérifications préalables
- Création des factures par PO
- Fusion des factures
- Liaison avec le batch picking
- Retour vers la facture créée
3. Interface utilisateur
Vue formulaire principale
<form>
<header>
<button name="action_create_bill"
string="Créer Facture"
type="object"
class="btn-primary"
attrs="{'invisible': [('preview_available', '=', False)]}"/>
<button special="cancel" string="Annuler" class="btn-secondary"/>
</header>
<sheet>
<group>
<group>
<field name="batch_id"/>
<field name="partner_id"/>
<field name="currency_id"/>
</group>
<group>
<field name="amount_total"/>
<field name="preview_available" invisible="1"/>
</group>
</group>
<notebook>
<page string="Ordres d'achat">
<field name="purchase_order_ids"/>
</page>
<page string="Prévisualisation des lignes">
<field name="preview_line_ids">
<tree>
<field name="product_id"/>
<field name="description"/>
<field name="quantity"/>
<field name="uom_id"/>
<field name="price_unit"/>
<field name="price_subtotal"/>
</tree>
</field>
</page>
</notebook>
</sheet>
</form>
4. Messages et Validations
Messages d'erreur
- Fournisseur différent :
_('Les bons de commande sélectionnés ont des fournisseurs différents : %s') - Batch non validé :
_('Le batch picking doit être validé avant de créer la facture') - Quantités invalides :
_('Certaines lignes ont des quantités reçues invalides')
Validations automatiques
- Vérification de l'état du batch
- Contrôle des quantités reçues
- Vérification des devises
- Validation des droits d'accès
3. Modifications de l'interface
Modifications des vues principales
1. Vue formulaire du batch picking (stock_picking_batch_views.xml)
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Héritage de la vue form du batch picking -->
<record id="view_picking_batch_form_inherit" model="ir.ui.view">
<field name="name">stock.picking.batch.form.inherit</field>
<field name="model">stock.picking.batch</field>
<field name="inherit_id" ref="stock.view_picking_batch_form"/>
<field name="arch" type="xml">
<!-- Ajout du bouton de création de facture -->
<xpath expr="//header" position="inside">
<button name="%(action_create_batch_bill_wizard)d"
string="Créer Facture"
type="action"
class="btn-primary"
attrs="{'invisible': [('state', '!=', 'done')]}"/>
</xpath>
<!-- Ajout du smart button pour les factures -->
<div name="button_box" position="inside">
<button class="oe_stat_button"
name="action_view_invoices"
type="object"
icon="fa-pencil-square-o">
<field name="invoice_count" widget="statinfo" string="Factures"/>
</button>
</div>
<!-- Ajout de l'option quantités à zéro -->
<group name="group_misc" position="inside">
<field name="zero_quantity_default"/>
</group>
</field>
</record>
<!-- Vue tree des batch pickings -->
<record id="view_picking_batch_tree_inherit" model="ir.ui.view">
<field name="name">stock.picking.batch.tree.inherit</field>
<field name="model">stock.picking.batch</field>
<field name="inherit_id" ref="stock.view_picking_batch_tree"/>
<field name="arch" type="xml">
<field name="state" position="after">
<field name="invoice_count"/>
</field>
</field>
</record>
<!-- Vue search des batch pickings -->
<record id="view_picking_batch_search_inherit" model="ir.ui.view">
<field name="name">stock.picking.batch.search.inherit</field>
<field name="model">stock.picking.batch</field>
<field name="inherit_id" ref="stock.view_picking_batch_search"/>
<field name="arch" type="xml">
<filter name="to_process" position="after">
<filter string="Non facturé"
name="not_invoiced"
domain="[('invoice_count', '=', 0)]"/>
<filter string="Facturé"
name="invoiced"
domain="[('invoice_count', '>', 0)]"/>
</filter>
</field>
</record>
</odoo>
2. Vue formulaire de la facture (account_move_views.xml)
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_move_form_inherit" model="ir.ui.view">
<field name="name">account.move.form.inherit</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<!-- Ajout des références aux batch pickings -->
<xpath expr="//field[@name='invoice_origin']" position="after">
<field name="batch_picking_ids"
widget="many2many_tags"
readonly="1"
attrs="{'invisible': [('move_type', '!=', 'in_invoice')]}"/>
</xpath>
</field>
</record>
</odoo>
Actions et menus
Actions du wizard
<record id="action_create_batch_bill_wizard" model="ir.actions.act_window">
<field name="name">Créer Facture Fournisseur</field>
<field name="res_model">batch.picking.create.bill.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="model_stock_picking_batch"/>
<field name="binding_view_types">form</field>
</record>
Sécurité et droits d'accès
Groupes et droits
<record id="group_batch_invoice" model="res.groups">
<field name="name">Création de factures depuis batch picking</field>
<field name="category_id" ref="base.module_category_inventory"/>
<field name="implied_ids" eval="[(4, ref('account.group_account_invoice'))]"/>
</record>
Règles d'accès
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_batch_bill_wizard,access.batch.picking.create.bill.wizard,model_batch_picking_create_bill_wizard,group_batch_invoice,1,1,1,0
access_batch_bill_line_preview,access.batch.picking.create.bill.line.preview,model_batch_picking_create_bill_line_preview,group_batch_invoice,1,1,1,1
4. Points techniques clés
Manifest du module
{
'name': 'Batch Picking - Create One Bill',
'version': '18.0.1.0.0',
'category': 'Inventory/Purchase',
'summary': 'Créer une seule facture pour tous les PO d’un batch picking',
'author': 'Pneumac',
'website': 'https://www.pneumac.com',
'license': 'LGPL-3',
'depends': [
'stock_picking_batch',
'purchase',
'account',
],
'data': [
'security/security_groups.xml',
'security/ir.model.access.csv',
'views/stock_picking_batch_views.xml',
'views/account_move_views.xml',
'wizard/create_merged_bill_views.xml',
],
'demo': [],
'installable': True,
'auto_install': False,
'application': False,
}
Structure complète des fichiers
batch_picking_create_one_bill/
├── __init__.py # Import des sous-modules
├── __manifest__.py # Configuration du module
├── models/ # Définition des modèles
│ ├── __init__.py
│ ├── stock_picking_batch.py # Extension du modèle batch
│ └── account_move.py # Extension du modèle facture
├── wizard/ # Assistant de création de facture
│ ├── __init__.py
│ ├── create_merged_bill.py # Logique du wizard
│ └── create_merged_bill_views.xml # Vues du wizard
├── views/ # Vues des modèles
│ ├── stock_picking_batch_views.xml
│ └── account_move_views.xml
├── security/ # Sécurité et droits d'accès
│ ├── ir.model.access.csv # Règles d'accès aux modèles
│ └── security_groups.xml # Définition des groupes
├── static/ # Ressources statiques
│ └── description/
│ └── icon.png # Icône du module
└── i18n/ # Traductions
└── fr.po # Traduction française
Points techniques spécifiques
1. Gestion des hooks
# Dans stock_picking_batch.py
from odoo import models, fields, api, _
class StockPickingBatch(models.Model):
_inherit = 'stock.picking.batch'
@api.model
def create(self, vals):
# Hook de création pour initialiser les quantités à 0
res = super().create(vals)
if res.zero_quantity_default:
res._reset_quantities()
return res
def write(self, vals):
# Hook d'écriture pour gérer les modifications
res = super().write(vals)
if 'zero_quantity_default' in vals:
self._handle_quantity_change()
return res
2. Gestion des contraintes
@api.constrains('picking_ids', 'picking_ids.purchase_id')
def _check_purchase_orders(self):
for batch in self:
purchases = batch.picking_ids.mapped('purchase_id')
partners = purchases.mapped('partner_id')
if len(partners) > 1:
raise ValidationError(_(
'Le batch picking ne peut contenir que des PO '
'du même fournisseur.'
))
3. Performance et optimisation
# Dans create_merged_bill.py
from odoo.tools.profiler import profile
@profile
def _prepare_invoice_lines(self):
# Optimisation avec prefetch
products = self.env['product.product'].browse(
self.purchase_order_ids.mapped('order_line.product_id.id')
).exists()
products.read(['name', 'type', 'invoice_policy'])
# Traitement par lots
moves_by_product = {}
for move in self.batch_id.move_lines:
if move.product_id not in moves_by_product:
moves_by_product[move.product_id] = self.env['stock.move']
moves_by_product[move.product_id] |= move
4. Gestion des erreurs
class BatchBillError(Exception):
""" Erreur spécifique pour la création de facture depuis batch """
pass
def _handle_bill_creation_error(self, error):
if isinstance(error, BatchBillError):
# Erreur métier spécifique
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('Erreur'),
'message': str(error),
'type': 'danger',
}
}
# Autres erreurs
raise error
5. Tests unitaires
# Dans tests/test_batch_bill.py
from odoo.tests import tagged
from odoo.tests.common import TransactionCase
@tagged('post_install', '-at_install')
class TestBatchBill(TransactionCase):
def setUp(self):
super().setUp()
# Préparation des données de test
def test_create_bill_from_batch(self):
# Test de création de facture
def test_zero_quantity_default(self):
# Test de l'initialisation des quantités
def test_merge_bills(self):
# Test de la fusion des factures
- Gestion des états des documents (draft, done, etc.)
- Traçabilité complète via les champs related et stored
- Gestion des droits d'accès via les groupes de sécurité
5. Améliorations suggérées
1. Fonctionnalités supplémentaires
-
Annulation en masse :
- Possibilité d'annuler la facture créée et de revenir à l'état précédent
- Traçabilité des annulations dans l'historique
-
Rapports et analyses :
- Rapport de réconciliation PO/Réceptions/Factures
2. Améliorations techniques
-
Cache et performance :
- Mise en cache des calculs fréquents
- Optimisation des requêtes SQL
- Indexation des champs clés
-
Gestion des erreurs avancée :
- Journal d'erreurs détaillé
- Mécanisme de reprise sur erreur
- Notifications utilisateur améliorées
-
Tests automatisés :
- Tests d'intégration avec scénarios complexes
- Tests de performance
- Tests de régression
3. Améliorations UX
-
Interface utilisateur :
- Vue kanban pour les batch pickings avec statut de facturation
- Filtres et groupements avancés
- Aperçu rapide des informations clés
-
Processus utilisateur :
- Assistant de validation en plusieurs étapes
- Suggestions automatiques basées sur l'historique
- Messages d'aide contextuels
4. Personnalisation
- Configuration avancée :
- Paramètres par entreprise
5. Structure des fichiers
batch_picking_create_one_bill/
├── __init__.py
├── __manifest__.py
├── models/
│ ├── __init__.py
│ ├── stock_picking_batch.py
│ └── account_move.py
├── wizard/
│ ├── __init__.py
│ └── create_merged_bill.py
├── views/
│ ├── stock_picking_batch_views.xml
│ └── account_move_views.xml
├── security/
│ └── ir.model.access.csv
└── data/
└── security_groups.xml
Dépendances
- stock_picking_batch
- purchase
- account
Version
- Odoo 18.0