Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 52 additions & 10 deletions product_secondary_unit/README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

======================
Product Secondary Unit
======================
Expand All @@ -17,7 +13,7 @@ Product Secondary Unit
.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
:target: https://odoo-community.org/page/development-status
:alt: Production/Stable
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproduct--attribute-lightgray.png?logo=github
Expand All @@ -40,6 +36,48 @@ other units with their conversion factor.
.. contents::
:local:

Configuration
=============

To configure this module, go to **Settings** and locate the **Units of
Measure** section.

Secondary Unit Price Display
----------------------------

Configure how unit prices and quantities are shown in reports when
secondary units are used.

- **Sales**: Select the display policy for sales order and customer
invoice reports and portal views.
- **Purchase**: Select the display policy for purchase order and vendor
bill reports and portal views.

Available options:

- **Primary Unit Price Only**: Show only the primary unit price.
- **Prioritize Secondary Unit Price**: Show the secondary unit price
when available, otherwise fall back to the primary unit price.
- **Both Primary and Secondary Unit Prices**: Show both primary and
secondary unit prices.

Hide Secondary Qty Column
-------------------------

Hide the separate **Second Qty** column in reports.

- When enabled, the **Second Qty** column is hidden in reports. The
secondary quantity can still be shown in the main **Qty** column
depending on the selected price display policy above.
- Apply the setting per document type:

- **Sales**
- **Purchase**

These settings are intended to be used by dependency modules (for
example, ``purchase_order_secondary_unit`` and
``account_move_secondary_unit``).

Usage
=====

Expand Down Expand Up @@ -70,11 +108,15 @@ Authors
Contributors
------------

- Carlos Dauden <carlos.dauden@tecnativa.com>
- Sergio Teruel <sergio.teruel@tecnativa.com>
- Kitti Upariphutthiphong <kittiu@ecosoft.co.th>
- Pimolnat Suntian <pimolnats@ecosoft.co.th>
- Alan Ramos <alan.ramos@jarsa.com.mx>
- Carlos Dauden <carlos.dauden@tecnativa.com>
- Sergio Teruel <sergio.teruel@tecnativa.com>
- Kitti Upariphutthiphong <kittiu@ecosoft.co.th>
- Pimolnat Suntian <pimolnats@ecosoft.co.th>
- Alan Ramos <alan.ramos@jarsa.com.mx>
- `Quartile <https://www.quartile.co>`__:

- Yoshi Tashiro
- Aung Ko Ko Lin

Maintainers
-----------
Expand Down
7 changes: 6 additions & 1 deletion product_secondary_unit/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
"application": False,
"installable": True,
"depends": ["product"],
"data": ["security/ir.model.access.csv", "views/product_views.xml"],
"data": [
"security/product_secondary_unit_groups.xml",
"security/ir.model.access.csv",
"views/product_views.xml",
"views/res_config_settings_views.xml",
],
"maintainers": ["sergio-teruel"],
}
2 changes: 2 additions & 0 deletions product_secondary_unit/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
from . import product_second_unit
from . import product_secondary_unit_mixin
from . import product_template
from . import res_company
from . import res_config_settings
53 changes: 53 additions & 0 deletions product_secondary_unit/models/product_secondary_unit_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,56 @@ def default_get(self, fields_list):
):
defaults["secondary_uom_qty"] = 1.0
return defaults

def _get_secondary_uom_report_type(self):
"""Return 'sale', 'purchase', or None."""
self.ensure_one()
if self._name == "sale.order.line" or (
self._name == "account.move.line"
and self.move_id.is_sale_document(include_receipts=True)
):
return "sale"
if self._name == "purchase.order.line" or (
self._name == "account.move.line"
and self.move_id.is_purchase_document(include_receipts=True)
):
return "purchase"
return None

def _get_secondary_uom_hide_col(self):
"""Return whether secondary UoM column should be hidden on reports."""
self.ensure_one()
if not self.secondary_uom_id:
return True
report_type = self._get_secondary_uom_report_type()
if report_type == "purchase":
return self.company_id.hide_secondary_uom_column_purchase
if report_type == "sale":
return self.company_id.hide_secondary_uom_column_sale
return True

def get_secondary_uom_display_mode(self):
"""Return display mode for secondary UoM price on reports."""
self.ensure_one()
if not self.secondary_uom_id:
return "primary"
report_type = self._get_secondary_uom_report_type()
if report_type == "purchase":
return self.company_id.secondary_uom_price_display_purchase
if report_type == "sale":
return self.company_id.secondary_uom_price_display_sale
return "primary"

