From e8ade9ed92e51ee0354754a7d86d41ab969a423d Mon Sep 17 00:00:00 2001 From: Eric Antones Date: Tue, 17 Feb 2026 11:24:48 +0100 Subject: [PATCH 1/3] [ADD] uom_rounding_coherence --- uom_rounding_coherence/README.rst | 114 +++++ uom_rounding_coherence/__init__.py | 1 + uom_rounding_coherence/__manifest__.py | 15 + uom_rounding_coherence/i18n/ca.po | 47 ++ uom_rounding_coherence/i18n/es.po | 47 ++ uom_rounding_coherence/models/__init__.py | 1 + uom_rounding_coherence/models/uom_uom.py | 83 ++++ uom_rounding_coherence/readme/CONFIGURE.rst | 3 + .../readme/CONTRIBUTORS.rst | 4 + uom_rounding_coherence/readme/DESCRIPTION.rst | 21 + uom_rounding_coherence/readme/USAGE.rst | 19 + .../static/description/icon.png | Bin 0 -> 6342 bytes .../static/description/index.html | 464 ++++++++++++++++++ uom_rounding_coherence/tests/__init__.py | 1 + .../tests/test_uom_rounding_coherence.py | 302 ++++++++++++ 15 files changed, 1122 insertions(+) create mode 100644 uom_rounding_coherence/README.rst create mode 100644 uom_rounding_coherence/__init__.py create mode 100644 uom_rounding_coherence/__manifest__.py create mode 100644 uom_rounding_coherence/i18n/ca.po create mode 100644 uom_rounding_coherence/i18n/es.po create mode 100644 uom_rounding_coherence/models/__init__.py create mode 100644 uom_rounding_coherence/models/uom_uom.py create mode 100644 uom_rounding_coherence/readme/CONFIGURE.rst create mode 100644 uom_rounding_coherence/readme/CONTRIBUTORS.rst create mode 100644 uom_rounding_coherence/readme/DESCRIPTION.rst create mode 100644 uom_rounding_coherence/readme/USAGE.rst create mode 100644 uom_rounding_coherence/static/description/icon.png create mode 100644 uom_rounding_coherence/static/description/index.html create mode 100644 uom_rounding_coherence/tests/__init__.py create mode 100644 uom_rounding_coherence/tests/test_uom_rounding_coherence.py diff --git a/uom_rounding_coherence/README.rst b/uom_rounding_coherence/README.rst new file mode 100644 index 000000000..9e296586c --- /dev/null +++ b/uom_rounding_coherence/README.rst @@ -0,0 +1,114 @@ +====================== +UoM Rounding Coherence +====================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:d646eddf3ef1d6ff89719e1651d153ab196194e731190ebbcaa2e135d9ff4a8e + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |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-NuoBiT%2Fodoo--addons-lightgray.png?logo=github + :target: https://github.com/NuoBiT/odoo-addons/tree/14.0/uom_rounding_coherence + :alt: NuoBiT/odoo-addons + +|badge1| |badge2| |badge3| + +This module adds validation to ensure that Unit of Measure (UoM) rounding +precision is coherent with the conversion ratio to the reference unit, +preventing precision loss during conversions. + +When converting quantities between UoMs in the same category, a non-reference +UoM whose rounding is too coarse relative to its conversion factor will lose +precision beyond the reference UoM's rounding. + +**Example Problem:** + +If the reference UoM has rounding 0.001 and a secondary UoM with ratio 1.141 +has rounding 0.01, each conversion can introduce up to ±0.004 error in +reference units — enough to accumulate visible discrepancies over multiple +transactions. + +**The Validation:** + +The module checks that converting the UoM's rounding step to reference units +(rounding / factor) does not exceed the reference UoM's rounding. This ensures +data consistency and prevents precision-related errors. + + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +No configuration is needed. The module automatically validates UoM rounding +coherence when you create or modify UoMs. + + +Usage +===== + +When creating or modifying Units of Measure: + +#. Go to *Inventory > Configuration > UoM Categories* +#. Create or edit a UoM +#. If the rounding precision is too coarse for the conversion ratio, + the system will show a validation error +#. The error message will indicate: + + * The UoM with the problem + * Its current rounding value + * The conversion factor + * The effective rounding in reference units + * The reference unit and its rounding + +#. To fix the error, either: + + * Decrease the rounding of the problematic UoM, or + * Increase the rounding of the reference UoM (if appropriate) + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* NuoBiT Solutions +* S.L. + +Contributors +~~~~~~~~~~~~ + +* `NuoBiT `__: + + * Eric Antones + + +Maintainers +~~~~~~~~~~~ + +This module is part of the `NuoBiT/odoo-addons `_ project on GitHub. + +You are welcome to contribute. diff --git a/uom_rounding_coherence/__init__.py b/uom_rounding_coherence/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/uom_rounding_coherence/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/uom_rounding_coherence/__manifest__.py b/uom_rounding_coherence/__manifest__.py new file mode 100644 index 000000000..cfa7cd420 --- /dev/null +++ b/uom_rounding_coherence/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2026 NuoBiT Solutions - Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +{ + "name": "UoM Rounding Coherence", + "summary": "Validates that UoM rounding precision is coherent" + " with the conversion ratio to prevent precision loss", + "version": "14.0.1.0.0", + "author": "NuoBiT Solutions, S.L.", + "website": "https://github.com/nuobit/odoo-addons", + "category": "Product", + "depends": ["uom"], + "license": "AGPL-3", + "installable": True, +} diff --git a/uom_rounding_coherence/i18n/ca.po b/uom_rounding_coherence/i18n/ca.po new file mode 100644 index 000000000..7c0fbb188 --- /dev/null +++ b/uom_rounding_coherence/i18n/ca.po @@ -0,0 +1,47 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * uom_rounding_coherence +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-02-17 00:00+0000\n" +"PO-Revision-Date: 2026-02-17 00:00+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: uom_rounding_coherence +#: code:addons/uom_rounding_coherence/models/uom_uom.py:0 +#, python-format +msgid "" +"Cannot validate rounding coherence for category '%(category)s': expected " +"exactly one active reference unit of measure but found %(count)s." +msgstr "" +"No es pot validar la coherència d'arrodoniment per a la categoria " +"'%(category)s': s'esperava exactament una unitat de mesura de referència " +"activa però se n'han trobat %(count)s." + +#. module: uom_rounding_coherence +#: code:addons/uom_rounding_coherence/models/uom_uom.py:0 +#, python-format +msgid "" +"The rounding precision of '%(uom)s' (%(uom_rounding)s) is too coarse for " +"its conversion ratio (%(factor)s). When converted to the reference unit " +"'%(ref)s', the effective rounding becomes %(effective)s, which exceeds the " +"reference rounding of %(ref_rounding)s.\n" +"\n" +"To fix this, either decrease the rounding of '%(uom)s' or increase the " +"rounding of '%(ref)s'." +msgstr "" +"La precisió d'arrodoniment de '%(uom)s' (%(uom_rounding)s) és massa " +"gruixuda per al seu ràtio de conversió (%(factor)s). En convertir a la " +"unitat de referència '%(ref)s', l'arrodoniment efectiu és %(effective)s, " +"que excedeix l'arrodoniment de referència de %(ref_rounding)s.\n" +"\n" +"Per solucionar-ho, disminuïu l'arrodoniment de '%(uom)s' o augmenteu " +"l'arrodoniment de '%(ref)s'." diff --git a/uom_rounding_coherence/i18n/es.po b/uom_rounding_coherence/i18n/es.po new file mode 100644 index 000000000..2b3a50e6a --- /dev/null +++ b/uom_rounding_coherence/i18n/es.po @@ -0,0 +1,47 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * uom_rounding_coherence +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-02-17 00:00+0000\n" +"PO-Revision-Date: 2026-02-17 00:00+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: uom_rounding_coherence +#: code:addons/uom_rounding_coherence/models/uom_uom.py:0 +#, python-format +msgid "" +"Cannot validate rounding coherence for category '%(category)s': expected " +"exactly one active reference unit of measure but found %(count)s." +msgstr "" +"No se puede validar la coherencia de redondeo para la categoría " +"'%(category)s': se esperaba exactamente una unidad de medida de referencia " +"activa pero se encontraron %(count)s." + +#. module: uom_rounding_coherence +#: code:addons/uom_rounding_coherence/models/uom_uom.py:0 +#, python-format +msgid "" +"The rounding precision of '%(uom)s' (%(uom_rounding)s) is too coarse for " +"its conversion ratio (%(factor)s). When converted to the reference unit " +"'%(ref)s', the effective rounding becomes %(effective)s, which exceeds the " +"reference rounding of %(ref_rounding)s.\n" +"\n" +"To fix this, either decrease the rounding of '%(uom)s' or increase the " +"rounding of '%(ref)s'." +msgstr "" +"La precisión de redondeo de '%(uom)s' (%(uom_rounding)s) es demasiado " +"gruesa para su ratio de conversión (%(factor)s). Al convertir a la unidad " +"de referencia '%(ref)s', el redondeo efectivo es %(effective)s, que excede " +"el redondeo de referencia de %(ref_rounding)s.\n" +"\n" +"Para solucionarlo, disminuya el redondeo de '%(uom)s' o aumente el " +"redondeo de '%(ref)s'." diff --git a/uom_rounding_coherence/models/__init__.py b/uom_rounding_coherence/models/__init__.py new file mode 100644 index 000000000..7753e8e76 --- /dev/null +++ b/uom_rounding_coherence/models/__init__.py @@ -0,0 +1 @@ +from . import uom_uom diff --git a/uom_rounding_coherence/models/uom_uom.py b/uom_rounding_coherence/models/uom_uom.py new file mode 100644 index 000000000..963ed116d --- /dev/null +++ b/uom_rounding_coherence/models/uom_uom.py @@ -0,0 +1,83 @@ +# Copyright 2026 NuoBiT Solutions - Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import _, api, models, tools +from odoo.exceptions import ValidationError + + +class UoM(models.Model): + _inherit = "uom.uom" + + @api.constrains("rounding", "factor", "uom_type", "category_id") + def _check_rounding_factor_coherence(self): + """Ensure UoM rounding is fine enough relative to the conversion ratio. + + When converting quantities between UoMs in the same category, + a non-reference UoM whose rounding is too coarse relative to + its conversion factor will lose precision beyond the reference + UoM's rounding. For example, if the reference UoM has rounding + 0.001 and a secondary UoM with ratio 1.141 has rounding 0.01, + each conversion can introduce up to ±0.004 error in reference + units — enough to accumulate visible discrepancies over + multiple transactions. + + The check: converting the UoM's rounding step to reference units + (rounding / factor) must not exceed the reference UoM's rounding. + """ + categories = self.mapped("category_id") + for category in categories: + uoms = self.env["uom.uom"].search( + [ + ("category_id", "=", category.id), + ("active", "=", True), + ] + ) + ref_uom = uoms.filtered(lambda u: u.uom_type == "reference") + if len(ref_uom) != 1: + raise ValidationError( + _( + "Cannot validate rounding coherence for " + "category '%(category)s': expected exactly one " + "active reference unit of measure but found " + "%(count)s." + ) + % { + "category": category.name, + "count": len(ref_uom), + } + ) + for uom in uoms - ref_uom: + # Convert this UoM's rounding to reference units + # Using the same formula as _compute_quantity: + # amount = qty / self.factor * to_unit.factor + # Since ref_uom.factor = 1.0: + # rounding_in_ref = uom.rounding / uom.factor + rounding_in_ref = uom.rounding / uom.factor + if ( + tools.float_compare( + rounding_in_ref, + ref_uom.rounding, + precision_rounding=ref_uom.rounding, + ) + > 0 + ): + raise ValidationError( + _( + "The rounding precision of '%(uom)s' (%(uom_rounding)s) " + "is too coarse for its conversion ratio (%(factor)s). " + "When converted to the reference unit '%(ref)s', " + "the effective rounding becomes %(effective)s, " + "which exceeds the reference rounding of " + "%(ref_rounding)s.\n\n" + "To fix this, either decrease the rounding of " + "'%(uom)s' or increase the rounding of '%(ref)s'." + ) + % { + "uom": uom.name, + "uom_rounding": uom.rounding, + "factor": uom.factor, + "ref": ref_uom.name, + "effective": rounding_in_ref, + "ref_rounding": ref_uom.rounding, + } + ) diff --git a/uom_rounding_coherence/readme/CONFIGURE.rst b/uom_rounding_coherence/readme/CONFIGURE.rst new file mode 100644 index 000000000..b7c42d38c --- /dev/null +++ b/uom_rounding_coherence/readme/CONFIGURE.rst @@ -0,0 +1,3 @@ +No configuration is needed. The module automatically validates UoM rounding +coherence when you create or modify UoMs. + diff --git a/uom_rounding_coherence/readme/CONTRIBUTORS.rst b/uom_rounding_coherence/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..3bbfec70a --- /dev/null +++ b/uom_rounding_coherence/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `NuoBiT `__: + + * Eric Antones + diff --git a/uom_rounding_coherence/readme/DESCRIPTION.rst b/uom_rounding_coherence/readme/DESCRIPTION.rst new file mode 100644 index 000000000..4e9541f14 --- /dev/null +++ b/uom_rounding_coherence/readme/DESCRIPTION.rst @@ -0,0 +1,21 @@ +This module adds validation to ensure that Unit of Measure (UoM) rounding +precision is coherent with the conversion ratio to the reference unit, +preventing precision loss during conversions. + +When converting quantities between UoMs in the same category, a non-reference +UoM whose rounding is too coarse relative to its conversion factor will lose +precision beyond the reference UoM's rounding. + +**Example Problem:** + +If the reference UoM has rounding 0.001 and a secondary UoM with ratio 1.141 +has rounding 0.01, each conversion can introduce up to ±0.004 error in +reference units — enough to accumulate visible discrepancies over multiple +transactions. + +**The Validation:** + +The module checks that converting the UoM's rounding step to reference units +(rounding / factor) does not exceed the reference UoM's rounding. This ensures +data consistency and prevents precision-related errors. + diff --git a/uom_rounding_coherence/readme/USAGE.rst b/uom_rounding_coherence/readme/USAGE.rst new file mode 100644 index 000000000..e321a6e48 --- /dev/null +++ b/uom_rounding_coherence/readme/USAGE.rst @@ -0,0 +1,19 @@ +When creating or modifying Units of Measure: + +#. Go to *Inventory > Configuration > UoM Categories* +#. Create or edit a UoM +#. If the rounding precision is too coarse for the conversion ratio, + the system will show a validation error +#. The error message will indicate: + + * The UoM with the problem + * Its current rounding value + * The conversion factor + * The effective rounding in reference units + * The reference unit and its rounding + +#. To fix the error, either: + + * Decrease the rounding of the problematic UoM, or + * Increase the rounding of the reference UoM (if appropriate) + diff --git a/uom_rounding_coherence/static/description/icon.png b/uom_rounding_coherence/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1cd641e792c30455187ca30940bc0f329ce8bbb0 GIT binary patch literal 6342 zcmd^^hf`C}*TzHWpfm-MZa|7OjYtjE(4`3pP0Eid3J8V{0s)mKJ<q zp^9|rp$mb~2}po9-@oIXJG(oxcjoS%d!O@s&d!Z9HP*e##KQyt0IurmK_64bp8pyH z9i^|ds>-JfbWVo4P{8GX*QeIfbjl2)kDfIG0ALvZuTgp2ZfK=U();NfY11z-vM>r= zo6RyI007+P`cO@apy}VqnaiVCLL`CEUGVGYE&5WpdhhbZv%|*-Y|2t(4~Cq|y`-Nmm-W zxaTf4+R69rVU1b%qjm?yu*PFgHFYd#J82-D8cpXqO&omwG2*Hd6ZIUiK@+ zNCo8Lg{1^vn^0ZQgz*~*ZR3wsULxnnSBN%7p()3EYs>sX9In)T{*nJ2q*qxXPNhFk z=z=+?4VOOdAF!ZYAVisYzF29g?udLQJtx@=HoAK_Kjx;4SO7>H_v*McB7(}RHMa> z+PNao{Hw&Mjo0P}CBR&l(k@iIeRI@PRH6R9^lR3e?TL?ZHra#GHvKmkeVBHG8nv4{ zz$nHGR7`D$ae@TrcXCSA=$~Yvp@J|bKul>6s-`yT7>JaM5?KcltZ)(ilt^74fqLA{ z1k!bKw(GMV*AOgI*glG_($h!cZgArkEAa1SkSG`0yF8JLWTq^J->2CRaqKH1ZSQt7 z29|+OBS3Rj91K1XL~_9&zn1p z)2Ez)&{9Of1X#b+mpgJ`{gurrlYqKrwrWXTOH{M%kEUhcgSp1J2FK4FF`JS|NfaAA6)?-&1}B`@lI2~kKWK) zhQ|}GQ$j(rNS}9?Yu9}MzWxz*HMwR=u8$RYY6sr2pu3x5Yx*P!Z&c|X zFZcC{+kqJV=XTZH=cMb6)MtgWo%C~XU8TEXDKx9;0hEV*74Z6i8vuzXp zw<8QvI~;n;3@<^G0C#HHf2{N6E~2DO3jw!?w}z?_vV6Q>?kJ>IF-kEc*TtP}k7cVd zvtdPgQ^jWhMXAL$Lqn!_A_IL+!hbY37)n@Sqc)6JwD4)3LP`up1cy^EXzh>B{$ce0 zgX~Iat{I@DM|zU|>9DuD?g}h7zCqV;o1*~3Hr=DYjDq;SG?3HS)(x+l@HAa-@>5wH zhw`oqg>hP$e41h5)>$#qFWq?LGX`dC8ph`RyR&_z&og>psSHzZ=_8<-M4yk+3HK-+ zxqe%Ntx88}49jJazM_Vov;)83cSeeLv@taHOL>zP>~bqdmEyfHl9M%`@ivb|7{I;N zzyHw9P7EH0$ww52RejJv>zvSr8v*iuX@X;(Z~NuUv$D0I_>OkcZWSulBUJjHUN=n| zSI$q@$)`(E;^(|}q|2utYl8}>IcXkPX#{6Z%JnhUBly1B@B}sECm2Y88-QrQZd2n2 zKL=1_&Z87xM=GaycA-Ac*R<^bJk>-^k%lt;DjswC+AM`71*2iG?;!3Bc)I>55v)^C zkt+Uzn&dhv|58XAY6{%ybSiVMl-sATTy=SUADQWD+(@-AVqg@Y+_fBV$LJnIEfujI4B5%4a@8S4M*50Lh7NqKSW>K=U5dW@)Hd{^oR4v% zCM2(rAq7Qe-)R0ko{l@iCHGsxhkCNWby zf&gByp!>=?r1ecWMqz5e-BmOED6n!_1V4<)R!!QNwM!AyGty8>p>ebEzdp*_(kAYA z5*F^g_K}%Rm;V}4Q46qJpU+&3bU10WYg{j`T>lv9{B)J}RHC}yzy9x)wm4ju23yQ& zUNm(i_(ChqD8d7AVUFMw zXmia0A{l#}Sfq!GmHjatiTk$f|OvS0iG>W{p<8cZu^6HX`rMuX?l8<+?WVAW6 z3!MLV*VOFpd&STaeN2qdwU* zk1ni(wdh{`{hLj-hCz&59jVIp~SmgtSQDf!FrPYKIF6_c_NJr zn<-BdXVU}OSE{-No~b(6tG)250`-S%YB9Si@&}{d@FUGqjcNE@SlSdG`}H-#!~M1& z;{E-SKUBb6)KwP1XB|S8MB=F>9k$#1$|^*t%%5zq#(35~S#+TgC^oj&COt~T>axhU0t zQff{8Jt+NH^_pqPzec@Iv#L^r?qs$jdiCY&xOU2pve78Pc{a8y+D;2N0aEJe5d#uL}ZkkYQ&XA;NK5v>r@NUaj=<_V$*Ll@&CF!{LWI zh@|EE!!M(B5qeQ40YHy86TVkX6Te=v4ytV_-JnKl93#Z9clghd^lywoBtgj)4%mxKR<#pH0*hxyHFQNJ zGW`7CtD9C6)ehKni=#!gKj#ZO7L$d_i4nJZhR!z$B(rX9j$$L8X1>~^2By%Dp*IJj z8QiI6*w*|IoF{UpFaD{!PWdOxja{DQq9?BK%2(Xuh#Tv2s_ELIvb@YAd{Af)Lph(9 z>DTXZ`|*!Jnw)?`BzPrdYx(?S2&<(1>1>-f=c}gi8^)=KW973rikh?!-B$fOy@x-Rd+?x= zM(0SbmCz!gY#)CqB9J_^v4K$urOnoj|E||~D>%ndVMwe)ef3BuZH0l!Z&M@fyN}{1 zD;n{juZF|*{lehy$NlM{B`Q0Z18O|&=wX!Nt*rLKfak}ww{ zJ$9BJA3Tq4n~%w3V$0UA(+PgZ#j-35$=_xzuk(w5o2f(WOCu%+h>cg3B*aqaQdfeQ zj@VutKTWtH8{S+}vR3Z`KIQl-h!4tFi1vG-Kuh^Lb0N=LN0+1ZP!WL39=Age)HS_E z8khUbE>xA^59Nmj`B0@u0IR<04wqF@ssF4AP6ZVhslN61xT#8o@ymhOWJ5zkUQN07 zyDEYVZ4#Z$(%wnd04Y_^B_4gjFoKPWgD&OUsj^ezcuXa}E4yjc@xi#az zyRy6>?#h2*VNdNO_jYQ1{@qaYoN7moT}cnd8cmK*&R@SeSYZgIBaJklh!n-3#3dyO z!@*@06=Y8#wl9|Bj3=C0Fi!SfzVz7$Stc4_Q`K2P?2|gT!JIBhc*P&-IkB?Mb5I&% z%BN*TF#vYzIW>)|=X`Chr};G5EZXg?_yvlDC|f%AP!ty{i{{pXQnHm<^|{P$D; z9ZAW#l9Cd2($R5@*5}FeUd#l;N11WwITb1nJSm8r@`#sXHPsuq!3S2&h>U)y=3MjV;j3oWLY>5EOvuruXC*WH2G){378-0tpcMF}1(^PSWUe>XEJN%5 zl|m59cX=GC{^$_E-4Wm1=5|!;Ek&{<4lIOt5M&GMq=+JQdyt?WI#6C!)i!s4;k9T0 z{;`B*>VQ%iU)>Zbhgb4|vd=Wy4>107#gyeqi^+-^2E~0Ja&rFpRb<)oirMj4-KuLg zSo1*y98TZlD<3^A&^bRESh~S*Lzqn0l;JfX-fdjA`M#a!@?b?zWdEr3mIiqS{m2J% z3nWGoQG6+FQ~&gQF-DLGWF}WfwHL(4$EUt(5Jcx#l79K-x~qdu!_gs;XaP0`8m(8a z2J#B{UvEhLT=w9*(6bFWp{9CI=Z&Hh)e}}1hnK6fPlSYqu4H|>g|Erg5fVWl5w&~Kdf{3+V{dCaNhFDg<~sELf1dC($hw|SmSkZ zKD6>nsj6Q+aHEZDHC9{UJxPZ9y{6)F5hg5bm*}ihsxQxj~`xNo%QnaTEJn)f#{CK-H5HYAM7kK zL!XvElM^Y!yC=uSu54Gj zTEgKhtTCOqx1EcIl=VA7`!xLiUj%p*eH??_??@gOJJxVX)#(G`=31lw3whFi2Y7Mq z1bXLvi+~U5E4R{v15H@yQI@=d!V9LD&P!p?0u7L&Rg=D<<*+ zouj?2?aYI{Ac%Gx!r&EkXmmvR`!Xl?06WsGs_Ts8ojW?id!X$>C}@~q>BMfGeGohw zkR}NImw2grp7>W(5s*(iPYn$1*t@i%(W7u#6m}l)%TmD-221>N?VBna!@FO-7!xjM z{`_^-yt<@e?fK$Sqzc7O%3&~A>HB|stQr64jx(U3y+}d}vp(r7c=iB8>t~T7HmYg1qJe4SLo$e62=EZUuFS7UqbSP}M^@%aI7g!ztzj{)_R0x*X6OMLAky)_Sv&%2DNGv zxH}pEr{gEYf&ZF&RJoII9*=yd^~fxKtFc@1f_3}Vqqi8_U?;lC`7etN$3$u0dW+-%7P zQ~iX&gr(5xd1M>3yrzZav9ZLIhbS&|=U$t!9iq*i5vy)(RsBw0TU#?~zdTKUXjyIl z%7Q)Vp}YoU$acz-9y_`%Oig!%TPyC=ie3*Qut3@4V`+A4d<*f%jOx>*bX%#Ao+@wM z;NW0DZKvmp%_oxvFw2#S9r8Sc?wXh}`3gVG`rBKr&jpxwTRQ7WtKY06QQVhs$u$!e zs;Y%~2xwpH*9vxfQ~q#gAwn+P+=YE(L>|P(Fl&H27@?);kUI4FW%LjHZKYGk#f~@3 zXW;a;3+{&c`g+uCR+``$V9)N#RBCk_#RQ(K-PxlQ7Ym;XdCqGn$j%JmAwgtkWKn1} z8^>3&)Q05VbBm+t`9B_${w9F7WfM{Jvawk;HDc*{Sa_Sla|zqX!vbKV%>gB|z6BCc z8_bdnPnzloGP1I)!^5hnC6CLZUU`;nO2NF2)FaAkYhQL$Z58+`p75dj7RKse#Z!uacCm z0@|m~U!QZOdb|V~`ktFK4;lg_ZOCjFXeV4`jGj&bh7Q6BEyN8~yGd*JyzwFbIRaAf z#KG$rvQxWFvqwn`i6jBQ?6o+k+oOC)Gj9ChlgabiScr};b5|opxUYjCZOwmhjTj6W zFzJt_htTuopW4IRiQ}r0L}`w=pE{HN<@(9Hl11P5cHmN6A1F^sg2OWXcw<+q2x>I5 zq9Bu>PBob6#^vrr<|IC)m+zJpFRRcCVsqbspNybriu&!R=H^@RcG#aBGz9RH}ZI=>4 zi(m?IA?Vr$Q7?wN6ZW7H`S?3}K8=$7J5MjWKri=_igw1%J?0~*6e_Ii*1&23dGcF} z&=vaMgF!^veGQ1f$3k?WK5Jaw%==+Bb!tI6zQ68&-dQ3Orl+Tqh#Nt?dBEV_w^wkjY+qJ+X*NCMs%J-Lc4%}pKryM#O)O&9 un*HHVB-AlUN`suyDkKONktc!@Ievk;6wT20MOSqhE{1gM*SZGeqiYU literal 0 HcmV?d00001 diff --git a/uom_rounding_coherence/static/description/index.html b/uom_rounding_coherence/static/description/index.html new file mode 100644 index 000000000..013f60b10 --- /dev/null +++ b/uom_rounding_coherence/static/description/index.html @@ -0,0 +1,464 @@ + + + + + +UoM Rounding Coherence + + + +
+

