Skip to content

Commit aa030d2

Browse files
committed
[IMP]pruchase_ux: new features in purchase.bill.line.match
closes #320 Signed-off-by: Juan Carreras <jc@adhoc.com.ar>
1 parent 3611ad5 commit aa030d2

4 files changed

Lines changed: 48 additions & 13 deletions

File tree

purchase_ux/__manifest__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
##############################################################################
2020
{
2121
"name": "Purchase UX",
22-
"version": "18.0.1.3.0",
22+
"version": "18.0.1.4.0",
2323
"category": "Purchases",
2424
"sequence": 14,
2525
"summary": "",

purchase_ux/models/account_move.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,25 @@ def get_product_lines_to_update(self):
8686
def action_purchase_matching(self):
8787
res = super().action_purchase_matching()
8888
# mark the action so compute method in the view can apply special filtering
89+
# also pass the current move id so matching adds POLs to this bill instead of creating a new one
8990
if isinstance(res, dict):
9091
ctx = dict(res.get("context") or {})
9192
ctx["purchase_matching_from_button"] = True
93+
ctx["default_account_move_id"] = self.id
9294
res["context"] = ctx
95+
# Show only POLs where ordered qty > invoiced qty (fully invoiced lines excluded).
96+
all_pols = self.env["purchase.order.line"].search(
97+
[
98+
("partner_id", "in", (self.partner_id | self.partner_id.commercial_partner_id).ids),
99+
("state", "in", ["purchase", "done"]),
100+
]
101+
)
102+
# exclude POLs already matched to a line in this bill (qty_invoiced ignores drafts)
103+
already_matched = set(self.invoice_line_ids.filtered("purchase_line_id").mapped("purchase_line_id").ids)
104+
pending_pol_ids = all_pols.filtered(
105+
lambda p: p.product_qty > p.qty_invoiced and p.id not in already_matched
106+
).ids
107+
domain = list(res.get("domain") or [])
108+
domain += ["|", ("pol_id", "=", False), ("pol_id", "in", pending_pol_ids)]
109+
res["domain"] = domain
93110
return res

purchase_ux/models/purchase_bill_line_match.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
class PurchaseBillLineMatch(models.Model):
99
_inherit = "purchase.bill.line.match"
10+
_order = "state, product_id, aml_id, pol_id"
1011

1112
reference_description = fields.Char(
1213
string="Description",
@@ -19,25 +20,34 @@ class PurchaseBillLineMatch(models.Model):
1920
readonly=True,
2021
)
2122

23+
date_order = fields.Datetime(related="pol_id.order_id.date_order", readonly=True)
24+
2225
@api.depends("pol_id", "product_id", "display_name")
2326
def _compute_reference_description(self):
27+
lang = self.env.user.lang
2428
for rec in self:
25-
if rec.pol_id and rec.product_id:
29+
if rec.pol_id:
2630
pol_name = rec.pol_id.name or ""
27-
product_name = rec.product_id.display_name or ""
28-
if pol_name.startswith(product_name):
29-
remaining = pol_name[len(product_name) :].strip()
30-
if remaining:
31-
rec.reference_description = f"{product_name} - {remaining}"
32-
else:
33-
rec.reference_description = product_name
34-
else:
35-
rec.reference_description = pol_name
36-
elif rec.pol_id:
37-
rec.reference_description = rec.pol_id.name
31+
# pol.name format: "[ref] Product Name\nExtra description"
32+
# Use only the extra description if present, otherwise translated product name
33+
parts = pol_name.split("\n", 1)
34+
extra = parts[1].strip() if len(parts) > 1 else ""
35+
rec.reference_description = extra or rec.pol_id.product_id.with_context(lang=lang).display_name
3836
else:
3937
rec.reference_description = rec.display_name
4038

39+
def action_match_lines(self):
40+
"""When opened from an existing draft bill and only PO lines are selected
41+
(no aml_id), add the POLs to the current bill instead of creating a new one.
42+
"""
43+
account_move_id = self.env.context.get("default_account_move_id")
44+
if account_move_id and not self.aml_id and self.pol_id:
45+
bill = self.env["account.move"].browse(account_move_id)
46+
if bill.exists() and bill.state == "draft":
47+
bill._add_purchase_order_lines(self.pol_id)
48+
return bill._get_records_action()
49+
return super().action_match_lines()
50+
4151
def _compute_product_uom_qty(self):
4252
# Only apply the incompatibility filter when the view was opened
4353
# via the purchase matching action (context flag set by the action).

purchase_ux/views/purchase_bill_line_match_views.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<field name="inherit_id" ref="purchase.purchase_bill_line_match_tree"/>
77
<field name="arch" type="xml">
88
<xpath expr="//field[@name='display_name']" position="replace">
9+
<field name="product_id"/>
910
<field name="reference_description" string="Description" decoration-it="not product_id"/>
1011
</xpath>
1112
<xpath expr="//field[@name='product_uom_qty']" position="after">
@@ -19,10 +20,17 @@
1920
<field name="model">purchase.bill.line.match</field>
2021
<field name="arch" type="xml">
2122
<search>
23+
<field name="product_id"/>
24+
<field name="reference_description" string="Description"/>
25+
<field name="purchase_order_id" string="Purchase Order"/>
2226
<filter string="Órdenes de Compra" name="pol_id" domain="[('pol_id', '!=', False)]"/>
2327
<separator/>
2428
<filter string="No Facturado" name="not_invoiced" domain="[('account_move_id', '=', False)]"/>
2529
<filter string="En Factura Actual" name="current_invoice" domain="[('account_move_id', '!=', False), ('state', '=', 'draft')]"/>
30+
<separator/>
31+
<filter string="Recibido" name="received" domain="[('qty_received', '>', 0)]"/>
32+
<separator/>
33+
<filter string="Fecha" name="Date" date="date_order"/>
2634
</search>
2735
</field>
2836
</record>

0 commit comments

Comments
 (0)