def report_show_price_uom(self, uom_source=None):
"""Return True if UoM should be shown in price column.

UoM is shown when the line displays multiple UoMs.
"""
self.ensure_one()
if not self.secondary_uom_id:
return False
hide_col = self._get_secondary_uom_hide_col()
display_mode = self.get_secondary_uom_display_mode()
if uom_source == "primary_uom" and display_mode == "secondary":
return False
return not hide_col or display_mode == "both"
51 changes: 51 additions & 0 deletions product_secondary_unit/models/res_company.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Copyright 2026 Quartile (https://www.quartile.co)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models

PRICE_DISPLAY_SELECTION = [
("primary", "Primary Unit Price Only"),
("secondary", "Prioritize Secondary Unit Price"),
("both", "Both Primary and Secondary Unit Prices"),
]


class ResCompany(models.Model):
_inherit = "res.company"

secondary_uom_price_display_sale = fields.Selection(
Copy link
Member

Choose a reason for hiding this comment

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

This configurability level seems a bit insane. Are you going to display UoM price on sale, but not on purchase, but show the UoM? Can't this be fit with a single security group "Show seconday unit prices"?

Copy link
Contributor Author

@AungKoKoLin1997 AungKoKoLin1997 Feb 17, 2026

Choose a reason for hiding this comment

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

For showing the UoM column option, it is intended only to preserve the existing Sales/Purchase report behavior of sale_order_secondary_unit and purchase_order_secondary_unit when Primary Unit Price Only is selected. For the other price display modes, the secondary UoM is already shown in the quantity and price columns, so showing an additional UoM column would be redundant.

I don't think Sales and Purchase should always share the same display style for the secondary unit price and the secondary unit column. Therefore, I prefer separate settings for Sales and Purchase documents, with separate security groups to control each document type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the expected report when Primary Unit Price Only is selected without hiding the UoM column.
image

This is the expected report when Prioritize Secondary Unit Price is selected.
image

This is the expected report when Both Primary and Secondary Unit Prices is selected.
image

Copy link
Member

Choose a reason for hiding this comment

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

But as said, this is too many configurations, while it's not a usual problem to show both. This only increases the maintenance burden to the main module being a residual feature that most people won't use, being enough with the default option. I think this has to be simplified.

Copy link
Member

Choose a reason for hiding this comment

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

@pedrobaeza To me it makes sense to be able to configure sales and purchasing separately as they have different needs. But we can do the secondary UoM price display thing in a separate module if it's too much to be covered in the main module, which will mean four new additional modules (i.e., product_secondary_unit_price, purchase_order_secondary_unit_price, sale_order_secondary_unit_price and account_move_secondary_unit_price).

What we still want to do in this module, though, is to be able to hide the secondary qty column in reports. For this, we don't need to have separate config for sales and purchasing (as we will most likely disable this for both anyway).

Does this sound like a good approach?

Copy link
Member

Choose a reason for hiding this comment

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

@sergio-teruel is the main creator of this module, so I let him to decide at last if he considers OK to have all the configuration tweaks in the main module.

selection=PRICE_DISPLAY_SELECTION,
string="Secondary Unit Price Display (Sales)",
default="primary",
required=True,
)
secondary_uom_price_display_purchase = fields.Selection(
selection=PRICE_DISPLAY_SELECTION,
string="Secondary Unit Price Display (Purchase)",
default="primary",
required=True,
)
# Added for supporting the existing report presentation. We can drop this together
# with the second qty column in reports if the community agrees with it.
hide_secondary_uom_column_sale = fields.Boolean(
string="Hide Secondary UoM Column (Sales)",
default=False,
)
hide_secondary_uom_column_purchase = fields.Boolean(
string="Hide Secondary UoM Column (Purchase)",
default=False,
)

def hide_secondary_uom_column(self, record):
"""Return whether to hide the 'Second Qty' column for this document type."""
if record._name == "sale.order" or (
record._name == "account.move"
and record.is_sale_document(include_receipts=True)
):
return self.hide_secondary_uom_column_sale
if record._name == "purchase.order" or (
record._name == "account.move"
and record.is_purchase_document(include_receipts=True)
):
return self.hide_secondary_uom_column_purchase
return True
25 changes: 25 additions & 0 deletions product_secondary_unit/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2026 Quartile (https://www.quartile.co)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models


class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"

secondary_uom_price_display_sale = fields.Selection(
related="company_id.secondary_uom_price_display_sale",
readonly=False,
)
secondary_uom_price_display_purchase = fields.Selection(
related="company_id.secondary_uom_price_display_purchase",
readonly=False,
)
hide_secondary_uom_column_sale = fields.Boolean(
related="company_id.hide_secondary_uom_column_sale",
readonly=False,
)
hide_secondary_uom_column_purchase = fields.Boolean(
related="company_id.hide_secondary_uom_column_purchase",
readonly=False,
)
34 changes: 34 additions & 0 deletions product_secondary_unit/readme/CONFIGURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
To configure this module, go to **Settings** and locate the **Units of Measure**
section.

## Secondary Unit Price Display

Configure how unit prices and quantities are shown in reports when secondary units are
used.

- **Sales**: Select the display policy for sales order and customer invoice reports and
portal views.
- **Purchase**: Select the display policy for purchase order and vendor bill reports and
portal views.

Available options:

- **Primary Unit Price Only**: Show only the primary unit price.
- **Prioritize Secondary Unit Price**: Show the secondary unit price when available,
otherwise fall back to the primary unit price.
- **Both Primary and Secondary Unit Prices**: Show both primary and secondary unit
prices.

## Hide Secondary Qty Column

Hide the separate **Second Qty** column in reports.

- When enabled, the **Second Qty** column is hidden in reports. The secondary quantity
can still be shown in the main **Qty** column depending on the selected price display
policy above.
- Apply the setting per document type:
- **Sales**
- **Purchase**

These settings are intended to be used by dependency modules (for example,
`purchase_order_secondary_unit` and `account_move_secondary_unit`).
3 changes: 3 additions & 0 deletions product_secondary_unit/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
- Kitti Upariphutthiphong \<<kittiu@ecosoft.co.th>\>
- Pimolnat Suntian \<<pimolnats@ecosoft.co.th>\>
- Alan Ramos \<<alan.ramos@jarsa.com.mx>\>
- [Quartile](https://www.quartile.co):
- Yoshi Tashiro
- Aung Ko Ko Lin
11 changes: 11 additions & 0 deletions product_secondary_unit/security/product_secondary_unit_groups.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="group_sale_secondary_unit" model="res.groups">
<field name="name">Sale Secondary Unit</field>
<field name="category_id" ref="base.module_category_hidden" />
</record>
<record id="group_purchase_secondary_unit" model="res.groups">
<field name="name">Purchase Secondary Unit</field>
<field name="category_id" ref="base.module_category_hidden" />
</record>
</odoo>
Loading