Skip to content

Comments

[18.0][IMP] purchase_order_secondary_unit: add vendor pricelist support#2932

Merged
OCA-git-bot merged 2 commits intoOCA:18.0from
qrtl:18.0-imp-purchase_secondary_unit
Jan 27, 2026
Merged

[18.0][IMP] purchase_order_secondary_unit: add vendor pricelist support#2932
OCA-git-bot merged 2 commits intoOCA:18.0from
qrtl:18.0-imp-purchase_secondary_unit

Conversation

@yostashiro
Copy link
Member

@yostashiro yostashiro commented Jan 11, 2026

  • Extend product.supplierinfo and purchase.order.line to support secondary unit pricing
  • Add company setting to control price display in purchase reports (primary only, prioritize secondary, or both) -> Moved to product_secondary_unit
  • Update purchase order/quotation reports to show secondary unit prices according to the configuration

@qrtl QT6202

@yostashiro yostashiro force-pushed the 18.0-imp-purchase_secondary_unit branch 6 times, most recently from ae63904 to fc3ea0f Compare January 11, 2026 13:28
@yostashiro
Copy link
Member Author

Secondary Price fields added to purchase order line and vendor pricelist.
image

Added company-level report settings. The default settings generate reports in the same layout but with a small enhancement of showing the UoM for the unit price (as it could otherwise be a bit confusing).
image

If you prioritize secondary unit price and hide secondary UoM column, the report will only show secondary qty and secondary price if the purchase order line has a secondary UoM set, otherwise fall back to primary qty and primary price.
image

If you choose to show both primary and secondary, the report will do so in the same columns.
image

@AungKoKoLin1997
Copy link
Contributor

AungKoKoLin1997 commented Jan 14, 2026

I am reviewing the functional and found some strange behaviors. I am not sure this may be not part of the scope of this PR.

Test Case 1

  • I create two Secondary Unit of Measure for a product.
  • Then, I create a purchase order with secondary uom. The secondary uom and secondary uom price are correctly assigned in vendor info.
  • After that, I create another purchase order with with the same vendor of first PO and different price unit and different secondary uom.
  • When I check the vendor info, I found the price unit of existing vendor line is updated with new price unit. But the secondary uom and secondary uom price are not updated with new values.

Proudct:

Screenshot 2026-01-14 101658

Purchase Order 1:

Screenshot 2026-01-14 102100

Seller Info:

Screenshot 2026-01-14 102146

Purchase Order 2:

Screenshot 2026-01-14 102424

Seller Info:

Screenshot 2026-01-14 102458

Test Case 2

  • I create two Secondary Unit of Measure for a product.
  • Then, I create a purchase order with secondary uom. The secondary uom and secondary uom price are correctly assigned in vendor info.
  • After that, I create another purchase order with with the same vendor of first PO and same price unit and different secondary uom.
  • In the vendor info, I found the secondary uom and secondary uom price are not updated.

Purchase Order 1:

Screenshot 2026-01-14 104256

Seller Info:

Screenshot 2026-01-14 104328

Purchase Order 2:

Screenshot 2026-01-14 104843

Seller Info:
Screenshot 2026-01-14 104944

@AungKoKoLin1997
Copy link
Contributor

Regarding the two test cases I mentioned in my previous comment, they’re out of scope for this PR. I’m also not confident they’re worth addressing right now, and we don’t expect to use those use cases at the moment—so I’ll ignore them for now.

@AungKoKoLin1997 AungKoKoLin1997 force-pushed the 18.0-imp-purchase_secondary_unit branch from fc3ea0f to 1021636 Compare January 14, 2026 04:16
Copy link
Contributor

@AungKoKoLin1997 AungKoKoLin1997 left a comment

Choose a reason for hiding this comment

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

LGTM

@AungKoKoLin1997 AungKoKoLin1997 force-pushed the 18.0-imp-purchase_secondary_unit branch 2 times, most recently from 736f15b to 8e4da27 Compare January 15, 2026 04:16
@AungKoKoLin1997
Copy link
Contributor

AungKoKoLin1997 commented Jan 15, 2026

In my last commit, I fixed the layout issue when we use Prioritize Secondary Unit Price without hiding Secondary UoM Column.
image

@AungKoKoLin1997
Copy link
Contributor

@yostashiro Could you please review my last two commits? I will squash commits after you review.

<!-- Unit price: add UoM and secondary price ('secondary' and 'both' modes) -->
<xpath expr="//span[@t-field='line.price_unit']" position="after">
<t
t-if="line.report_show_price_uom() and line.get_secondary_uom_display_mode() != 'secondary'"
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
t-if="line.report_show_price_uom() and line.get_secondary_uom_display_mode() != 'secondary'"
t-if="line.report_show_price_uom()"

@AungKoKoLin1997 We should not change the code here. Please update line.report_show_price_uom() instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

Updated!

Comment on lines 68 to 91
<t
t-if="line.report_show_price_uom() and line.get_secondary_uom_display_mode() != 'secondary'"
>
/ <span t-field="line.product_uom.name" />
</t>
<t t-if="line.get_secondary_uom_display_mode() == 'secondary'">
<span t-field="line.secondary_uom_price" />
<t t-if="line.report_show_price_uom()">
/ <span
t-field="line.secondary_uom_id.name"
groups="uom.group_uom"
/>
</t>
</t>
<t t-if="line.get_secondary_uom_display_mode() == 'both'">
<br />
<span class="text-muted">
(<span t-field="line.secondary_uom_price" />
/ <span
t-field="line.secondary_uom_id.name"
groups="uom.group_uom"
/>)
</span>
</t>
Copy link
Member Author

Choose a reason for hiding this comment

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

I guess we could create a common template and call it from both report and portal view.

Copy link
Contributor

Choose a reason for hiding this comment

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

Updated!

@@ -0,0 +1,89 @@
<odoo>
<!-- Unit price: add UoM and secondary price ('secondary' and 'both' modes) -->
<template id="purchase_secondary_uom_price">
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
<template id="purchase_secondary_uom_price">
<template id="purchase_uom_price">

Copy link
Contributor

Choose a reason for hiding this comment

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

I changed the template name.

@yostashiro
Copy link
Member Author

@AungKoKoLin1997 Looks good. 👍 Please go ahead and squash commits.

@AungKoKoLin1997 AungKoKoLin1997 force-pushed the 18.0-imp-purchase_secondary_unit branch 2 times, most recently from 91f742d to 3a74084 Compare January 20, 2026 05:32
@AungKoKoLin1997
Copy link
Contributor

Depends on OCA/product-attribute#2182.

@AungKoKoLin1997
Copy link
Contributor

I added new commit to propagate secondary_uom_id from purchase order lines to account move lines when the field exists on account.move.line. This is useful when account_move_secondary_unit is installed, and avoids the need for a glue module.

else:
rec.secondary_uom_price = 0.0

@api.onchange("secondary_uom_price")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@api.onchange("secondary_uom_price")

I think we don't have to use @api.onchane for _inverse function.

Copy link
Contributor

@nobuQuartile nobuQuartile Jan 21, 2026

Choose a reason for hiding this comment

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

I removed the onchange myself and tested the behavior, and found that the value isn’t updated dynamically until the record is saved.
With onchange in place, it seems the value updates dynamically as soon as it’s written, even before saving.
So, we should have api.onchane for this _inverse function.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe you can directly update the price in _onchange_product_id_secondary_uom and skip adding the onchange in the inverse.

Copy link
Member Author

Choose a reason for hiding this comment

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

We could add a new onchange method that replicates the logic of this inverse (I guess _onchange_product_id_secondary_uom is not the one to work on) if we should be strict about not using onchange with an inverse.

I see a lot of these patterns, though, in the standard codebase, so I thought it was a widely accepted practice.

Copy link
Member

Choose a reason for hiding this comment

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

Why not reusing the onchange_product_id_secondary_uom? You update secondary_uom_id and price which is related with it in a single transaction

Copy link
Member Author

Choose a reason for hiding this comment

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

We could make it work but I don't feel there is a good reason to do so as the intents are different.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, not blocking the decision, let me know when we can move it forward, appreciate that the module has been functional tested deeply :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks! You can go ahead and merge this if you are okay with the current code, or should we add new onchange methods to remove the onchange from inverses?

else:
rec.secondary_uom_price = 0.0

@api.onchange("secondary_uom_price")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@api.onchange("secondary_uom_price")

I think we don't have to use @api.onchane for _inverse function.

Copy link
Contributor

@nobuQuartile nobuQuartile Jan 21, 2026

Choose a reason for hiding this comment

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

I removed the onchange myself and tested the behavior, and found that the value isn’t updated dynamically until the record is saved.
With onchange in place, it seems the value updates dynamically as soon as it’s written, even before saving.
So, we should have api.onchane for this _inverse function.

Copy link
Member

Choose a reason for hiding this comment

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

Same here

Copy link
Contributor

@nobuQuartile nobuQuartile left a comment

Choose a reason for hiding this comment

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

LGTM
Code review and functional review

@OCA-git-bot
Copy link
Contributor

This PR has the approved label and has been created more than 5 days ago. It should therefore be ready to merge by a maintainer (or a PSC member if the concerned addon has no declared maintainer). 🤖

1 similar comment
@OCA-git-bot
Copy link
Contributor

This PR has the approved label and has been created more than 5 days ago. It should therefore be ready to merge by a maintainer (or a PSC member if the concerned addon has no declared maintainer). 🤖

@yostashiro yostashiro changed the title [18.0][IMP] purchase_order_secondary_unit: add vendor pricelist support and report config [18.0][IMP] purchase_order_secondary_unit: add vendor pricelist support Jan 21, 2026
Copy link
Member

@HviorForgeFlow HviorForgeFlow left a comment

Choose a reason for hiding this comment

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

Code review, as it looks like adding a api.onchange in the inverse can work, because in the user interface the compute is not done until you save, you could add that price recalculation directly on the secondary uom on change, you can try, if this does not work we can go with your option

else:
rec.secondary_uom_price = 0.0

@api.onchange("secondary_uom_price")
Copy link
Member

Choose a reason for hiding this comment

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

Maybe you can directly update the price in _onchange_product_id_secondary_uom and skip adding the onchange in the inverse.

else:
rec.secondary_uom_price = 0.0

@api.onchange("secondary_uom_price")
Copy link
Member

Choose a reason for hiding this comment

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

Same here

@yostashiro
Copy link
Member Author

@HviorForgeFlow Thank you very much for your review. Commented here: #2932 (comment)

Copy link
Member

@HviorForgeFlow HviorForgeFlow left a comment

Choose a reason for hiding this comment

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

Code is not beautiful as it could be but it works.

My second concern is about adding that compute store fields.

@pedrobaeza what do you think about the onchange in the inverse and the compute store? Thanks in advice for taking your time to respond

Comment on lines +37 to +44
secondary_uom_price = fields.Float(
string="Secondary Price",
digits="Product Price",
aggregator="avg",
compute="_compute_secondary_uom_price",
inverse="_inverse_secondary_uom_price",
store=True,
)
Copy link
Member

Choose a reason for hiding this comment

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

My next concern is that this compute store can slowdown the update of the module for big databases.

Maybe we need a migration script where we create the column and fill it manually with a sql query.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, the Héctor's concern is legit. About being computed or use onchanges, I usually prefer computed fields for not depending on UI, but sometimes the things get messy due to dependencies, and a simple onchange resolves the usability question.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hi @pedrobaeza , I think Héctor is concerned about adding @api.onchange to inverse methods. Do you have any thoughts on this?

Copy link
Member

Choose a reason for hiding this comment

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

Well, I meant about making it computed stored. About reusing inverse methods as onchange may be legit for reflecting things in UI, but it depends on the logic of inverse method.

@yostashiro
Copy link
Member Author

Maybe we need a migration script where we create the column and fill it manually with a sql query.

Thanks for catching this!

@AungKoKoLin1997 Can you please help add a migration script for the purchase order line field?

@AungKoKoLin1997 AungKoKoLin1997 force-pushed the 18.0-imp-purchase_secondary_unit branch from 3ad0c96 to 2f98124 Compare January 27, 2026 01:59
@AungKoKoLin1997
Copy link
Contributor

Can you please help add a migration script for the purchase order line field?

@yostashiro Done!
I already bump module version from 18.0.1.0.118.0.1.0.2.

Comment on lines 13 to 22
UPDATE purchase_order_line pol
SET secondary_uom_price =
COALESCE(
pol.price_unit * (
SELECT su.factor
FROM product_secondary_unit su
WHERE su.id = pol.secondary_uom_id
),
0.0
);
Copy link
Member Author

Choose a reason for hiding this comment

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

I suppose this would be more performant?

Suggested change
UPDATE purchase_order_line pol
SET secondary_uom_price =
COALESCE(
pol.price_unit * (
SELECT su.factor
FROM product_secondary_unit su
WHERE su.id = pol.secondary_uom_id
),
0.0
);
UPDATE purchase_order_line pol
SET secondary_uom_price = pol.price_unit * su.factor
FROM product_secondary_unit su
WHERE su.id = pol.secondary_uom.id;

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. It will be.

