Fixes to purchase_customer_requisition and shipping info on cust. inv.

purchase_customer_requisition:

* Make sure to check the validity (state + dates) on purchase
  requisitions being selected for PO lines.

shipping_information_on_customer_invoice:

* Rework how the picking is selected, going through the sale lines
  related to the invoice lines instead of the non-existing picking_id
  field previously coded.
This commit is contained in:
Marc Durepos 2025-02-11 14:27:50 -05:00
parent dd76c00ec1
commit 4c5b55a7dd
5 changed files with 155 additions and 49 deletions

View file

@ -53,6 +53,7 @@ class PurchaseOrderLine(models.Model):
def _compute_requisition_id(self):
for line in self:
customer = line._get_customer()
order_date = line.order_id.date_order
domain = [
"|",
("requisition_id.vendor_id", "=", line.order_id.partner_id.id),
@ -62,6 +63,9 @@ class PurchaseOrderLine(models.Model):
line.order_id.partner_id.id,
),
("product_id", "=", line.product_id.id),
("requisition_id.state", "=", "confirmed"),
("requisition_id.date_start", "<=", order_date),
("requisition_id.date_end", ">=", order_date),
]
requisition = self.order_id.requisition_id
if customer:

View file

@ -89,7 +89,7 @@ class TestPurchaseOrder(TransactionCase):
"date_end": fields.Date.today() + timedelta(days=265),
}
)
cls.agreement_1.action_confirm()
cls.agreement_2.action_confirm()
def test_one_purchase_order_line_gets_correct_agreement(self):
sale_order = self.env["sale.order"].create(
@ -175,3 +175,116 @@ class TestPurchaseOrder(TransactionCase):
line.requisition_id = False
self.assertEqual(purchase_order.order_line[0].price_unit, 3000)
def test_requisition_selection_state_and_validity(self):
"""Test that requisitions are only selected if they are confirmed and currently valid."""
# Create a draft requisition
draft_agreement = self.env["purchase.requisition"].create(
{
"vendor_id": self.supplier.id,
"customer_ids": [Command.set([self.client_1.id])],
"line_ids": [
Command.create(
{
"product_id": self.product_1.id,
"product_qty": 100,
"price_unit": 4000,
}
),
],
"date_start": fields.Date.today() - timedelta(days=100),
"date_end": fields.Date.today() + timedelta(days=265),
}
)
# Create an expired requisition
expired_agreement = self.env["purchase.requisition"].create(
{
"vendor_id": self.supplier.id,
"customer_ids": [Command.set([self.client_1.id])],
"line_ids": [
Command.create(
{
"product_id": self.product_1.id,
"product_qty": 100,
"price_unit": 5000,
}
),
],
"date_start": fields.Date.today() - timedelta(days=200),
"date_end": fields.Date.today() - timedelta(days=100),
}
)
expired_agreement.action_confirm()
# Create a future requisition
future_agreement = self.env["purchase.requisition"].create(
{
"vendor_id": self.supplier.id,
"customer_ids": [Command.set([self.client_1.id])],
"line_ids": [
Command.create(
{
"product_id": self.product_1.id,
"product_qty": 100,
"price_unit": 6000,
}
),
],
"date_start": fields.Date.today() + timedelta(days=100),
"date_end": fields.Date.today() + timedelta(days=200),
}
)
future_agreement.action_confirm()
# Create and confirm a sale order
sale_order = self.env["sale.order"].create(
{
"partner_id": self.client_1.id,
"order_line": [
Command.create(
{
"product_id": self.product_1.id,
"product_uom_qty": 50,
}
)
],
}
)
sale_order.action_confirm()
# Verify that the purchase order line gets the correct agreement (agreement_1)
purchase_order = sale_order._get_purchase_orders()[0]
purchase_line = purchase_order.order_line[0]
# Should select agreement_1 which is confirmed and currently valid
self.assertEqual(
purchase_line.requisition_id,
self.agreement_1,
"Purchase order line should select the confirmed and currently valid agreement",
)
self.assertEqual(
purchase_line.price_unit,
1000,
"Purchase order line should have the price from the valid agreement",
)
# The other agreements should not be selected because:
# - draft_agreement is not confirmed
# - expired_agreement is outside its validity dates
# - future_agreement hasn't started yet
self.assertNotEqual(
purchase_line.requisition_id,
draft_agreement,
"Draft agreement should not be selected",
)
self.assertNotEqual(
purchase_line.requisition_id,
expired_agreement,
"Expired agreement should not be selected",
)
self.assertNotEqual(
purchase_line.requisition_id,
future_agreement,
"Future agreement should not be selected",
)

View file

@ -1,19 +1,19 @@
{
'name': 'Shipping Information on Customer Invoice',
'version': '18.0.0.1',
'category': 'Accounting',
'summary': 'Add shipping carrier information on customer invoices',
'description': """
"name": "Shipping Information on Customer Invoice",
"version": "18.0.0.1",
"category": "Accounting",
"summary": "Add shipping carrier information on customer invoices",
"description": """
This module adds shipping carrier information to customer invoices:
* Carrier name
* Tracking number
* Billing mode
""",
'depends': ['account', 'delivery'],
'data': [
'views/report_invoice.xml',
"depends": ["account", "delivery", "delivery_carrier_partner_account"],
"data": [
"views/report_invoice.xml",
],
'installable': True,
'auto_install': False,
'license': 'LGPL-3',
"installable": True,
"auto_install": False,
"license": "LGPL-3",
}

View file

@ -1,21 +1,17 @@
from odoo import api, fields, models
class AccountMove(models.Model):
_inherit = 'account.move'
def _get_delivery_info(self):
"""Get the delivery information for the invoice."""
self.ensure_one()
if self.move_type != 'out_invoice':
return False
deliveries = self.picking_ids.filtered(lambda p: p.carrier_id)
if not deliveries:
return False
carrier = deliveries[0].carrier_id
return {
'carrier_name': carrier.name,
'tracking_ref': deliveries[0].carrier_tracking_ref or '',
'invoice_policy': dict(carrier._fields['invoice_policy'].selection).get(carrier.invoice_policy, carrier.invoice_policy),
}
class AccountMove(models.Model):
_inherit = "account.move"
picking_id = fields.One2many(
comodel_name="stock.picking",
string="Pickings",
compute="_compute_picking_id",
)
@api.depends("invoice_line_ids.sale_line_ids.move_ids.picking_id")
def _compute_picking_id(self):
for move in self:
pickings = move.invoice_line_ids.mapped("sale_line_ids.move_ids.picking_id")
move.picking_id = pickings and pickings[0] or False

View file

@ -2,25 +2,18 @@
<odoo>
<template id="report_invoice_document_inherit_shipping" inherit_id="account.report_invoice_document">
<xpath expr="//div[@id='informations']" position="inside">
<t t-if="o._get_delivery_info()">
<div class="col-auto col-3 mw-100 mb-2" name="shipping_info">
<strong>Shipping Information:</strong>
<p class="m-0">
<strong>Carrier: </strong>
<span t-esc="o._get_delivery_info()['carrier_name']"/>
</p>
<t t-if="o._get_delivery_info()['tracking_ref']">
<p class="m-0">
<strong>Tracking: </strong>
<span t-esc="o._get_delivery_info()['tracking_ref']"/>
</p>
</t>
<p class="m-0">
<strong>Billing Mode: </strong>
<span t-esc="o._get_delivery_info()['invoice_policy']"/>
</p>
</div>
</t>
<div class="col-auto col-3 mw-100 mb-2" name="carrier" t-if="o.picking_id.carrier_id">
<strong>Carrier: </strong>
<span t-field="o.picking_id.carrier_id.name"/>
</div>
<div class="col-auto col-3 mw-100 mb-2" name="tracking" t-if="o.picking_id.carrier_tracking_ref">
<strong>Tracking: </strong>
<span t-field="o.picking_id.carrier_tracking_ref"/>
</div>
<div class="col-auto col-3 mw-100 mb-2" name="delivery_billing_mode" t-if="o.picking_id.delivery_billing_mode">
<strong>Billing Mode: </strong>
<span t-field="o.picking_id.delivery_billing_mode"/>
</div>
</xpath>
</template>
</odoo>