diff --git a/pos_second_uom/__init__.py b/pos_second_uom/__init__.py
new file mode 100644
index 00000000000..0650744f6bc
--- /dev/null
+++ b/pos_second_uom/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/pos_second_uom/__manifest__.py b/pos_second_uom/__manifest__.py
new file mode 100644
index 00000000000..8bc6d7099cf
--- /dev/null
+++ b/pos_second_uom/__manifest__.py
@@ -0,0 +1,18 @@
+{
+ "name": "POS Second UoM",
+ "category": "Point of Sale",
+ "depends": ["product", "uom", "point_of_sale"],
+ "author": "habar",
+ "data": [
+ "views/product_template_view.xml",
+ ],
+ "assets": {
+ "point_of_sale._assets_pos": [
+ "pos_second_uom/static/src/js/control_buttons.js",
+ "pos_second_uom/static/src/xml/control_button.xml",
+ ],
+ },
+ "installable": True,
+ "application": False,
+ "license": "LGPL-3",
+}
diff --git a/pos_second_uom/models/__init__.py b/pos_second_uom/models/__init__.py
new file mode 100644
index 00000000000..049669dd0fe
--- /dev/null
+++ b/pos_second_uom/models/__init__.py
@@ -0,0 +1,2 @@
+from . import product_template
+from . import product_product
diff --git a/pos_second_uom/models/product_product.py b/pos_second_uom/models/product_product.py
new file mode 100644
index 00000000000..88d43925ee0
--- /dev/null
+++ b/pos_second_uom/models/product_product.py
@@ -0,0 +1,11 @@
+from odoo import api, models
+
+
+class ProductProduct(models.Model):
+ _inherit = "product.product"
+
+ @api.model
+ def _load_pos_data_fields(self, config_id):
+ fields_list = super()._load_pos_data_fields(config_id)
+ fields_list.append("pos_second_uom_id")
+ return fields_list
diff --git a/pos_second_uom/models/product_template.py b/pos_second_uom/models/product_template.py
new file mode 100644
index 00000000000..e08e5c92a97
--- /dev/null
+++ b/pos_second_uom/models/product_template.py
@@ -0,0 +1,44 @@
+from odoo import _, api, fields, models
+from odoo.exceptions import ValidationError
+
+
+class ProductTemplate(models.Model):
+ _inherit = 'product.template'
+
+ pos_second_uom_id = fields.Many2one(
+ 'uom.uom',
+ string="POS Second Unit of Measure"
+ )
+ pos_second_uom_domain_ids = fields.Many2many('uom.uom', compute="_compute_pos_second_uom_domain_ids", string="All Pos Second Uom")
+
+ @api.depends("uom_id")
+ def _compute_pos_second_uom_domain_ids(self):
+ for rec in self:
+ if not rec.uom_id:
+ rec.pos_second_uom_domain_ids = self.env['uom.uom'].search([])
+ continue
+
+ root_uom = rec.uom_id
+ while root_uom.relative_uom_id:
+ root_uom = root_uom.relative_uom_id
+
+ compatible_uoms = self.env['uom.uom'].search([
+ '|', ('id', '=', root_uom.id), ('parent_path', 'like', f'%{root_uom.id}%')
+ ])
+
+ rec.pos_second_uom_domain_ids = compatible_uoms
+
+ @api.constrains('pos_second_uom_id', 'uom_id')
+ def _check_uom_compatibility(self):
+ for record in self:
+ if record.pos_second_uom_id and record.uom_id:
+ root_main = record.uom_id
+ while root_main.relative_uom_id:
+ root_main = root_main.relative_uom_id
+
+ root_second = record.pos_second_uom_id
+ while root_second.relative_uom_id:
+ root_second = root_second.relative_uom_id
+
+ if root_main.id != root_second.id:
+ raise ValidationError(_("Selected Second UoM must be from the same unit hierarchy family!"))
diff --git a/pos_second_uom/static/src/js/control_buttons.js b/pos_second_uom/static/src/js/control_buttons.js
new file mode 100644
index 00000000000..514bc11028e
--- /dev/null
+++ b/pos_second_uom/static/src/js/control_buttons.js
@@ -0,0 +1,45 @@
+import { ControlButtons } from "@point_of_sale/app/screens/product_screen/control_buttons/control_buttons";
+import { NumberPopup } from "@point_of_sale/app/components/popups/number_popup/number_popup";
+import { makeAwaitable } from "@point_of_sale/app/utils/make_awaitable_dialog";
+import { patch } from "@web/core/utils/patch";
+
+patch(ControlButtons.prototype, {
+ displaySecondUomButton() {
+ const line = this.currentOrder?.getSelectedOrderline();
+ return line?.product_id?.pos_second_uom_id;
+ },
+
+ async clickSecondUomButton() {
+ const line = this.currentOrder?.getSelectedOrderline();
+
+ if (!line) {
+ return;
+ }
+
+ const secondUom = line.product_id.pos_second_uom_id;
+ const mainUom = line.product_id.uom_id;
+
+ const qty = await makeAwaitable(
+ this.dialog,
+ NumberPopup,
+ {
+ title: `Enter ${secondUom.name} Quantity`,
+ startingValue: 0,
+ }
+ );
+
+ if (qty === null || qty === undefined) {
+ return;
+ }
+
+ const enteredQty = parseFloat(qty);
+
+ if (isNaN(enteredQty)) {
+ return;
+ }
+
+ const convertedQty = (enteredQty * secondUom.factor) / mainUom.factor;
+
+ line.setQuantity(convertedQty);
+ },
+});
diff --git a/pos_second_uom/static/src/xml/control_button.xml b/pos_second_uom/static/src/xml/control_button.xml
new file mode 100644
index 00000000000..e097562387d
--- /dev/null
+++ b/pos_second_uom/static/src/xml/control_button.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pos_second_uom/views/product_template_view.xml b/pos_second_uom/views/product_template_view.xml
new file mode 100644
index 00000000000..7a2920c1a39
--- /dev/null
+++ b/pos_second_uom/views/product_template_view.xml
@@ -0,0 +1,14 @@
+
+
+
+ product.template.form.pos.second.uom
+ product.template
+
+
+
+
+
+
+
+
+