UoM Rounding Coherence

+ + +

Beta License: AGPL-3 NuoBiT/odoo-addons

+

This module adds validation to ensure that Unit of Measure (UoM) rounding +precision is coherent with the conversion ratio to the reference unit, +preventing precision loss during conversions.

+

When converting quantities between UoMs in the same category, a non-reference +UoM whose rounding is too coarse relative to its conversion factor will lose +precision beyond the reference UoM’s rounding.

+

Example Problem:

+

If the reference UoM has rounding 0.001 and a secondary UoM with ratio 1.141 +has rounding 0.01, each conversion can introduce up to ±0.004 error in +reference units — enough to accumulate visible discrepancies over multiple +transactions.

+

The Validation:

+

The module checks that converting the UoM’s rounding step to reference units +(rounding / factor) does not exceed the reference UoM’s rounding. This ensures +data consistency and prevents precision-related errors.

+

Table of contents

+ +
+

Configuration

+

No configuration is needed. The module automatically validates UoM rounding +coherence when you create or modify UoMs.

+
+
+

Usage

+

When creating or modifying Units of Measure:

+
    +
  1. Go to Inventory > Configuration > UoM Categories
  2. +
  3. Create or edit a UoM
  4. +
  5. If the rounding precision is too coarse for the conversion ratio, +the system will show a validation error
  6. +
  7. The error message will indicate:
      +
    • The UoM with the problem
    • +
    • Its current rounding value
    • +
    • The conversion factor
    • +
    • The effective rounding in reference units
    • +
    • The reference unit and its rounding
    • +
    +
  8. +
  9. To fix the error, either:
      +
    • Decrease the rounding of the problematic UoM, or
    • +
    • Increase the rounding of the reference UoM (if appropriate)
    • +
    +
  10. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • NuoBiT Solutions
  • +
  • S.L.
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the NuoBiT/odoo-addons project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/uom_rounding_coherence/tests/__init__.py b/uom_rounding_coherence/tests/__init__.py new file mode 100644 index 000000000..5a50ffdb8 --- /dev/null +++ b/uom_rounding_coherence/tests/__init__.py @@ -0,0 +1 @@ +from . import test_uom_rounding_coherence diff --git a/uom_rounding_coherence/tests/test_uom_rounding_coherence.py b/uom_rounding_coherence/tests/test_uom_rounding_coherence.py new file mode 100644 index 000000000..8f1638945 --- /dev/null +++ b/uom_rounding_coherence/tests/test_uom_rounding_coherence.py @@ -0,0 +1,302 @@ +# Copyright 2026 NuoBiT Solutions - Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.exceptions import ValidationError +from odoo.tests import SavepointCase + + +class TestUomRoundingCoherence(SavepointCase): + @classmethod + def setUpClass(cls): + super(TestUomRoundingCoherence, cls).setUpClass() + cls.category = cls.env["uom.category"].create( + {"name": "Test Coherence Category"} + ) + cls.ref_uom = cls.env["uom.uom"].create( + { + "name": "Ref Unit", + "category_id": cls.category.id, + "uom_type": "reference", + "rounding": 0.001, + } + ) + + def test_coherent_rounding_passes(self): + """ + PRE: - A reference UoM exists with rounding 0.001 + ACT: - Create a smaller UoM with factor=1.141 and rounding=0.001 + POST: - The UoM is created without error because + effective rounding (0.001 / 1.141 ≈ 0.000877) <= ref rounding (0.001) + """ + # ARRANGE & ACT + self.env["uom.uom"].create( + { + "name": "Fine Unit", + "category_id": self.category.id, + "uom_type": "smaller", + "factor": 1.141, + "rounding": 0.001, + } + ) + + def test_coarse_rounding_fails(self): + """ + PRE: - A reference UoM exists with rounding 0.001 + ACT: - Create a smaller UoM with factor=1.141 and rounding=0.01 + POST: - ValidationError is raised because + effective rounding (0.01 / 1.141 ≈ 0.00877) > ref rounding (0.001) + """ + # ARRANGE & ACT & ASSERT + with self.assertRaises(ValidationError): + self.env["uom.uom"].create( + { + "name": "Coarse Unit", + "category_id": self.category.id, + "uom_type": "smaller", + "factor": 1.141, + "rounding": 0.01, + } + ) + + def test_borderline_rounding_passes(self): + """ + PRE: - A reference UoM exists with rounding 0.0001 + ACT: - Create a smaller UoM with factor=0.8086 and rounding=0.0001 + POST: - The UoM is created without error because + effective rounding (0.0001 / 0.8086 ≈ 0.0001237) is within + float_compare tolerance of ref rounding (0.0001) + """ + # ARRANGE + category = self.env["uom.category"].create({"name": "Test Borderline Category"}) + self.env["uom.uom"].create( + { + "name": "Ref Borderline", + "category_id": category.id, + "uom_type": "reference", + "rounding": 0.0001, + } + ) + # ACT + self.env["uom.uom"].create( + { + "name": "Borderline Unit", + "category_id": category.id, + "uom_type": "smaller", + "factor": 0.8086, + "rounding": 0.0001, + } + ) + + def test_update_rounding_to_coarse_fails(self): + """ + PRE: - A reference UoM exists with rounding 0.001 + - A smaller UoM exists with factor=1.141 and rounding=0.001 + ACT: - Update the smaller UoM rounding to 0.01 + POST: - ValidationError is raised because + effective rounding (0.01 / 1.141 ≈ 0.00877) > ref rounding (0.001) + """ + # ARRANGE + uom = self.env["uom.uom"].create( + { + "name": "Update Test Unit", + "category_id": self.category.id, + "uom_type": "smaller", + "factor": 1.141, + "rounding": 0.001, + } + ) + # ACT & ASSERT + with self.assertRaises(ValidationError): + uom.write({"rounding": 0.01}) + + def test_update_ref_rounding_to_finer_fails(self): + """ + PRE: - A reference UoM exists with rounding 0.01 + - A smaller UoM exists with factor=1.141 and rounding=0.01 + (effective rounding 0.01 / 1.141 ≈ 0.00877 <= 0.01: OK) + ACT: - Update the reference UoM rounding to 0.001 + POST: - ValidationError is raised because + effective rounding (0.01 / 1.141 ≈ 0.00877) > new ref rounding (0.001) + """ + # ARRANGE + category = self.env["uom.category"].create({"name": "Test Ref Update Category"}) + ref = self.env["uom.uom"].create( + { + "name": "Ref Updatable", + "category_id": category.id, + "uom_type": "reference", + "rounding": 0.01, + } + ) + self.env["uom.uom"].create( + { + "name": "Secondary", + "category_id": category.id, + "uom_type": "smaller", + "factor": 1.141, + "rounding": 0.01, + } + ) + # ACT & ASSERT + with self.assertRaises(ValidationError): + ref.write({"rounding": 0.001}) + + def test_bigger_uom_coarse_rounding_fails(self): + """ + PRE: - A reference UoM exists with rounding 0.001 + ACT: - Create a bigger UoM with factor=0.001 and rounding=1.0 + POST: - ValidationError is raised because + effective rounding (1.0 / 0.001 = 1000) >> ref rounding (0.001) + """ + # ARRANGE & ACT & ASSERT + with self.assertRaises(ValidationError): + self.env["uom.uom"].create( + { + "name": "Big Coarse Unit", + "category_id": self.category.id, + "uom_type": "bigger", + "factor": 0.001, + "rounding": 1.0, + } + ) + + def test_bigger_uom_coherent_rounding_passes(self): + """ + PRE: - A reference UoM exists with rounding 0.001 + ACT: - Create a bigger UoM with factor=0.001 and rounding=0.000001 + POST: - The UoM is created without error because + effective rounding (0.000001 / 0.001 = 0.001) <= ref rounding (0.001) + """ + # ARRANGE & ACT + self.env["uom.uom"].create( + { + "name": "Big Fine Unit", + "category_id": self.category.id, + "uom_type": "bigger", + "factor": 0.001, + "rounding": 0.000001, + } + ) + + def test_bigger_uom_integer_ratio_passes(self): + """ + PRE: - A reference UoM exists with rounding 0.001 + ACT: - Create a bigger UoM with factor=1/12 (dozen) and rounding=0.00001 + POST: - The UoM is created without error because + effective rounding (0.00001 / 0.08333 ≈ 0.00012) <= ref rounding (0.001) + """ + # ARRANGE & ACT + self.env["uom.uom"].create( + { + "name": "Dozen", + "category_id": self.category.id, + "uom_type": "bigger", + "factor": 0.08333333333, # 1/12 + "rounding": 0.00001, + } + ) + + def test_reference_only_category_passes(self): + """ + PRE: - A category exists with only a reference UoM + ACT: - Update the reference UoM rounding + POST: - No error is raised because there are no non-reference + UoMs to validate against + """ + # ARRANGE + category = self.env["uom.category"].create( + {"name": "Test Reference Only Category"} + ) + ref = self.env["uom.uom"].create( + { + "name": "Ref Only", + "category_id": category.id, + "uom_type": "reference", + "rounding": 0.01, + } + ) + # ACT + ref.write({"rounding": 0.001}) + + def test_no_active_reference_uom_fails(self): + """ + PRE: - A category exists with a reference UoM and a smaller UoM + ACT: - Archive the reference UoM via SQL (bypassing ORM constraints) + and update the non-reference UoM rounding to trigger validation + POST: - ValidationError is raised because no active reference UoM + is found to validate against + """ + # ARRANGE + category = self.env["uom.category"].create( + {"name": "Test No Active Ref Category"} + ) + ref = self.env["uom.uom"].create( + { + "name": "Ref To Deactivate", + "category_id": category.id, + "uom_type": "reference", + "rounding": 0.001, + } + ) + non_ref = self.env["uom.uom"].create( + { + "name": "Orphan Unit", + "category_id": category.id, + "uom_type": "smaller", + "factor": 2.0, + "rounding": 0.001, + } + ) + self.env.cr.execute( + "UPDATE uom_uom SET active = FALSE WHERE id = %s", (ref.id,) + ) + self.env["uom.uom"].invalidate_cache() + # ACT & ASSERT + with self.assertRaises(ValidationError): + non_ref.write({"rounding": 0.0001}) + + def test_multiple_active_reference_uoms_fails(self): + """ + PRE: - A category exists with a reference UoM and two smaller UoMs + ACT: - Change one smaller UoM to reference type via SQL + (bypassing ORM constraints) and update the other smaller + UoM rounding to trigger validation + POST: - ValidationError is raised because multiple active reference + UoMs are found + """ + # ARRANGE + category = self.env["uom.category"].create({"name": "Test Multi Ref Category"}) + self.env["uom.uom"].create( + { + "name": "Ref Original", + "category_id": category.id, + "uom_type": "reference", + "rounding": 0.001, + } + ) + to_corrupt = self.env["uom.uom"].create( + { + "name": "Unit To Corrupt", + "category_id": category.id, + "uom_type": "smaller", + "factor": 2.0, + "rounding": 0.001, + } + ) + another = self.env["uom.uom"].create( + { + "name": "Another Unit", + "category_id": category.id, + "uom_type": "smaller", + "factor": 3.0, + "rounding": 0.001, + } + ) + self.env.cr.execute( + "UPDATE uom_uom SET uom_type = 'reference', factor = 1.0 WHERE id = %s", + (to_corrupt.id,), + ) + self.env["uom.uom"].invalidate_cache() + # ACT & ASSERT + with self.assertRaises(ValidationError): + another.write({"rounding": 0.0001}) From 0ef1c94e345ac222cf58539cdc0e41f1f0cfb294 Mon Sep 17 00:00:00 2001 From: ??? Date: Wed, 11 Mar 2026 12:33:08 +0100 Subject: [PATCH 2/3] [IMP] uom_rounding_coherence: pre-commit auto fixes --- uom_rounding_coherence/README.rst | 77 +++++++++---------- uom_rounding_coherence/__manifest__.py | 2 +- uom_rounding_coherence/pyproject.toml | 3 + .../readme/{CONFIGURE.rst => CONFIGURE.md} | 5 +- uom_rounding_coherence/readme/CONTRIBUTORS.md | 2 + .../readme/CONTRIBUTORS.rst | 4 - uom_rounding_coherence/readme/DESCRIPTION.md | 21 +++++ uom_rounding_coherence/readme/DESCRIPTION.rst | 21 ----- uom_rounding_coherence/readme/USAGE.md | 15 ++++ uom_rounding_coherence/readme/USAGE.rst | 19 ----- .../static/description/index.html | 41 +++++----- .../tests/test_uom_rounding_coherence.py | 2 +- 12 files changed, 103 insertions(+), 109 deletions(-) create mode 100644 uom_rounding_coherence/pyproject.toml rename uom_rounding_coherence/readme/{CONFIGURE.rst => CONFIGURE.md} (53%) create mode 100644 uom_rounding_coherence/readme/CONTRIBUTORS.md delete mode 100644 uom_rounding_coherence/readme/CONTRIBUTORS.rst create mode 100644 uom_rounding_coherence/readme/DESCRIPTION.md delete mode 100644 uom_rounding_coherence/readme/DESCRIPTION.rst create mode 100644 uom_rounding_coherence/readme/USAGE.md delete mode 100644 uom_rounding_coherence/readme/USAGE.rst diff --git a/uom_rounding_coherence/README.rst b/uom_rounding_coherence/README.rst index 9e296586c..6f2f96279 100644 --- a/uom_rounding_coherence/README.rst +++ b/uom_rounding_coherence/README.rst @@ -17,32 +17,32 @@ UoM Rounding Coherence :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-NuoBiT%2Fodoo--addons-lightgray.png?logo=github - :target: https://github.com/NuoBiT/odoo-addons/tree/14.0/uom_rounding_coherence + :target: https://github.com/NuoBiT/odoo-addons/tree/18.0/uom_rounding_coherence :alt: NuoBiT/odoo-addons |badge1| |badge2| |badge3| -This module adds validation to ensure that Unit of Measure (UoM) rounding -precision is coherent with the conversion ratio to the reference unit, -preventing precision loss during conversions. +This module adds validation to ensure that Unit of Measure (UoM) +rounding precision is coherent with the conversion ratio to the +reference unit, preventing precision loss during conversions. -When converting quantities between UoMs in the same category, a non-reference -UoM whose rounding is too coarse relative to its conversion factor will lose -precision beyond the reference UoM's rounding. +When converting quantities between UoMs in the same category, a +non-reference UoM whose rounding is too coarse relative to its +conversion factor will lose precision beyond the reference UoM's +rounding. **Example Problem:** -If the reference UoM has rounding 0.001 and a secondary UoM with ratio 1.141 -has rounding 0.01, each conversion can introduce up to ±0.004 error in -reference units — enough to accumulate visible discrepancies over multiple -transactions. +If the reference UoM has rounding 0.001 and a secondary UoM with ratio +1.141 has rounding 0.01, each conversion can introduce up to ±0.004 +error in reference units — enough to accumulate visible discrepancies +over multiple transactions. **The Validation:** -The module checks that converting the UoM's rounding step to reference units -(rounding / factor) does not exceed the reference UoM's rounding. This ensures -data consistency and prevents precision-related errors. - +The module checks that converting the UoM's rounding step to reference +units (rounding / factor) does not exceed the reference UoM's rounding. +This ensures data consistency and prevents precision-related errors. **Table of contents** @@ -52,32 +52,30 @@ data consistency and prevents precision-related errors. Configuration ============= -No configuration is needed. The module automatically validates UoM rounding -coherence when you create or modify UoMs. - +No configuration is needed. The module automatically validates UoM +rounding coherence when you create or modify UoMs. Usage ===== When creating or modifying Units of Measure: -#. Go to *Inventory > Configuration > UoM Categories* -#. Create or edit a UoM -#. If the rounding precision is too coarse for the conversion ratio, - the system will show a validation error -#. The error message will indicate: +1. Go to *Inventory > Configuration > UoM Categories* +2. Create or edit a UoM +3. If the rounding precision is too coarse for the conversion ratio, the + system will show a validation error +4. The error message will indicate: - * The UoM with the problem - * Its current rounding value - * The conversion factor - * The effective rounding in reference units - * The reference unit and its rounding + - The UoM with the problem + - Its current rounding value + - The conversion factor + - The effective rounding in reference units + - The reference unit and its rounding -#. To fix the error, either: - - * Decrease the rounding of the problematic UoM, or - * Increase the rounding of the reference UoM (if appropriate) +5. To fix the error, either: + - Decrease the rounding of the problematic UoM, or + - Increase the rounding of the reference UoM (if appropriate) Bug Tracker =========== @@ -85,7 +83,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -93,22 +91,21 @@ Credits ======= Authors -~~~~~~~ +------- * NuoBiT Solutions * S.L. Contributors -~~~~~~~~~~~~ - -* `NuoBiT `__: +------------ - * Eric Antones +- `NuoBiT `__: + - Eric Antones Maintainers -~~~~~~~~~~~ +----------- -This module is part of the `NuoBiT/odoo-addons `_ project on GitHub. +This module is part of the `NuoBiT/odoo-addons `_ project on GitHub. You are welcome to contribute. diff --git a/uom_rounding_coherence/__manifest__.py b/uom_rounding_coherence/__manifest__.py index cfa7cd420..1ecb7071a 100644 --- a/uom_rounding_coherence/__manifest__.py +++ b/uom_rounding_coherence/__manifest__.py @@ -7,7 +7,7 @@ " with the conversion ratio to prevent precision loss", "version": "14.0.1.0.0", "author": "NuoBiT Solutions, S.L.", - "website": "https://github.com/nuobit/odoo-addons", + "website": "https://github.com/NuoBiT/odoo-addons", "category": "Product", "depends": ["uom"], "license": "AGPL-3", diff --git a/uom_rounding_coherence/pyproject.toml b/uom_rounding_coherence/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/uom_rounding_coherence/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/uom_rounding_coherence/readme/CONFIGURE.rst b/uom_rounding_coherence/readme/CONFIGURE.md similarity index 53% rename from uom_rounding_coherence/readme/CONFIGURE.rst rename to uom_rounding_coherence/readme/CONFIGURE.md index b7c42d38c..09d8c0726 100644 --- a/uom_rounding_coherence/readme/CONFIGURE.rst +++ b/uom_rounding_coherence/readme/CONFIGURE.md @@ -1,3 +1,2 @@ -No configuration is needed. The module automatically validates UoM rounding -coherence when you create or modify UoMs. - +No configuration is needed. The module automatically validates UoM +rounding coherence when you create or modify UoMs. diff --git a/uom_rounding_coherence/readme/CONTRIBUTORS.md b/uom_rounding_coherence/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..be221b732 --- /dev/null +++ b/uom_rounding_coherence/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- [NuoBiT](https://www.nuobit.com): + - Eric Antones \ diff --git a/uom_rounding_coherence/readme/CONTRIBUTORS.rst b/uom_rounding_coherence/readme/CONTRIBUTORS.rst deleted file mode 100644 index 3bbfec70a..000000000 --- a/uom_rounding_coherence/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1,4 +0,0 @@ -* `NuoBiT `__: - - * Eric Antones - diff --git a/uom_rounding_coherence/readme/DESCRIPTION.md b/uom_rounding_coherence/readme/DESCRIPTION.md new file mode 100644 index 000000000..f35a92e54 --- /dev/null +++ b/uom_rounding_coherence/readme/DESCRIPTION.md @@ -0,0 +1,21 @@ +This module adds validation to ensure that Unit of Measure (UoM) +rounding precision is coherent with the conversion ratio to the +reference unit, preventing precision loss during conversions. + +When converting quantities between UoMs in the same category, a +non-reference UoM whose rounding is too coarse relative to its +conversion factor will lose precision beyond the reference UoM's +rounding. + +**Example Problem:** + +If the reference UoM has rounding 0.001 and a secondary UoM with ratio +1.141 has rounding 0.01, each conversion can introduce up to ±0.004 +error in reference units — enough to accumulate visible discrepancies +over multiple transactions. + +**The Validation:** + +The module checks that converting the UoM's rounding step to reference +units (rounding / factor) does not exceed the reference UoM's rounding. +This ensures data consistency and prevents precision-related errors. diff --git a/uom_rounding_coherence/readme/DESCRIPTION.rst b/uom_rounding_coherence/readme/DESCRIPTION.rst deleted file mode 100644 index 4e9541f14..000000000 --- a/uom_rounding_coherence/readme/DESCRIPTION.rst +++ /dev/null @@ -1,21 +0,0 @@ -This module adds validation to ensure that Unit of Measure (UoM) rounding -precision is coherent with the conversion ratio to the reference unit, -preventing precision loss during conversions. - -When converting quantities between UoMs in the same category, a non-reference -UoM whose rounding is too coarse relative to its conversion factor will lose -precision beyond the reference UoM's rounding. - -**Example Problem:** - -If the reference UoM has rounding 0.001 and a secondary UoM with ratio 1.141 -has rounding 0.01, each conversion can introduce up to ±0.004 error in -reference units — enough to accumulate visible discrepancies over multiple -transactions. - -**The Validation:** - -The module checks that converting the UoM's rounding step to reference units -(rounding / factor) does not exceed the reference UoM's rounding. This ensures -data consistency and prevents precision-related errors. - diff --git a/uom_rounding_coherence/readme/USAGE.md b/uom_rounding_coherence/readme/USAGE.md new file mode 100644 index 000000000..904cd2d9a --- /dev/null +++ b/uom_rounding_coherence/readme/USAGE.md @@ -0,0 +1,15 @@ +When creating or modifying Units of Measure: + +1. Go to *Inventory \> Configuration \> UoM Categories* +2. Create or edit a UoM +3. If the rounding precision is too coarse for the conversion ratio, + the system will show a validation error +4. The error message will indicate: + - The UoM with the problem + - Its current rounding value + - The conversion factor + - The effective rounding in reference units + - The reference unit and its rounding +5. To fix the error, either: + - Decrease the rounding of the problematic UoM, or + - Increase the rounding of the reference UoM (if appropriate) diff --git a/uom_rounding_coherence/readme/USAGE.rst b/uom_rounding_coherence/readme/USAGE.rst deleted file mode 100644 index e321a6e48..000000000 --- a/uom_rounding_coherence/readme/USAGE.rst +++ /dev/null @@ -1,19 +0,0 @@ -When creating or modifying Units of Measure: - -#. Go to *Inventory > Configuration > UoM Categories* -#. Create or edit a UoM -#. If the rounding precision is too coarse for the conversion ratio, - the system will show a validation error -#. The error message will indicate: - - * The UoM with the problem - * Its current rounding value - * The conversion factor - * The effective rounding in reference units - * The reference unit and its rounding - -#. To fix the error, either: - - * Decrease the rounding of the problematic UoM, or - * Increase the rounding of the reference UoM (if appropriate) - diff --git a/uom_rounding_coherence/static/description/index.html b/uom_rounding_coherence/static/description/index.html index 013f60b10..69cee46b2 100644 --- a/uom_rounding_coherence/static/description/index.html +++ b/uom_rounding_coherence/static/description/index.html @@ -369,22 +369,23 @@

UoM Rounding Coherence

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:d646eddf3ef1d6ff89719e1651d153ab196194e731190ebbcaa2e135d9ff4a8e !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 NuoBiT/odoo-addons

-

This module adds validation to ensure that Unit of Measure (UoM) rounding -precision is coherent with the conversion ratio to the reference unit, -preventing precision loss during conversions.

-

When converting quantities between UoMs in the same category, a non-reference -UoM whose rounding is too coarse relative to its conversion factor will lose -precision beyond the reference UoM’s rounding.

+

Beta License: AGPL-3 NuoBiT/odoo-addons

+

This module adds validation to ensure that Unit of Measure (UoM) +rounding precision is coherent with the conversion ratio to the +reference unit, preventing precision loss during conversions.

+

When converting quantities between UoMs in the same category, a +non-reference UoM whose rounding is too coarse relative to its +conversion factor will lose precision beyond the reference UoM’s +rounding.

Example Problem:

-

If the reference UoM has rounding 0.001 and a secondary UoM with ratio 1.141 -has rounding 0.01, each conversion can introduce up to ±0.004 error in -reference units — enough to accumulate visible discrepancies over multiple -transactions.

+

If the reference UoM has rounding 0.001 and a secondary UoM with ratio +1.141 has rounding 0.01, each conversion can introduce up to ±0.004 +error in reference units — enough to accumulate visible discrepancies +over multiple transactions.

The Validation:

-

The module checks that converting the UoM’s rounding step to reference units -(rounding / factor) does not exceed the reference UoM’s rounding. This ensures -data consistency and prevents precision-related errors.

+

The module checks that converting the UoM’s rounding step to reference +units (rounding / factor) does not exceed the reference UoM’s rounding. +This ensures data consistency and prevents precision-related errors.

Table of contents

    @@ -401,8 +402,8 @@

    UoM Rounding Coherence

Configuration

-

No configuration is needed. The module automatically validates UoM rounding -coherence when you create or modify UoMs.

+

No configuration is needed. The module automatically validates UoM +rounding coherence when you create or modify UoMs.

Usage

@@ -410,8 +411,8 @@

Usage

  1. Go to Inventory > Configuration > UoM Categories
  2. Create or edit a UoM
  3. -
  4. If the rounding precision is too coarse for the conversion ratio, -the system will show a validation error
  5. +
  6. If the rounding precision is too coarse for the conversion ratio, the +system will show a validation error
  7. The error message will indicate:
    • The UoM with the problem
    • Its current rounding value
    • @@ -432,7 +433,7 @@

      Bug Tracker

      Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

      +feedback.

      Do not contact contributors directly about support or help with technical issues.

@@ -455,7 +456,7 @@

Contributors

