# 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
```python
# 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
```python
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
```python
@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
```python
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
```xml
stock.picking.batch.form.inheritstock.picking.batch
```
#### Gestion des mouvements de stock
##### Modification du comportement des move lines
```python
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
```python
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 :
1. Utilisation de la méthode standard d'Odoo `action_create_invoice` pour créer les factures basées sur les réceptions
2. Les factures sont créées selon la politique 'Sur réception' (On received quantities)
3. Création automatique de factures partielles pour les quantités reçues
- Fusion automatique de toutes les factures créées en une seule facture
1. Regroupement des lignes par produit
2. Conservation des liens avec les PO d'origine
3. 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
1. Création et traitement normal du batch picking
2. Validation du batch picking
3. Utilisation du nouveau bouton pour créer la facture groupée
4. Pour chaque PO impliqué dans le batch picking :
- Utilisation de la méthode standard `action_create_invoice` d'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
5. 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
6. 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 principal
- `product_id`: Many2one vers product.product
- `description`: Char (nom du produit + description)
- `quantity`: Float (quantité reçue)
- `uom_id`: Many2one vers uom.uom
- `price_unit`: Float
- `price_subtotal`: Float
- `purchase_line_ids`: Many2many vers purchase.order.line
- `picking_ids`: Many2many vers stock.picking
##### 2. Fonctions principales
###### Calculs et vérifications
- `_compute_purchase_orders()`:
```python
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()`:
```python
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()`:
1. Vérifications préalables
2. Création des factures par PO
3. Fusion des factures
4. Liaison avec le batch picking
5. Retour vers la facture créée
##### 3. Interface utilisateur
###### Vue formulaire principale
```xml
```
##### 4. Messages et Validations
###### Messages d'erreur
- Fournisseur différent :
```python
_('Les bons de commande sélectionnés ont des fournisseurs différents : %s')
```
- Batch non validé :
```python
_('Le batch picking doit être validé avant de créer la facture')
```
- Quantités invalides :
```python
_('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
stock.picking.batch.form.inheritstock.picking.batch
stock.picking.batch.tree.inheritstock.picking.batchstock.picking.batch.search.inheritstock.picking.batch
```
##### 2. Vue formulaire de la facture (`account_move_views.xml`)
```xml
account.move.form.inheritaccount.move
```
#### Actions et menus
##### Actions du wizard
```xml
Créer Facture Fournisseurbatch.picking.create.bill.wizardformnewform
```
#### Sécurité et droits d'accès
##### Groupes et droits
```xml
Création de factures depuis batch picking
```
##### Règles d'accès
```csv
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
```python
{
'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
```python
# 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
```python
@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
```python
# 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
```python
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
```python
# 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