Fixes an issue where creating the bills from the purchase orders could create a bill with quantities vastly different from the received batch quantities. This would happen when the purchase orders had lines with quantities to invoice that did not match the receipt quantities. Instead of creating the bills from the POs, we now generate the bill and its line values directly from the quantities on the move lines related to the batch.
164 lines
5.7 KiB
Python
164 lines
5.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
from odoo import models, fields, api, Command, _
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class StockPickingBatch(models.Model):
|
|
_inherit = "stock.picking.batch"
|
|
|
|
purchase_order_ids = fields.Many2many(
|
|
"purchase.order",
|
|
string="Bons de commande",
|
|
compute="_compute_purchase_orders",
|
|
store=True,
|
|
)
|
|
|
|
partner_ids = fields.Many2many(
|
|
"res.partner", string="Fournisseur", compute="_compute_partner", store=True
|
|
)
|
|
|
|
@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",
|
|
default=True,
|
|
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,
|
|
)
|
|
invoice_count = fields.Integer(
|
|
string="Nombre de factures",
|
|
compute="_compute_invoice_count",
|
|
store=True,
|
|
compute_sudo=True,
|
|
)
|
|
|
|
# Field computation methods
|
|
|
|
@api.depends("picking_ids.move_ids.purchase_line_id.order_id")
|
|
def _compute_purchase_orders(self):
|
|
for batch in self:
|
|
batch.purchase_order_ids = (
|
|
batch.picking_ids.move_ids.purchase_line_id.order_id
|
|
)
|
|
|
|
@api.depends("purchase_order_ids")
|
|
def _compute_partner(self):
|
|
for wizard in self:
|
|
wizard.partner_ids = wizard.purchase_order_ids.mapped("partner_id")
|
|
|
|
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
|
|
|
|
@api.depends("move_line_ids.move_id.purchase_line_id.order_id.invoice_ids")
|
|
def _compute_invoice_ids(self):
|
|
for batch in self:
|
|
purchase_orders = batch.move_line_ids.move_id.purchase_line_id.order_id
|
|
batch.invoice_ids = purchase_orders.invoice_ids
|
|
|
|
@api.depends("invoice_ids")
|
|
def _compute_invoice_count(self):
|
|
for batch in self:
|
|
batch.invoice_count = len(batch.invoice_ids)
|
|
|
|
# Actions
|
|
|
|
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
|
|
|
|
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
|
|
|
|
def action_create_bill(self):
|
|
self.ensure_one()
|
|
if not self.purchase_order_ids:
|
|
raise ValidationError(_("No purchase orders found in this batch."))
|
|
|
|
if len(self.partner_ids) > 1:
|
|
raise ValidationError(_("The batch must have only one supplier."))
|
|
|
|
bill = self.env["account.move"].create(self._get_bill_values())
|
|
return self._get_view_bill_action(bill)
|
|
|
|
# Helpers
|
|
|
|
def _get_view_bill_action(self, bill):
|
|
action = self.env["ir.actions.act_window"]._for_xml_id(
|
|
"account.action_move_in_invoice_type"
|
|
)
|
|
action["views"] = [(False, "form")]
|
|
action["res_id"] = self.invoice_ids[-1].id
|
|
return action
|
|
|
|
def _get_currency_id(self):
|
|
currency_id = self.purchase_order_ids.mapped("currency_id")
|
|
if len(currency_id) > 1:
|
|
raise UserError(_("The selected receipts do not have the same currency."))
|
|
return currency_id.id
|
|
|
|
def _get_bill_values(self):
|
|
"""Build a dictionary of the values for the vendor bill to create."""
|
|
|
|
company_id = self.company_id.id
|
|
partner_id = self.partner_ids.id
|
|
invoice_date = self.scheduled_date
|
|
invoice_origin = ", ".join(self.purchase_order_ids.mapped("name"))
|
|
move_line_ids = self._get_line_values()
|
|
currency_id = self._get_currency_id()
|
|
return {
|
|
"company_id": company_id,
|
|
"partner_id": partner_id,
|
|
"move_type": "in_invoice",
|
|
"invoice_date": invoice_date,
|
|
"invoice_origin": invoice_origin,
|
|
"currency_id": currency_id,
|
|
"line_ids": move_line_ids,
|
|
}
|
|
|
|
def _get_line_values(self):
|
|
"""For each of the stock.move.line in the batch, build a dictionary of values for the invoice line to match."""
|
|
line_vals = []
|
|
for move_line in self.move_line_ids:
|
|
purchase_line_id = move_line.move_id.purchase_line_id
|
|
line_vals.append(
|
|
Command.create(
|
|
{
|
|
"product_id": move_line.product_id.id,
|
|
"quantity": move_line.quantity,
|
|
"price_unit": purchase_line_id.price_unit,
|
|
"discount": purchase_line_id.discount,
|
|
"purchase_line_id": purchase_line_id.id,
|
|
}
|
|
)
|
|
)
|
|
return line_vals
|