Maintainers

-

This module is part of the NuoBiT/odoo-addons project on GitHub.

+

This module is part of the NuoBiT/odoo-addons project on GitHub.

You are welcome to contribute.

diff --git a/uom_rounding_coherence/tests/test_uom_rounding_coherence.py b/uom_rounding_coherence/tests/test_uom_rounding_coherence.py index 8f1638945..e357b2571 100644 --- a/uom_rounding_coherence/tests/test_uom_rounding_coherence.py +++ b/uom_rounding_coherence/tests/test_uom_rounding_coherence.py @@ -8,7 +8,7 @@ class TestUomRoundingCoherence(SavepointCase): @classmethod def setUpClass(cls): - super(TestUomRoundingCoherence, cls).setUpClass() + super().setUpClass() cls.category = cls.env["uom.category"].create( {"name": "Test Coherence Category"} ) From dfff7b04ca23a795d3645182976ca2223dcb26c3 Mon Sep 17 00:00:00 2001 From: ??? Date: Wed, 11 Mar 2026 13:10:05 +0100 Subject: [PATCH 3/3] [MIG] uom_rounding_coherence: Migration to 18.0 --- uom_rounding_coherence/README.rst | 6 +++--- uom_rounding_coherence/__manifest__.py | 8 ++++---- uom_rounding_coherence/i18n/ca.po | 2 +- uom_rounding_coherence/i18n/es.po | 2 +- uom_rounding_coherence/models/uom_uom.py | 2 +- uom_rounding_coherence/readme/CONTRIBUTORS.md | 3 ++- .../static/description/index.html | 6 +++--- .../tests/test_uom_rounding_coherence.py | 14 ++++++++------ 8 files changed, 23 insertions(+), 20 deletions(-) diff --git a/uom_rounding_coherence/README.rst b/uom_rounding_coherence/README.rst index 6f2f96279..ea6faf83c 100644 --- a/uom_rounding_coherence/README.rst +++ b/uom_rounding_coherence/README.rst @@ -93,15 +93,15 @@ Credits Authors ------- -* NuoBiT Solutions -* S.L. +* NuoBiT Solutions SL Contributors ------------ - `NuoBiT `__: - - Eric Antones + - Eric Antones eantones@nuobit.com + - Deniz Gallo dgallo@nuobit.com Maintainers ----------- diff --git a/uom_rounding_coherence/__manifest__.py b/uom_rounding_coherence/__manifest__.py index 1ecb7071a..0116d20cd 100644 --- a/uom_rounding_coherence/__manifest__.py +++ b/uom_rounding_coherence/__manifest__.py @@ -1,15 +1,15 @@ -# Copyright 2026 NuoBiT Solutions - Eric Antones +# Copyright 2026 NuoBiT Solutions SL - Eric Antones +# Copyright 2026 NuoBiT Solutions SL - Deniz Gallo # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) { "name": "UoM Rounding Coherence", "summary": "Validates that UoM rounding precision is coherent" " with the conversion ratio to prevent precision loss", - "version": "14.0.1.0.0", - "author": "NuoBiT Solutions, S.L.", + "version": "18.0.1.0.0", + "author": "NuoBiT Solutions SL", "website": "https://github.com/NuoBiT/odoo-addons", "category": "Product", "depends": ["uom"], "license": "AGPL-3", - "installable": True, } diff --git a/uom_rounding_coherence/i18n/ca.po b/uom_rounding_coherence/i18n/ca.po index 7c0fbb188..667152d26 100644 --- a/uom_rounding_coherence/i18n/ca.po +++ b/uom_rounding_coherence/i18n/ca.po @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 14.0\n" +"Project-Id-Version: Odoo Server 18.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-02-17 00:00+0000\n" "PO-Revision-Date: 2026-02-17 00:00+0000\n" diff --git a/uom_rounding_coherence/i18n/es.po b/uom_rounding_coherence/i18n/es.po index 2b3a50e6a..474ff88d0 100644 --- a/uom_rounding_coherence/i18n/es.po +++ b/uom_rounding_coherence/i18n/es.po @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 14.0\n" +"Project-Id-Version: Odoo Server 18.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-02-17 00:00+0000\n" "PO-Revision-Date: 2026-02-17 00:00+0000\n" diff --git a/uom_rounding_coherence/models/uom_uom.py b/uom_rounding_coherence/models/uom_uom.py index 963ed116d..f8c15d249 100644 --- a/uom_rounding_coherence/models/uom_uom.py +++ b/uom_rounding_coherence/models/uom_uom.py @@ -1,4 +1,4 @@ -# Copyright 2026 NuoBiT Solutions - Eric Antones +# Copyright 2026 NuoBiT Solutions SL - Eric Antones # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo import _, api, models, tools diff --git a/uom_rounding_coherence/readme/CONTRIBUTORS.md b/uom_rounding_coherence/readme/CONTRIBUTORS.md index be221b732..2f88e0877 100644 --- a/uom_rounding_coherence/readme/CONTRIBUTORS.md +++ b/uom_rounding_coherence/readme/CONTRIBUTORS.md @@ -1,2 +1,3 @@ - [NuoBiT](https://www.nuobit.com): - - Eric Antones \ + - Eric Antones + - Deniz Gallo diff --git a/uom_rounding_coherence/static/description/index.html b/uom_rounding_coherence/static/description/index.html index 69cee46b2..de3dc3734 100644 --- a/uom_rounding_coherence/static/description/index.html +++ b/uom_rounding_coherence/static/description/index.html @@ -441,15 +441,15 @@

Credits

Authors

    -
  • NuoBiT Solutions
  • -
  • S.L.
  • +
  • NuoBiT Solutions SL

Contributors

diff --git a/uom_rounding_coherence/tests/test_uom_rounding_coherence.py b/uom_rounding_coherence/tests/test_uom_rounding_coherence.py index e357b2571..5134025f8 100644 --- a/uom_rounding_coherence/tests/test_uom_rounding_coherence.py +++ b/uom_rounding_coherence/tests/test_uom_rounding_coherence.py @@ -1,11 +1,12 @@ -# Copyright 2026 NuoBiT Solutions - Eric Antones +# Copyright 2026 NuoBiT Solutions SL- Eric Antones +# Copyright 2026 NuoBiT Solutions SL - Deniz Gallo # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo.exceptions import ValidationError -from odoo.tests import SavepointCase +from odoo.tests import TransactionCase -class TestUomRoundingCoherence(SavepointCase): +class TestUomRoundingCoherence(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() @@ -183,7 +184,8 @@ def test_bigger_uom_integer_ratio_passes(self): PRE: - A reference UoM exists with rounding 0.001 ACT: - Create a bigger UoM with factor=1/12 (dozen) and rounding=0.00001 POST: - The UoM is created without error because - effective rounding (0.00001 / 0.08333 ≈ 0.00012) <= ref rounding (0.001) + effective rounding + (0.00001 / 0.08333 ≈ 0.00012) <= ref rounding (0.001) """ # ARRANGE & ACT self.env["uom.uom"].create( @@ -250,7 +252,7 @@ def test_no_active_reference_uom_fails(self): self.env.cr.execute( "UPDATE uom_uom SET active = FALSE WHERE id = %s", (ref.id,) ) - self.env["uom.uom"].invalidate_cache() + self.env["uom.uom"].invalidate_model() # ACT & ASSERT with self.assertRaises(ValidationError): non_ref.write({"rounding": 0.0001}) @@ -296,7 +298,7 @@ def test_multiple_active_reference_uoms_fails(self): "UPDATE uom_uom SET uom_type = 'reference', factor = 1.0 WHERE id = %s", (to_corrupt.id,), ) - self.env["uom.uom"].invalidate_cache() + self.env["uom.uom"].invalidate_model() # ACT & ASSERT with self.assertRaises(ValidationError): another.write({"rounding": 0.0001})