batch_picking_create_one_bill upgrades
- Default to zero is now functional
- Checkbox ("Picked" field) visible on move lines in the batch view
- Checking the Picked field marks the line green
- More tests written to validate functionality
This commit is contained in:
parent
cc66512df2
commit
b43f0f5c85
12 changed files with 180 additions and 53 deletions
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import models
|
||||
from . import wizards
|
||||
from . import wizard
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': 'Batch Picking - Create One Bill',
|
||||
'version': '18.0.1.0.0',
|
||||
'category': 'Inventory/Purchase',
|
||||
'summary': 'Créer une seule facture fournisseur pour tous les bons de commande d\'un batch picking',
|
||||
'description': """
|
||||
"name": "Batch Picking - Create One Bill",
|
||||
"version": "18.0.1.0.0",
|
||||
"category": "Inventory/Purchase",
|
||||
"summary": "Créer une seule facture fournisseur pour tous les bons de commande d'un batch picking",
|
||||
"description": """
|
||||
Ce module permet de générer une seule facture fournisseur pour tous les bons de commande
|
||||
associés à un batch picking.
|
||||
|
||||
|
|
@ -14,23 +14,24 @@
|
|||
- Validation que tous les bons de commande proviennent du même fournisseur
|
||||
- Suivi des factures créées directement depuis le batch
|
||||
""",
|
||||
'author': 'Pneumac',
|
||||
'website': 'https://www.pneumac.ca',
|
||||
'depends': [
|
||||
'stock_picking_batch',
|
||||
'purchase',
|
||||
'purchase_stock',
|
||||
'account',
|
||||
'account_reports',
|
||||
"author": "Pneumac",
|
||||
"website": "https://www.pneumac.ca",
|
||||
"depends": [
|
||||
"stock_picking_batch",
|
||||
"purchase",
|
||||
"purchase_stock",
|
||||
"account",
|
||||
"account_reports",
|
||||
],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'wizards/create_bill_wizard_views.xml',
|
||||
'wizards/merge_bill_wizard_views.xml',
|
||||
'views/stock_picking_batch_views.xml',
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"wizard/create_bill_wizard_views.xml",
|
||||
"wizard/merge_bill_wizard_views.xml",
|
||||
"wizard/stock_picking_to_batch_views.xml",
|
||||
"views/stock_picking_batch_views.xml",
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': False,
|
||||
'license': 'LGPL-3',
|
||||
"installable": True,
|
||||
"application": False,
|
||||
"auto_install": False,
|
||||
"license": "LGPL-3",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,61 +2,81 @@
|
|||
from odoo import models, fields, api
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class StockPickingBatch(models.Model):
|
||||
_inherit = 'stock.picking.batch'
|
||||
_inherit = "stock.picking.batch"
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
# Handle zero_quantity_default from context if not explicitly set in vals
|
||||
for vals in vals_list:
|
||||
if (
|
||||
"zero_quantity_default" not in vals
|
||||
and self.env.context.get("default_zero_quantity_default") is not None
|
||||
):
|
||||
vals["zero_quantity_default"] = self.env.context.get(
|
||||
"default_zero_quantity_default"
|
||||
)
|
||||
return super().create(vals_list)
|
||||
|
||||
zero_quantity_default = fields.Boolean(
|
||||
string='Quantités à zéro par défaut',
|
||||
string="Quantités à zéro par défaut",
|
||||
default=True,
|
||||
help='Initialiser les quantités à zéro lors de la création du batch'
|
||||
help="Initialiser les quantités à zéro lors de la création du batch",
|
||||
)
|
||||
invoice_ids = fields.Many2many(
|
||||
'account.move',
|
||||
string='Factures associées',
|
||||
compute='_compute_invoice_ids',
|
||||
store=True
|
||||
"account.move",
|
||||
string="Factures associées",
|
||||
compute="_compute_invoice_ids",
|
||||
store=True,
|
||||
)
|
||||
invoice_count = fields.Integer(
|
||||
string='Nombre de factures',
|
||||
compute='_compute_invoice_count',
|
||||
string="Nombre de factures",
|
||||
compute="_compute_invoice_count",
|
||||
store=True,
|
||||
compute_sudo=True
|
||||
compute_sudo=True,
|
||||
)
|
||||
|
||||
def _prepare_move_line_vals(self, **kwargs):
|
||||
vals = super()._prepare_move_line_vals(**kwargs)
|
||||
if self.zero_quantity_default:
|
||||
vals['quantity'] = 0.0
|
||||
vals["quantity"] = 0.0
|
||||
return vals
|
||||
|
||||
@api.depends('picking_ids.move_ids.purchase_line_id.order_id.invoice_ids')
|
||||
@api.depends("picking_ids.move_ids.purchase_line_id.order_id.invoice_ids")
|
||||
def _compute_invoice_ids(self):
|
||||
for batch in self:
|
||||
purchase_orders = batch.picking_ids.move_ids.purchase_line_id.order_id
|
||||
batch.invoice_ids = purchase_orders.invoice_ids
|
||||
|
||||
@api.depends('invoice_ids')
|
||||
@api.depends("invoice_ids")
|
||||
def _compute_invoice_count(self):
|
||||
for batch in self:
|
||||
batch.invoice_count = len(batch.invoice_ids)
|
||||
|
||||
def action_view_invoices(self):
|
||||
self.ensure_one()
|
||||
action = self.env['ir.actions.act_window']._for_xml_id(
|
||||
'account.action_move_in_invoice_type'
|
||||
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
|
||||
action["views"] = [(False, "form")]
|
||||
action["res_id"] = self.invoice_ids.id
|
||||
else:
|
||||
action['domain'] = [('id', 'in', self.invoice_ids.ids)]
|
||||
action["domain"] = [("id", "in", self.invoice_ids.ids)]
|
||||
return action
|
||||
|
||||
@api.constrains('picking_ids')
|
||||
@api.constrains("picking_ids")
|
||||
def _check_purchase_orders(self):
|
||||
for batch in self:
|
||||
partners = batch.picking_ids.purchase_id.partner_id
|
||||
if len(partners) > 1:
|
||||
raise ValidationError(
|
||||
'Tous les bons de commande du batch doivent provenir du même fournisseur.'
|
||||
"Tous les bons de commande du batch doivent provenir du même fournisseur."
|
||||
)
|
||||
|
||||
def action_confirm(self):
|
||||
"""Override to set zero quantity on move lines at confirmation of the batch"""
|
||||
res = super().action_confirm()
|
||||
self.filtered("zero_quantity_default").move_line_ids.write({"quantity": 0})
|
||||
return res
|
||||
|
|
|
|||
|
|
@ -251,3 +251,68 @@ class TestBatchPickingBill(TransactionCase):
|
|||
],
|
||||
}
|
||||
)
|
||||
|
||||
def test_zero_quantity_default_from_wizard(self):
|
||||
"""Test that zero_quantity_default is properly set when creating a batch from wizard"""
|
||||
# Switch to warehouse user like in the parent test
|
||||
self.env = self.env(user=self.warehouse_user)
|
||||
|
||||
pickings = (self.po1 + self.po2).picking_ids
|
||||
self.assertTrue(pickings, "Purchase orders should have created pickings")
|
||||
|
||||
# Create batch through wizard
|
||||
wizard = (
|
||||
self.env["stock.picking.to.batch"]
|
||||
.with_context(active_ids=pickings.ids, active_model="stock.picking")
|
||||
.create(
|
||||
{
|
||||
"mode": "new",
|
||||
"zero_quantity_default": True,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
result = wizard.attach_pickings()
|
||||
batch = self.env["stock.picking.batch"].browse(result["res_id"])
|
||||
|
||||
# Check that zero_quantity_default was properly set on the batch
|
||||
self.assertTrue(
|
||||
batch.zero_quantity_default,
|
||||
"zero_quantity_default should be True on the created batch",
|
||||
)
|
||||
|
||||
# Check that all move lines have quantity 0
|
||||
for move_line in batch.move_line_ids:
|
||||
self.assertEqual(
|
||||
move_line.quantity,
|
||||
0.0,
|
||||
f"Move line for {move_line.product_id.name} should have quantity 0",
|
||||
)
|
||||
|
||||
def test_zero_quantity_default_from_picking(self):
|
||||
"""Test that zero_quantity_default works when adding picking to existing batch"""
|
||||
# Switch to warehouse user like in the parent test
|
||||
self.env = self.env(user=self.warehouse_user)
|
||||
|
||||
pickings = (self.po1 + self.po2).picking_ids
|
||||
|
||||
# Create batch directly
|
||||
batch = self.env["stock.picking.batch"].create(
|
||||
{
|
||||
"zero_quantity_default": True,
|
||||
}
|
||||
)
|
||||
|
||||
# Add pickings to batch
|
||||
pickings.write({"batch_id": batch.id})
|
||||
|
||||
# Confirm the batch to get the move lines set up
|
||||
batch.action_confirm()
|
||||
|
||||
# Check that all move lines have quantity 0
|
||||
for move_line in batch.move_line_ids:
|
||||
self.assertEqual(
|
||||
move_line.quantity,
|
||||
0.0,
|
||||
f"Move line for {move_line.product_id.name} should have quantity 0",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,18 +7,10 @@
|
|||
<field name="arch" type="xml">
|
||||
<xpath expr="//header" position="inside">
|
||||
<field name="id" invisible="1"/>
|
||||
<button name="%(batch_picking_create_one_bill.action_create_batch_bill_wizard)d"
|
||||
string="Créer Facture"
|
||||
type="action"
|
||||
class="btn-primary"
|
||||
context="{'default_batch_id': id}"
|
||||
invisible="state != 'done'"/>
|
||||
<button name="%(batch_picking_create_one_bill.action_create_batch_bill_wizard)d" string="Créer Facture" type="action" class="btn-primary" context="{'default_batch_id': id}" 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">
|
||||
<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>
|
||||
|
|
@ -27,4 +19,19 @@
|
|||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Inherit the move line tree view to add the Picked field -->
|
||||
<record id="view_move_line_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.move.line.tree.picked.inherit</field>
|
||||
<field name="model">stock.move.line</field>
|
||||
<field name="inherit_id" ref="stock_picking_batch.view_move_line_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='tracking']" position="before">
|
||||
<field name="picked" optional="show"/>
|
||||
</xpath>
|
||||
<xpath expr="//list" position="attributes">
|
||||
<attribute name="decoration-success">picked</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import create_bill_wizard
|
||||
from . import merge_bill_wizard
|
||||
from . import stock_picking_to_batch
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from odoo import _, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class StockPickingToBatch(models.TransientModel):
|
||||
_inherit = "stock.picking.to.batch"
|
||||
|
||||
zero_quantity_default = fields.Boolean(
|
||||
string="Zero Quantity by Default",
|
||||
default=False,
|
||||
help="If checked, the default quantity for new move lines will be 0 instead of the computed quantity.",
|
||||
)
|
||||
|
||||
def attach_pickings(self):
|
||||
self.ensure_one()
|
||||
# Pass zero_quantity_default through context to be handled in batch create
|
||||
return super(
|
||||
StockPickingToBatch,
|
||||
self.with_context(default_zero_quantity_default=self.zero_quantity_default),
|
||||
).attach_pickings()
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="stock_picking_to_batch_form_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.picking.to.batch.form.inherit</field>
|
||||
<field name="model">stock.picking.to.batch</field>
|
||||
<field name="inherit_id" ref="stock_picking_batch.stock_picking_to_batch_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="is_create_draft" position="after">
|
||||
<field name="zero_quantity_default" invisible="mode != 'new'"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Reference in a new issue