need test for merging from different contract
This commit is contained in:
parent
b95ca97022
commit
996fb3767e
6 changed files with 192 additions and 54 deletions
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
30
purchase_customer_requisition/models/purchase_order.py
Normal file
30
purchase_customer_requisition/models/purchase_order.py
Normal 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
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
)
|
||||
|
|
|
|||
14
purchase_customer_requisition/views/purchase_order_views.xml
Normal file
14
purchase_customer_requisition/views/purchase_order_views.xml
Normal 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>
|
||||
Loading…
Reference in a new issue