Copy link
Member Author

Choose a reason for hiding this comment

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

The bump should be "minor". i.e. 18.0.1.0.1 -> 18.0.1.1.0

yostashiro and others added 2 commits January 27, 2026 03:52
… report config

- Extend product.supplierinfo and purchase.order.line to support secondary unit pricing
- Update purchase order/quotation reports to show secondary unit prices according to the
  configuration
… move line

This commit propagates secondary_uom_id from purchase order lines to account move
lines when the field exists on account.move.line. This is useful when
account_move_secondary_unit is installed, and avoids the need for a glue module.
@AungKoKoLin1997 AungKoKoLin1997 force-pushed the 18.0-imp-purchase_secondary_unit branch from 2f98124 to 9970e65 Compare January 27, 2026 03:52
@yostashiro
Copy link
Member Author

Looks good now.

@HviorForgeFlow
Copy link
Member

/ocabot merge minor

@OCA-git-bot
Copy link
Contributor

What a great day to merge this nice PR. Let's do it!
Prepared branch 18.0-ocabot-merge-pr-2932-by-HviorForgeFlow-bump-minor, awaiting test results.

@OCA-git-bot OCA-git-bot merged commit beee471 into OCA:18.0 Jan 27, 2026
6 of 7 checks passed
@OCA-git-bot
Copy link
Contributor

Congratulations, your PR was merged at 695e72e. Thanks a lot for contributing to OCA. ❤️

@mav-adhoc
Copy link
Contributor

Hello! I don't get the "hide_secondary_uom_column(o)" in the view. There is not such attribute in res.company.
image

@yostashiro
Copy link
Member Author

@mav-adhoc Oops, OCA/product-attribute#2182 needs to be merged, or we need to revert the parts that use hide_secondary_uom_column().

@sergio-teruel Would you be able to take a look at OCA/product-attribute#2182?

<xpath expr="//span[@t-field='line.product_qty']" position="attributes">
<attribute
name="t-if"
>line.get_secondary_uom_display_mode() != 'secondary'</attribute>

Choose a reason for hiding this comment

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

@yostashiro Where is this function? We updated and getting errors as not existing, can't find anywhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

This PR depends on OCA/product-attribute#2182, and the function is introduced in that PR , as I mentioned in #2932 (comment) . Unfortunately, this PR was merged before the dependency PR was merged. @yostashiro also explained the situation in the comment above: #2932 (comment).

Choose a reason for hiding this comment

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

OK, we have pinned to old version anyway after review as we really don't want these changes. In my opinion this should have been a seperate extension module, not because of the breakage, but it is for a feature that not everyone will want, its not really fixing an obvious oversight or error in functionality. In my experience this module is mostly used for straight conversions of qty's and adding price features just makes harder.

Copy link
Member Author

Choose a reason for hiding this comment

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

@gdgellatly That's a fair point and we did consider a split. The tradeoff is four additional modules across the core apps (xxx_secondary_unit_price for product, purchase, sale, and account), and we thought the maintenance overhead would outweigh the benefit. Our clients who need the secondary unit conversion also tend to need pricing per secondary unit, and all the added features here are effectively opt-in. That said, we are happy to split the module if the PSC opts for it. You can also leave a comment on OCA/product-attribute#2182 as it gets harder to change direction after merge.

Choose a reason for hiding this comment

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

@yostashiro Honestly it is no big deal, we have pinned now and that will get us through the v18 lifecycle which is only a few more months. For us a secondary unit is just a conversion to a primary unit, so price_subtotal is always identical and secondary_price_unit if we had would always just be price_subtotal/secondary_qty

And then look again during migration to decide what to do.

Copy link
Contributor

@celm1990 celm1990 left a comment

Choose a reason for hiding this comment

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

I think this PR must be reverted until the dependency OCA/product-attribute#2182
is merged; otherwise, this module is broken when you try to print the report.

@yostashiro
Copy link
Member Author

@AungKoKoLin1997 Can you please help create a PR to revert the template adjustments to avoid printing errors?

@celm1990 The dependency is now OCA/product-attribute#2211 by the way. We could also have it merged quickly if it gets support from the community.

@yostashiro
Copy link
Member Author

Here is a follow-up: #2953

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants