Skip to content

[ADD] purchase_order_line_merge: merge PO lines into a new purchase order - #2981

Open
rjaraspearhead wants to merge 1 commit into
OCA:18.0from
rjaraspearhead:18.0-add-purchase_order_line_merge
Open

[ADD] purchase_order_line_merge: merge PO lines into a new purchase order#2981
rjaraspearhead wants to merge 1 commit into
OCA:18.0from
rjaraspearhead:18.0-add-purchase_order_line_merge

Conversation

@rjaraspearhead

Copy link
Copy Markdown

Adds a new module that allows users to select purchase order lines from
the list view and merge them into a single new purchase order.

Unlike purchase_merge which works at the order level, this module
operates at the line level, providing granular control over which
lines to consolidate and how much quantity to move.

@rjaraspearhead

Copy link
Copy Markdown
Author

Ping: @celm1990

@rjaraspearhead
rjaraspearhead force-pushed the 18.0-add-purchase_order_line_merge branch 3 times, most recently from 96cd0b3 to c1b1891 Compare March 5, 2026 03:39
@rjaraspearhead

Copy link
Copy Markdown
Author

@mymage could you please review your changes again?
I accidentally did a force push and updated some validations.

Also, could you please review the changes in sale_purchase_force_vendor as well?



@tagged("post_install", "-at_install")
class TestPurchaseOrderLineMerge(TransactionCase):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class TestPurchaseOrderLineMerge(TransactionCase):
class TestPurchaseOrderLineMerge(BaseCommon):

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inheriting from BaseCommon, this line is not necessary.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

"product_uom": cls.product_1.uom_id.id,
"product_qty": 8.0,
"price_unit": 100.0,
"taxes_id": [(6, 0, cls.tax.ids)],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"taxes_id": [(6, 0, cls.tax.ids)],
"taxes_id": [Command.set(cls.tax.ids)],

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +38 to +42
(
0,
0,
self._prepare_default_line_vals(line),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(
0,
0,
self._prepare_default_line_vals(line),
)
Command.create(
self._prepare_default_line_vals(line)
)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

"""Return the purchase lines eligible for merge initialization."""
# Placeholder: if needed, filter out lines whose order is not in draft.
# Example: po_lines.filtered(lambda ln: ln.order_id.state == "draft")
return po_lines

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you must exclude at least the canceled lines.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

"product_qty": vals["quantity"],
"price_unit": vals["price_unit"],
"date_planned": self.date_order,
"taxes_id": [(6, 0, vals["taxes_id"].ids)],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"taxes_id": [(6, 0, vals["taxes_id"].ids)],
"taxes_id": [Command.set(vals["taxes_id"].ids)],

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +161 to +182
def _action_view_purchase_order(self, purchase_order):
action = {
"name": self.env._("Purchase Order"),
"type": "ir.actions.act_window",
"res_model": "purchase.order",
"context": {"create": False},
}
if len(purchase_order) == 1:
action.update(
{
"view_mode": "form",
"res_id": purchase_order.id,
}
)
else:
action.update(
{
"view_mode": "list,form",
"domain": [("id", "in", purchase_order.ids)],
}
)
return action

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Odoo has a generic function to do this. Please review this function; I think it can be reused:
https://github.com/odoo/odoo/blob/8295abd79ec50646cc833916d7c8995fae905665/odoo/addons/base/models/ir_ui_view.py#L2899C9-L2899C28

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +234 to +241
def _get_merge_key(self):
self.ensure_one()
return (
self.product_id.id,
self.price_unit,
self.product_uom.id,
tuple(sorted(self.taxes_id.ids)),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the discount field is native in Odoo. I think you must consider the discount when merging lines: merge the lines only if they have the same discount, or do not merge them if the discount is different.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

<field name="name">purchase.order.line.merge.form</field>
<field name="model">purchase.order.line.merge</field>
<field name="arch" type="xml">
<form string="Merge Purchase Order Lines">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<form string="Merge Purchase Order Lines">
<form>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

"license": "AGPL-3",
"category": "Inventory/Purchase",
"summary": "Merge purchase order lines into a new purchase order",
"depends": ["purchase", "purchase_order_line_menu", "purchase_stock"],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"depends": ["purchase", "purchase_order_line_menu", "purchase_stock"],
"depends": ["purchase_order_line_menu", "purchase_stock"],

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@rjaraspearhead
rjaraspearhead force-pushed the 18.0-add-purchase_order_line_merge branch 2 times, most recently from b3fe6d0 to 1974013 Compare March 5, 2026 20:55

@mymage mymage left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional OK

@celm1990 celm1990 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review the older comments. Some of them are still pending your review.

@api.depends("quantity", "price_unit")
def _compute_price_subtotal(self):
for line in self:
line.price_subtotal = line.quantity * line.price_unit

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the discount? I think it should be taken into account when computing the value.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listo

self.discount,
)

def _is_mergeable_for_merge(self):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def _is_mergeable_for_merge(self):
def _is_mergeable(self):

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listo

@rjaraspearhead
rjaraspearhead force-pushed the 18.0-add-purchase_order_line_merge branch from 1974013 to a085d93 Compare July 27, 2026 16:01
@OCA-git-bot OCA-git-bot added series:18.0 mod:purchase_order_line_merge Module purchase_order_line_merge labels Jul 27, 2026
@rjaraspearhead

Copy link
Copy Markdown
Author

Please review the older comments. Some of them are still pending your review.

Could you please help me review it again?

@rjaraspearhead
rjaraspearhead requested a review from celm1990 July 27, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mod:purchase_order_line_merge Module purchase_order_line_merge series:18.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants