need test for merging from different contract

This commit is contained in:
Benoît Vézina 2025-02-19 11:51:54 -05:00
parent b95ca97022
commit 996fb3767e
6 changed files with 192 additions and 54 deletions

View file

@ -72,7 +72,8 @@ class ResCompany(models.Model):
string='Maximum Products per Batch',
default=800,
help="Maximum number of products that can be processed at once (default: 800)"
)
def test_ollama_connection(self):
"""Test the connection to Ollama server.

View file

@ -16,10 +16,12 @@
"stock",
"purchase_requisition_stock",
"purchase_requisition",
"sale_purchase",
],
"data": [
"views/purchase_views.xml",
"views/purchase_requisition_views.xml",
"views/purchase_order_views.xml",
],
"installable": True,
"application": False,

View file

@ -0,0 +1,30 @@
from odoo import models
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
def _merge_alternative_po(self, rfqs):
"""Override to handle requisition_id during merge.
Args:
rfqs: recordset of purchase.order to merge
Returns:
purchase.order: the merged purchase order
"""
# Only merge orders in draft or sent state
rfqs = rfqs.filtered(lambda o: o.state in ('draft', 'sent'))
# Get the oldest order as base
base_order = rfqs.sorted(lambda x: (x.date_order, x.id))[0]
# Call parent method to merge orders
merged_order = super()._merge_alternative_po(rfqs)
# After merge, ensure lines are properly linked to their requisitions
for line in merged_order.order_line:
line._compute_requisition_id()
if line.requisition_id and line.requisition_line_id:
line.price_unit = line.requisition_line_id.price_unit
return merged_order

View file

@ -9,6 +9,7 @@ class PurchaseOrderLine(models.Model):
comodel_name="purchase.requisition",
string="Agreement",
store=True,
index=True,
compute="_compute_requisition_id",
inverse="_inverse_requisition_id",
)
@ -21,84 +22,168 @@ class PurchaseOrderLine(models.Model):
@api.model_create_multi
def create(self, vals_list):
"""Create purchase order lines and set price from requisition if applicable.
Args:
vals_list (list): List of values to create purchase order lines
Returns:
recordset: Created purchase order lines
"""
res = super().create(vals_list)
for line in res.filtered("requisition_id"):
line.price_unit = line.requisition_line_id.price_unit
return res
def _compute_price_unit_and_date_planned_and_name(self):
super()._compute_price_unit_and_date_planned_and_name()
po_lines_with_requisition = self.filtered("requisition_id")
"""Compute the price unit, date planned and name for purchase order lines.
This method extends the standard computation by:
1. Setting prices from requisition lines when applicable
2. Handling lines without requisition based on customer agreements
3. Falling back to basic computation for lines without matching agreements
"""
# Only process lines with products
lines_with_product = self.filtered('product_id')
if not lines_with_product:
return
super(PurchaseOrderLine, lines_with_product)._compute_price_unit_and_date_planned_and_name()
# Process lines with requisition
po_lines_with_requisition = lines_with_product.filtered("requisition_id")
for line in po_lines_with_requisition:
line.price_unit = line.requisition_line_id.price_unit
po_lines_without_requisition = self - po_lines_with_requisition
if line.requisition_line_id:
line.price_unit = line.requisition_line_id.price_unit
# Process lines without requisition
po_lines_without_requisition = lines_with_product - po_lines_with_requisition
to_compute_basic = self.env["purchase.order.line"]
for line in po_lines_without_requisition:
po_agreement_customers = line.order_id.requisition_id.customer_ids
customer = line._get_customer()
if po_agreement_customers and customer not in po_agreement_customers:
to_compute_basic |= line
func = BasePOL._compute_price_unit_and_date_planned_and_name
func(to_compute_basic)
if to_compute_basic:
func = BasePOL._compute_price_unit_and_date_planned_and_name
func(to_compute_basic)
@api.depends("requisition_id")
@api.depends("requisition_id", "product_id")
def _compute_requisition_line_id(self):
"""Compute the requisition line associated with this purchase order line.
For each purchase order line with a requisition, find the matching
requisition line based on the product using a search domain for better
performance. Takes the first matching line if multiple exist.
"""
RequisitionLine = self.env['purchase.requisition.line']
for line in self:
candidates = line.requisition_id.line_ids.filtered(
lambda req_line: req_line.product_id == line.product_id
)
line.requisition_line_id = candidates[0] if candidates else False
if not line.requisition_id or not line.product_id:
line.requisition_line_id = False
continue
domain = [
('requisition_id', '=', line.requisition_id.id),
('product_id', '=', line.product_id.id)
]
candidates = RequisitionLine.search(domain, limit=1)
line.requisition_line_id = candidates
@api.depends("order_id.requisition_id", "product_id")
def _get_vendor_domain(self, line):
"""Get domain for vendor matching.
Args:
line: The purchase order line
Returns:
list: Domain list for vendor conditions
"""
return [
"|",
("requisition_id.vendor_id", "=", line.order_id.partner_id.id),
(
"requisition_id.vendor_id.commercial_partner_id",
"=",
line.order_id.partner_id.id,
),
]
def _get_basic_domain(self, line, order_date):
"""Get basic domain for requisition search.
Args:
line: The purchase order line
order_date: The order date
Returns:
list: Domain list for basic conditions
"""
return [
("product_id", "=", line.product_id.id),
("requisition_id.state", "=", "confirmed"),
("requisition_id.date_start", "<=", order_date),
("requisition_id.date_end", ">=", order_date),
]
def _get_customer_domain(self, customer):
"""Get domain for customer matching.
Args:
customer: The customer record
Returns:
list: Domain list for customer conditions
"""
if customer:
return [
"|",
("requisition_id.customer_ids", "in", [customer.id]),
("requisition_id.customer_ids", "=", False),
]
return [("requisition_id.customer_ids", "=", False)]
@api.depends("product_id", "order_id.partner_id", "order_id.date_order")
def _compute_requisition_id(self):
"""Compute the requisition_id field based on various conditions.
This method finds the appropriate purchase requisition for the line by:
1. Matching the vendor
2. Matching the product
3. Checking date validity
4. Checking customer applicability
"""
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),
(
"requisition_id.vendor_id.commercial_partner_id",
"=",
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:
domain += [
"|",
("requisition_id.customer_ids", "in", [customer.id]),
("requisition_id.customer_ids", "=", False),
]
else:
domain += [
"|",
(
"requisition_id",
"=",
requisition.id,
),
("requisition_id.customer_ids", "=", False),
]
requisition_lines = self.env["purchase.requisition.line"].search(domain)
# If the current order's requisition_id is in the possible lines, use it
req_id = False
if line.order_id.requisition_id and requisition_lines:
req_id = requisition_lines.filtered(
lambda req_line: req_line.requisition_id
== line.order_id.requisition_id
).requisition_id
if not req_id and requisition_lines:
req_id = requisition_lines[0].requisition_id
line.requisition_id = req_id
# Build complete domain from components
domain = (
self._get_vendor_domain(line)
+ self._get_basic_domain(line, order_date)
+ self._get_customer_domain(customer)
)
# Search for matching requisition lines
requisition_lines = self.env["purchase.requisition.line"].search(
domain, order="create_date desc"
)
line.requisition_id = requisition_lines[0].requisition_id if requisition_lines else False
def _get_customer(self):
"""Get the customer associated with this purchase order line.
Tries to find the customer in the following order:
1. From the linked sale order
2. From the procurement group
3. From the destination moves' sale order
Returns:
res.partner: The customer record or False if not found
"""
self.ensure_one()
customer = self.sale_order_id.partner_id or self.group_id.partner_id
# BV: Questioning why this code is there?
if not customer:
sale_order = self.move_dest_ids.group_id.sale_id
if len(sale_order) == 1:
@ -106,6 +191,11 @@ class PurchaseOrderLine(models.Model):
return customer
def _inverse_requisition_id(self):
"""Inverse method for requisition_id field.
When the requisition_id is manually changed, recompute the price unit
and other related fields to ensure consistency.
"""
self._compute_price_unit_and_date_planned_and_name()
def _find_candidate(

View file

@ -7,5 +7,6 @@ class PurchaseRequisition(models.Model):
customer_ids = fields.Many2many(
comodel_name="res.partner",
string="Applicable Customers",
index=True,
help="Customer for whom this requisition is applicable",
)

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="purchase_order_form_inherit_requisition" model="ir.ui.view">
<field name="name">purchase.order.form.inherit.requisition</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<!-- Masquer le champ requisition_id -->
<field name="requisition_id" position="attributes">
<attribute name="invisible">1</attribute>
</field>
</field>
</record>
</odoo>