From 3887a12c2ed0bfeb7971b9b592bbe0945e368caf Mon Sep 17 00:00:00 2001 From: Kevin Khao Date: Tue, 20 May 2025 09:54:08 +0300 Subject: [PATCH 1/4] [18.0][ADD] account_move_tier_validation_approver_group --- .../README.rst | 107 ++++ .../__init__.py | 2 + .../__manifest__.py | 21 + .../hooks.py | 11 + .../models/__init__.py | 4 + .../models/account_move.py | 50 ++ .../models/res_company.py | 15 + .../models/res_config_settings.py | 48 ++ .../models/res_partner.py | 12 + .../pyproject.toml | 3 + .../readme/CONFIGURE.md | 5 + .../readme/CONTRIBUTORS.md | 2 + .../readme/DESCRIPTION.md | 1 + .../readme/ROADMAP.md | 2 + .../readme/USAGE.md | 1 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 459 ++++++++++++++++++ .../views/account_move.xml | 19 + .../views/res_config_settings_views.xml | 25 + .../views/res_partner.xml | 15 + 20 files changed, 802 insertions(+) create mode 100644 account_move_tier_validation_approver_group/README.rst create mode 100644 account_move_tier_validation_approver_group/__init__.py create mode 100644 account_move_tier_validation_approver_group/__manifest__.py create mode 100644 account_move_tier_validation_approver_group/hooks.py create mode 100644 account_move_tier_validation_approver_group/models/__init__.py create mode 100644 account_move_tier_validation_approver_group/models/account_move.py create mode 100644 account_move_tier_validation_approver_group/models/res_company.py create mode 100644 account_move_tier_validation_approver_group/models/res_config_settings.py create mode 100644 account_move_tier_validation_approver_group/models/res_partner.py create mode 100644 account_move_tier_validation_approver_group/pyproject.toml create mode 100644 account_move_tier_validation_approver_group/readme/CONFIGURE.md create mode 100644 account_move_tier_validation_approver_group/readme/CONTRIBUTORS.md create mode 100644 account_move_tier_validation_approver_group/readme/DESCRIPTION.md create mode 100644 account_move_tier_validation_approver_group/readme/ROADMAP.md create mode 100644 account_move_tier_validation_approver_group/readme/USAGE.md create mode 100644 account_move_tier_validation_approver_group/static/description/icon.png create mode 100644 account_move_tier_validation_approver_group/static/description/index.html create mode 100644 account_move_tier_validation_approver_group/views/account_move.xml create mode 100644 account_move_tier_validation_approver_group/views/res_config_settings_views.xml create mode 100644 account_move_tier_validation_approver_group/views/res_partner.xml diff --git a/account_move_tier_validation_approver_group/README.rst b/account_move_tier_validation_approver_group/README.rst new file mode 100644 index 000000000..ee4811594 --- /dev/null +++ b/account_move_tier_validation_approver_group/README.rst @@ -0,0 +1,107 @@ +=========================================== +Account Move Tier Validation Approver Group +=========================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:f58257d33200151133e841b5f93a04c16b438444563afefec83cbd9d72900792 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-OCA%2Faccount--invoicing-lightgray.png?logo=github + :target: https://github.com/OCA/account-invoicing/tree/18.0/account_move_tier_validation_approver_group + :alt: OCA/account-invoicing +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/account-invoicing-18-0/account-invoicing-18-0-account_move_tier_validation_approver_group + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/account-invoicing&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module is an extension of account_move_tier_validation_approver to +add groups + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +- NOTE: If you install this module, it is highly likely you will want + to configure the domains. Without it, it will require both an + individual approver and an approver group +- Create a tier definition that mirrors the base + account_move_tier_validation's (automatic if you activate/deactivate + the approver option in settings) +- Add domains to the approver tier definitions for prioritizing which + field takes over. Suggested configuration: + + - Group approver Tier Definition: apply only if the bill has an + approver group set + - (Individual) approver Tier Definition: apply only if the bill has + no approver group set + +Usage +===== + +Once configured, use this module the same way as +account_move_tier_validation_approver + +Known issues / Roadmap +====================== + +- Add default valid domains to include/exclude bills depending on + whether the approver_id or approver_group_id is set +- More tests + +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 +------- + +* 360 ERP + +Contributors +------------ + +- Kevin Khao kevinkhao@gmail.com (https://www.360erp.com) + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/account-invoicing `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_move_tier_validation_approver_group/__init__.py b/account_move_tier_validation_approver_group/__init__.py new file mode 100644 index 000000000..548c73eb1 --- /dev/null +++ b/account_move_tier_validation_approver_group/__init__.py @@ -0,0 +1,2 @@ +from .hooks import pre_init_hook +from . import models diff --git a/account_move_tier_validation_approver_group/__manifest__.py b/account_move_tier_validation_approver_group/__manifest__.py new file mode 100644 index 000000000..7068dabc0 --- /dev/null +++ b/account_move_tier_validation_approver_group/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2025 360ERP +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Account Move Tier Validation Approver Group", + "summary": """Account move tier validation approver""", + "version": "18.0.1.0.1", + "license": "AGPL-3", + "author": "360 ERP,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/account-invoicing", + "depends": [ + "account_move_tier_validation_approver", + ], + "data": [ + "views/account_move.xml", + "views/res_partner.xml", + "views/res_config_settings_views.xml", + ], + "demo": [], + "pre_init_hook": "pre_init_hook", +} diff --git a/account_move_tier_validation_approver_group/hooks.py b/account_move_tier_validation_approver_group/hooks.py new file mode 100644 index 000000000..e7c9f01b0 --- /dev/null +++ b/account_move_tier_validation_approver_group/hooks.py @@ -0,0 +1,11 @@ +from odoo.tools import sql + + +def pre_init_hook(env): + """Prepare new account.move.approver_group_id computed field. + + Add column to avoid MemoryError on an existing Odoo instance + with lots of data. + """ + if not sql.column_exists(env.cr, "account_move", "approver_group_id"): + sql.create_column(env.cr, "account_move", "approver_group_id", "int4") diff --git a/account_move_tier_validation_approver_group/models/__init__.py b/account_move_tier_validation_approver_group/models/__init__.py new file mode 100644 index 000000000..fab388250 --- /dev/null +++ b/account_move_tier_validation_approver_group/models/__init__.py @@ -0,0 +1,4 @@ +from . import account_move +from . import res_partner +from . import res_company +from . import res_config_settings diff --git a/account_move_tier_validation_approver_group/models/account_move.py b/account_move_tier_validation_approver_group/models/account_move.py new file mode 100644 index 000000000..30b774a50 --- /dev/null +++ b/account_move_tier_validation_approver_group/models/account_move.py @@ -0,0 +1,50 @@ +# Copyright 2025 360 ERP +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.exceptions import UserError + + +class AccountMove(models.Model): + _inherit = "account.move" + + approver_group_id = fields.Many2one( + "res.groups", + string="Group responsible for approval", + compute="_compute_approver_group_id", + readonly=False, + store=True, + ) + + @api.depends("partner_id") + def _compute_approver_group_id(self): + for rec in self: + if rec.approver_group_id: + rec.approver_group_id = rec.approver_group_id + elif rec.partner_id.approver_group_id: + rec.approver_group_id = rec.partner_id.approver_group_id + else: + rec.approver_id = False + + def _post(self, soft=True): + for move in self: + move._check_has_approver() + return super()._post(soft) + + def _check_has_approver(self): + """Overload base function to take into account approver group""" + require_approver_in_vendor_bills = ( + self.company_id.require_approver_in_vendor_bills + ) + if ( + self.is_purchase_document(include_receipts=True) + and require_approver_in_vendor_bills + and not self.approver_id + and not self.approver_group_id + ): + raise UserError( + self.env._( + "It is mandatory to indicate a Responsible for " + "Approval individual or group (in {})" + ).format(self.name) + ) diff --git a/account_move_tier_validation_approver_group/models/res_company.py b/account_move_tier_validation_approver_group/models/res_company.py new file mode 100644 index 000000000..780418c56 --- /dev/null +++ b/account_move_tier_validation_approver_group/models/res_company.py @@ -0,0 +1,15 @@ +# Copyright 2020 ForgeFlow, S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + require_approver_group_in_vendor_bills = fields.Boolean( + string="Require approver in vendor bills (group)" + ) + validation_approver_group_tier_definition_id = fields.Many2one( + comodel_name="tier.definition", string="Bill approval tier definition (group)" + ) diff --git a/account_move_tier_validation_approver_group/models/res_config_settings.py b/account_move_tier_validation_approver_group/models/res_config_settings.py new file mode 100644 index 000000000..7dfef832a --- /dev/null +++ b/account_move_tier_validation_approver_group/models/res_config_settings.py @@ -0,0 +1,48 @@ +# Copyright 2020 ForgeFlow, S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + require_approver_group_in_vendor_bills = fields.Boolean( + string="Require Approver Group In Vendor Bills", + help="Requires adding an approver group before a vendor bill can be posted. " + "Note: If you combine both rules, be sure to configure the appropriate domains " + "on the tier definitions", + related="company_id.require_approver_in_vendor_bills", + readonly=False, + ) + + def set_values(self): + tier_definition = self.company_id.validation_approver_group_tier_definition_id + if not tier_definition: + field = self.env["ir.model.fields"].search( + [("model", "=", "account.move"), ("name", "=", "approver_group_id")] + ) + tier_definition = self.env["tier.definition"].create( + self._prepare_tier_approver_group_definition_values(field) + ) + self.company_id.validation_approver_group_tier_definition_id = ( + tier_definition + ) + if self.require_approver_group_in_vendor_bills: + tier_definition.action_unarchive() + else: + tier_definition.action_archive() + return super().set_values() + + def _prepare_tier_approver_group_definition_values(self, field): + return { + "model_id": self.env["ir.model"] + .search([("model", "=", "account.move")]) + .id, + "review_type": "field", + "name": "Validation with Approver field (group)", + "reviewer_field_id": field.id, + "definition_domain": "[('move_type', '=', 'in_invoice')]", + "approve_sequence": True, + "active": self.require_approver_in_vendor_bills, + } diff --git a/account_move_tier_validation_approver_group/models/res_partner.py b/account_move_tier_validation_approver_group/models/res_partner.py new file mode 100644 index 000000000..06552beb1 --- /dev/null +++ b/account_move_tier_validation_approver_group/models/res_partner.py @@ -0,0 +1,12 @@ +# Copyright 2025 360 ERP +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + approver_group_id = fields.Many2one( + "res.groups", string="Approver of Vendor Bills group" + ) diff --git a/account_move_tier_validation_approver_group/pyproject.toml b/account_move_tier_validation_approver_group/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/account_move_tier_validation_approver_group/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/account_move_tier_validation_approver_group/readme/CONFIGURE.md b/account_move_tier_validation_approver_group/readme/CONFIGURE.md new file mode 100644 index 000000000..a187f1232 --- /dev/null +++ b/account_move_tier_validation_approver_group/readme/CONFIGURE.md @@ -0,0 +1,5 @@ +* NOTE: If you install this module, it is highly likely you will want to configure the domains. Without it, it will require both an individual approver and an approver group +* Create a tier definition that mirrors the base account_move_tier_validation's (automatic if you activate/deactivate the approver option in settings) +* Add domains to the approver tier definitions for prioritizing which field takes over. Suggested configuration: + * Group approver Tier Definition: apply only if the bill has an approver group set + * (Individual) approver Tier Definition: apply only if the bill has no approver group set diff --git a/account_move_tier_validation_approver_group/readme/CONTRIBUTORS.md b/account_move_tier_validation_approver_group/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..a807ce13e --- /dev/null +++ b/account_move_tier_validation_approver_group/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Kevin Khao (https://www.360erp.com) + diff --git a/account_move_tier_validation_approver_group/readme/DESCRIPTION.md b/account_move_tier_validation_approver_group/readme/DESCRIPTION.md new file mode 100644 index 000000000..fe438a905 --- /dev/null +++ b/account_move_tier_validation_approver_group/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module is an extension of account_move_tier_validation_approver to add groups diff --git a/account_move_tier_validation_approver_group/readme/ROADMAP.md b/account_move_tier_validation_approver_group/readme/ROADMAP.md new file mode 100644 index 000000000..43e5ee367 --- /dev/null +++ b/account_move_tier_validation_approver_group/readme/ROADMAP.md @@ -0,0 +1,2 @@ +* Add default valid domains to include/exclude bills depending on whether the approver_id or approver_group_id is set +* More tests diff --git a/account_move_tier_validation_approver_group/readme/USAGE.md b/account_move_tier_validation_approver_group/readme/USAGE.md new file mode 100644 index 000000000..2f7a5a70a --- /dev/null +++ b/account_move_tier_validation_approver_group/readme/USAGE.md @@ -0,0 +1 @@ +Once configured, use this module the same way as account_move_tier_validation_approver diff --git a/account_move_tier_validation_approver_group/static/description/icon.png b/account_move_tier_validation_approver_group/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 zcmW++2RxMjAAjx~&dlBk9S+%}OXg)AGE&Cb*&}d0jUxM@u(PQx^-s)697TX`ehR4?GS^qbkof1cslKgkU)h65qZ9Oc=ml_0temigYLJfnz{IDzUf>bGs4N!v3=Z3jMq&A#7%rM5eQ#dc?k~! zVpnB`o+K7|Al`Q_U;eD$B zfJtP*jH`siUq~{KE)`jP2|#TUEFGRryE2`i0**z#*^6~AI|YzIWy$Cu#CSLW3q=GA z6`?GZymC;dCPk~rBS%eCb`5OLr;RUZ;D`}um=H)BfVIq%7VhiMr)_#G0N#zrNH|__ zc+blN2UAB0=617@>_u;MPHN;P;N#YoE=)R#i$k_`UAA>WWCcEVMh~L_ zj--gtp&|K1#58Yz*AHCTMziU1Jzt_jG0I@qAOHsk$2}yTmVkBp_eHuY$A9)>P6o~I z%aQ?!(GqeQ-Y+b0I(m9pwgi(IIZZzsbMv+9w{PFtd_<_(LA~0H(xz{=FhLB@(1&qHA5EJw1>>=%q2f&^X>IQ{!GJ4e9U z&KlB)z(84HmNgm2hg2C0>WM{E(DdPr+EeU_N@57;PC2&DmGFW_9kP&%?X4}+xWi)( z;)z%wI5>D4a*5XwD)P--sPkoY(a~WBw;E~AW`Yue4kFa^LM3X`8x|}ZUeMnqr}>kH zG%WWW>3ml$Yez?i%)2pbKPI7?5o?hydokgQyZsNEr{a|mLdt;X2TX(#B1j35xPnPW z*bMSSOauW>o;*=kO8ojw91VX!qoOQb)zHJ!odWB}d+*K?#sY_jqPdg{Sm2HdYzdEx zOGVPhVRTGPtv0o}RfVP;Nd(|CB)I;*t&QO8h zFfekr30S!-LHmV_Su-W+rEwYXJ^;6&3|L$mMC8*bQptyOo9;>Qb9Q9`ySe3%V$A*9 zeKEe+b0{#KWGp$F+tga)0RtI)nhMa-K@JS}2krK~n8vJ=Ngm?R!9G<~RyuU0d?nz# z-5EK$o(!F?hmX*2Yt6+coY`6jGbb7tF#6nHA zuKk=GGJ;ZwON1iAfG$E#Y7MnZVmrY|j0eVI(DN_MNFJmyZ|;w4tf@=CCDZ#5N_0K= z$;R~bbk?}TpfDjfB&aiQ$VA}s?P}xPERJG{kxk5~R`iRS(SK5d+Xs9swCozZISbnS zk!)I0>t=A<-^z(cmSFz3=jZ23u13X><0b)P)^1T_))Kr`e!-pb#q&J*Q`p+B6la%C zuVl&0duN<;uOsB3%T9Fp8t{ED108<+W(nOZd?gDnfNBC3>M8WE61$So|P zVvqH0SNtDTcsUdzaMDpT=Ty0pDHHNL@Z0w$Y`XO z2M-_r1S+GaH%pz#Uy0*w$Vdl=X=rQXEzO}d6J^R6zjM1u&c9vYLvLp?W7w(?np9x1 zE_0JSAJCPB%i7p*Wvg)pn5T`8k3-uR?*NT|J`eS#_#54p>!p(mLDvmc-3o0mX*mp_ zN*AeS<>#^-{S%W<*mz^!X$w_2dHWpcJ6^j64qFBft-o}o_Vx80o0>}Du;>kLts;$8 zC`7q$QI(dKYG`Wa8#wl@V4jVWBRGQ@1dr-hstpQL)Tl+aqVpGpbSfN>5i&QMXfiZ> zaA?T1VGe?rpQ@;+pkrVdd{klI&jVS@I5_iz!=UMpTsa~mBga?1r}aRBm1WS;TT*s0f0lY=JBl66Upy)-k4J}lh=P^8(SXk~0xW=T9v*B|gzIhN z>qsO7dFd~mgxAy4V?&)=5ieYq?zi?ZEoj)&2o)RLy=@hbCRcfT5jigwtQGE{L*8<@Yd{zg;CsL5mvzfDY}P-wos_6PfprFVaeqNE%h zKZhLtcQld;ZD+>=nqN~>GvROfueSzJD&BE*}XfU|H&(FssBqY=hPCt`d zH?@s2>I(|;fcW&YM6#V#!kUIP8$Nkdh0A(bEVj``-AAyYgwY~jB zT|I7Bf@%;7aL7Wf4dZ%VqF$eiaC38OV6oy3Z#TER2G+fOCd9Iaoy6aLYbPTN{XRPz z;U!V|vBf%H!}52L2gH_+j;`bTcQRXB+y9onc^wLm5wi3-Be}U>k_u>2Eg$=k!(l@I zcCg+flakT2Nej3i0yn+g+}%NYb?ta;R?(g5SnwsQ49U8Wng8d|{B+lyRcEDvR3+`O{zfmrmvFrL6acVP%yG98X zo&+VBg@px@i)%o?dG(`T;n*$S5*rnyiR#=wW}}GsAcfyQpE|>a{=$Hjg=-*_K;UtD z#z-)AXwSRY?OPefw^iI+ z)AXz#PfEjlwTes|_{sB?4(O@fg0AJ^g8gP}ex9Ucf*@_^J(s_5jJV}c)s$`Myn|Kd z$6>}#q^n{4vN@+Os$m7KV+`}c%4)4pv@06af4-x5#wj!KKb%caK{A&Y#Rfs z-po?Dcb1({W=6FKIUirH&(yg=*6aLCekcKwyfK^JN5{wcA3nhO(o}SK#!CINhI`-I z1)6&n7O&ZmyFMuNwvEic#IiOAwNkR=u5it{B9n2sAJV5pNhar=j5`*N!Na;c7g!l$ z3aYBqUkqqTJ=Re-;)s!EOeij=7SQZ3Hq}ZRds%IM*PtM$wV z@;rlc*NRK7i3y5BETSKuumEN`Xu_8GP1Ri=OKQ$@I^ko8>H6)4rjiG5{VBM>B|%`&&s^)jS|-_95&yc=GqjNo{zFkw%%HHhS~e=s zD#sfS+-?*t|J!+ozP6KvtOl!R)@@-z24}`9{QaVLD^9VCSR2b`b!KC#o;Ki<+wXB6 zx3&O0LOWcg4&rv4QG0)4yb}7BFSEg~=IR5#ZRj8kg}dS7_V&^%#Do==#`u zpy6{ox?jWuR(;pg+f@mT>#HGWHAJRRDDDv~@(IDw&R>9643kK#HN`!1vBJHnC+RM&yIh8{gG2q zA%e*U3|N0XSRa~oX-3EAneep)@{h2vvd3Xvy$7og(sayr@95+e6~Xvi1tUqnIxoIH zVWo*OwYElb#uyW{Imam6f2rGbjR!Y3`#gPqkv57dB6K^wRGxc9B(t|aYDGS=m$&S!NmCtrMMaUg(c zc2qC=2Z`EEFMW-me5B)24AqF*bV5Dr-M5ig(l-WPS%CgaPzs6p_gnCIvTJ=Y<6!gT zVt@AfYCzjjsMEGi=rDQHo0yc;HqoRNnNFeWZgcm?f;cp(6CNylj36DoL(?TS7eU#+ z7&mfr#y))+CJOXQKUMZ7QIdS9@#-}7y2K1{8)cCt0~-X0O!O?Qx#E4Og+;A2SjalQ zs7r?qn0H044=sDN$SRG$arw~n=+T_DNdSrarmu)V6@|?1-ZB#hRn`uilTGPJ@fqEy zGt(f0B+^JDP&f=r{#Y_wi#AVDf-y!RIXU^0jXsFpf>=Ji*TeqSY!H~AMbJdCGLhC) zn7Rx+sXw6uYj;WRYrLd^5IZq@6JI1C^YkgnedZEYy<&4(z%Q$5yv#Boo{AH8n$a zhb4Y3PWdr269&?V%uI$xMcUrMzl=;w<_nm*qr=c3Rl@i5wWB;e-`t7D&c-mcQl7x! zZWB`UGcw=Y2=}~wzrfLx=uet<;m3~=8I~ZRuzvMQUQdr+yTV|ATf1Uuomr__nDf=X zZ3WYJtHp_ri(}SQAPjv+Y+0=fH4krOP@S&=zZ-t1jW1o@}z;xk8 z(Nz1co&El^HK^NrhVHa-_;&88vTU>_J33=%{if;BEY*J#1n59=07jrGQ#IP>@u#3A z;!q+E1Rj3ZJ+!4bq9F8PXJ@yMgZL;>&gYA0%_Kbi8?S=XGM~dnQZQ!yBSgcZhY96H zrWnU;k)qy`rX&&xlDyA%(a1Hhi5CWkmg(`Gb%m(HKi-7Z!LKGRP_B8@`7&hdDy5n= z`OIxqxiVfX@OX1p(mQu>0Ai*v_cTMiw4qRt3~NBvr9oBy0)r>w3p~V0SCm=An6@3n)>@z!|o-$HvDK z|3D2ZMJkLE5loMKl6R^ez@Zz%S$&mbeoqH5`Bb){Ei21q&VP)hWS2tjShfFtGE+$z zzCR$P#uktu+#!w)cX!lWN1XU%K-r=s{|j?)Akf@q#3b#{6cZCuJ~gCxuMXRmI$nGtnH+-h z+GEi!*X=AP<|fG`1>MBdTb?28JYc=fGvAi2I<$B(rs$;eoJCyR6_bc~p!XR@O-+sD z=eH`-ye})I5ic1eL~TDmtfJ|8`0VJ*Yr=hNCd)G1p2MMz4C3^Mj?7;!w|Ly%JqmuW zlIEW^Ft%z?*|fpXda>Jr^1noFZEwFgVV%|*XhH@acv8rdGxeEX{M$(vG{Zw+x(ei@ zmfXb22}8-?Fi`vo-YVrTH*C?a8%M=Hv9MqVH7H^J$KsD?>!SFZ;ZsvnHr_gn=7acz z#W?0eCdVhVMWN12VV^$>WlQ?f;P^{(&pYTops|btm6aj>_Uz+hqpGwB)vWp0Cf5y< zft8-je~nn?W11plq}N)4A{l8I7$!ks_x$PXW-2XaRFswX_BnF{R#6YIwMhAgd5F9X zGmwdadS6(a^fjHtXg8=l?Rc0Sm%hk6E9!5cLVloEy4eh(=FwgP`)~I^5~pBEWo+F6 zSf2ncyMurJN91#cJTy_u8Y}@%!bq1RkGC~-bV@SXRd4F{R-*V`bS+6;W5vZ(&+I<9$;-V|eNfLa5n-6% z2(}&uGRF;p92eS*sE*oR$@pexaqr*meB)VhmIg@h{uzkk$9~qh#cHhw#>O%)b@+(| z^IQgqzuj~Sk(J;swEM-3TrJAPCq9k^^^`q{IItKBRXYe}e0Tdr=Huf7da3$l4PdpwWDop%^}n;dD#K4s#DYA8SHZ z&1!riV4W4R7R#C))JH1~axJ)RYnM$$lIR%6fIVA@zV{XVyx}C+a-Dt8Y9M)^KU0+H zR4IUb2CJ{Hg>CuaXtD50jB(_Tcx=Z$^WYu2u5kubqmwp%drJ6 z?Fo40g!Qd<-l=TQxqHEOuPX0;^z7iX?Ke^a%XT<13TA^5`4Xcw6D@Ur&VT&CUe0d} z1GjOVF1^L@>O)l@?bD~$wzgf(nxX1OGD8fEV?TdJcZc2KoUe|oP1#=$$7ee|xbY)A zDZq+cuTpc(fFdj^=!;{k03C69lMQ(|>uhRfRu%+!k&YOi-3|1QKB z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/account_move_tier_validation_approver_group/static/description/index.html b/account_move_tier_validation_approver_group/static/description/index.html new file mode 100644 index 000000000..ee8d9b1e1 --- /dev/null +++ b/account_move_tier_validation_approver_group/static/description/index.html @@ -0,0 +1,459 @@ + + + + + +Account Move Tier Validation Approver Group + + + +
+

Account Move Tier Validation Approver Group

+ + +

Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runboat

+

This module is an extension of account_move_tier_validation_approver to +add groups

+

Table of contents

+ +
+

Configuration

+
    +
  • NOTE: If you install this module, it is highly likely you will want +to configure the domains. Without it, it will require both an +individual approver and an approver group
  • +
  • Create a tier definition that mirrors the base +account_move_tier_validation’s (automatic if you activate/deactivate +the approver option in settings)
  • +
  • Add domains to the approver tier definitions for prioritizing which +field takes over. Suggested configuration:
      +
    • Group approver Tier Definition: apply only if the bill has an +approver group set
    • +
    • (Individual) approver Tier Definition: apply only if the bill has +no approver group set
    • +
    +
  • +
+
+
+

Usage

+

Once configured, use this module the same way as +account_move_tier_validation_approver

+
+
+

Known issues / Roadmap

+
    +
  • Add default valid domains to include/exclude bills depending on +whether the approver_id or approver_group_id is set
  • +
  • More tests
  • +
+
+
+

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

+
    +
  • 360 ERP
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/account-invoicing project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/account_move_tier_validation_approver_group/views/account_move.xml b/account_move_tier_validation_approver_group/views/account_move.xml new file mode 100644 index 000000000..c1ccf0b79 --- /dev/null +++ b/account_move_tier_validation_approver_group/views/account_move.xml @@ -0,0 +1,19 @@ + + + + + account.move.form + account.move + + + + + + + + diff --git a/account_move_tier_validation_approver_group/views/res_config_settings_views.xml b/account_move_tier_validation_approver_group/views/res_config_settings_views.xml new file mode 100644 index 000000000..9618f2c5e --- /dev/null +++ b/account_move_tier_validation_approver_group/views/res_config_settings_views.xml @@ -0,0 +1,25 @@ + + + + res.config.settings.view.form.inherit + res.config.settings + + + + + + + + + + diff --git a/account_move_tier_validation_approver_group/views/res_partner.xml b/account_move_tier_validation_approver_group/views/res_partner.xml new file mode 100644 index 000000000..c30301dd4 --- /dev/null +++ b/account_move_tier_validation_approver_group/views/res_partner.xml @@ -0,0 +1,15 @@ + + + + + res.partner.move_approve.user + res.partner + + + + + + + + From 8340aa76854caa83a118803701875f293da02c8c Mon Sep 17 00:00:00 2001 From: Andrea Stirpe Date: Thu, 22 Oct 2026 14:21:15 +0200 Subject: [PATCH 2/4] [IMP] account_move_tier_validation_approver_group: pre-commit auto fixes --- account_move_tier_validation_approver_group/__manifest__.py | 2 +- .../views/account_move.xml | 2 +- .../views/res_config_settings_views.xml | 2 +- .../views/res_partner.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/account_move_tier_validation_approver_group/__manifest__.py b/account_move_tier_validation_approver_group/__manifest__.py index 7068dabc0..971f0116e 100644 --- a/account_move_tier_validation_approver_group/__manifest__.py +++ b/account_move_tier_validation_approver_group/__manifest__.py @@ -7,7 +7,7 @@ "version": "18.0.1.0.1", "license": "AGPL-3", "author": "360 ERP,Odoo Community Association (OCA)", - "website": "https://github.com/OCA/account-invoicing", + "website": "https://github.com/OCA/tier-validation", "depends": [ "account_move_tier_validation_approver", ], diff --git a/account_move_tier_validation_approver_group/views/account_move.xml b/account_move_tier_validation_approver_group/views/account_move.xml index c1ccf0b79..182eceb28 100644 --- a/account_move_tier_validation_approver_group/views/account_move.xml +++ b/account_move_tier_validation_approver_group/views/account_move.xml @@ -1,4 +1,4 @@ - + diff --git a/account_move_tier_validation_approver_group/views/res_config_settings_views.xml b/account_move_tier_validation_approver_group/views/res_config_settings_views.xml index 9618f2c5e..180f828f6 100644 --- a/account_move_tier_validation_approver_group/views/res_config_settings_views.xml +++ b/account_move_tier_validation_approver_group/views/res_config_settings_views.xml @@ -1,4 +1,4 @@ - + res.config.settings.view.form.inherit diff --git a/account_move_tier_validation_approver_group/views/res_partner.xml b/account_move_tier_validation_approver_group/views/res_partner.xml index c30301dd4..382f4fda4 100644 --- a/account_move_tier_validation_approver_group/views/res_partner.xml +++ b/account_move_tier_validation_approver_group/views/res_partner.xml @@ -1,4 +1,4 @@ - + From 686abfe4b5b3b09869761ccdd38b4116a2952b05 Mon Sep 17 00:00:00 2001 From: Andrea Stirpe Date: Thu, 22 Oct 2026 14:23:00 +0200 Subject: [PATCH 3/4] [MIG] account_move_tier_validation_approver_group: Migration to 20.0 --- .../README.rst | 70 ++++---- .../__manifest__.py | 6 +- .../models/account_move.py | 31 ++-- .../models/res_config_settings.py | 23 ++- .../readme/CONFIGURE.md | 19 ++- .../readme/DESCRIPTION.md | 7 +- .../readme/ROADMAP.md | 4 +- .../readme/USAGE.md | 7 +- .../static/description/index.html | 87 +++++----- .../tests/__init__.py | 1 + ...unt_move_tier_validation_approver_group.py | 157 ++++++++++++++++++ .../views/account_move.xml | 13 ++ 12 files changed, 322 insertions(+), 103 deletions(-) create mode 100644 account_move_tier_validation_approver_group/tests/__init__.py create mode 100644 account_move_tier_validation_approver_group/tests/test_account_move_tier_validation_approver_group.py diff --git a/account_move_tier_validation_approver_group/README.rst b/account_move_tier_validation_approver_group/README.rst index ee4811594..290e5bd0d 100644 --- a/account_move_tier_validation_approver_group/README.rst +++ b/account_move_tier_validation_approver_group/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + =========================================== Account Move Tier Validation Approver Group =========================================== @@ -13,23 +17,27 @@ Account Move Tier Validation Approver Group .. |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 +.. |badge2| image:: https://img.shields.io/badge/license-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%2Faccount--invoicing-lightgray.png?logo=github - :target: https://github.com/OCA/account-invoicing/tree/18.0/account_move_tier_validation_approver_group - :alt: OCA/account-invoicing +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Ftier--validation-lightgray.png?logo=github + :target: https://github.com/OCA/tier-validation/tree/20.0/account_move_tier_validation_approver_group + :alt: OCA/tier-validation .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/account-invoicing-18-0/account-invoicing-18-0-account_move_tier_validation_approver_group + :target: https://translation.odoo-community.org/projects/tier-validation-20-0/tier-validation-20-0-account_move_tier_validation_approver_group :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/account-invoicing&target_branch=18.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/tier-validation&target_branch=20.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| -This module is an extension of account_move_tier_validation_approver to -add groups +This module extends *Account Move Tier Validation Approver* so that a +group of users, and not only a single user, can be responsible for +approving a vendor bill. + +You can set a default *Approver of Vendor Bills group* on the vendor. +New vendor bills take this group as *Group responsible for approval*. **Table of contents** @@ -39,40 +47,44 @@ add groups Configuration ============= -- NOTE: If you install this module, it is highly likely you will want - to configure the domains. Without it, it will require both an - individual approver and an approver group -- Create a tier definition that mirrors the base - account_move_tier_validation's (automatic if you activate/deactivate - the approver option in settings) -- Add domains to the approver tier definitions for prioritizing which - field takes over. Suggested configuration: - - - Group approver Tier Definition: apply only if the bill has an - approver group set - - (Individual) approver Tier Definition: apply only if the bill has - no approver group set +1. Go to *Invoicing > Configuration > Settings*. +2. In the *Vendor Bills* section, enable *Require Approver Group In + Vendor Bills*. This creates (or activates) the tier definition + "Validation with Approver field (group)". This tier only applies to + bills that have a *Group responsible for approval*. +3. On the vendor, in the *Sales & Purchase* tab, set the *Approver of + Vendor Bills group*. + +If you also enable *Require Approver In Vendor Bills* (the individual +approver), a bill can be posted when it has a user OR a group +responsible for approval. In this case, add a domain to the individual +tier definition ("Validation with Approver field") so that it only +applies when no group is set, for example +``[('move_type', '=', 'in_invoice'), ('approver_group_id', '=', False)]``. Usage ===== -Once configured, use this module the same way as -account_move_tier_validation_approver +1. Create a vendor bill for a vendor with an *Approver of Vendor Bills + group*. The *Group responsible for approval* is filled in. You can + change it if needed. +2. Click on *Request Validation*. Any member of the group can approve + the bill. +3. Once approved, post the bill. Known issues / Roadmap ====================== -- Add default valid domains to include/exclude bills depending on - whether the approver_id or approver_group_id is set -- More tests +- Add the individual approver tier domain automatically when both + options are enabled. Bug Tracker =========== -Bugs are tracked on `GitHub Issues `_. +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. @@ -102,6 +114,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/account-invoicing `_ project on GitHub. +This module is part of the `OCA/tier-validation `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_move_tier_validation_approver_group/__manifest__.py b/account_move_tier_validation_approver_group/__manifest__.py index 971f0116e..bb5baf165 100644 --- a/account_move_tier_validation_approver_group/__manifest__.py +++ b/account_move_tier_validation_approver_group/__manifest__.py @@ -3,8 +3,9 @@ { "name": "Account Move Tier Validation Approver Group", - "summary": """Account move tier validation approver""", - "version": "18.0.1.0.1", + "summary": "Allow a group of users to be responsible for approving vendor bills", + "version": "20.0.1.0.0", + "category": "Accounting", "license": "AGPL-3", "author": "360 ERP,Odoo Community Association (OCA)", "website": "https://github.com/OCA/tier-validation", @@ -16,6 +17,5 @@ "views/res_partner.xml", "views/res_config_settings_views.xml", ], - "demo": [], "pre_init_hook": "pre_init_hook", } diff --git a/account_move_tier_validation_approver_group/models/account_move.py b/account_move_tier_validation_approver_group/models/account_move.py index 30b774a50..1a97bf9c1 100644 --- a/account_move_tier_validation_approver_group/models/account_move.py +++ b/account_move_tier_validation_approver_group/models/account_move.py @@ -20,31 +20,36 @@ class AccountMove(models.Model): def _compute_approver_group_id(self): for rec in self: if rec.approver_group_id: + # assign a value in any case rec.approver_group_id = rec.approver_group_id elif rec.partner_id.approver_group_id: rec.approver_group_id = rec.partner_id.approver_group_id else: - rec.approver_id = False - - def _post(self, soft=True): - for move in self: - move._check_has_approver() - return super()._post(soft) + rec.approver_group_id = False def _check_has_approver(self): - """Overload base function to take into account approver group""" - require_approver_in_vendor_bills = ( - self.company_id.require_approver_in_vendor_bills + """Take the approver group into account. + + When an approver (user) or an approver group is required, a vendor + bill can be posted if it has a user OR a group responsible for + the approval. + """ + self.ensure_one() + company = self.company_id + approver_required = ( + company.require_approver_in_vendor_bills + or company.require_approver_group_in_vendor_bills ) if ( self.is_purchase_document(include_receipts=True) - and require_approver_in_vendor_bills + and approver_required and not self.approver_id and not self.approver_group_id ): raise UserError( self.env._( - "It is mandatory to indicate a Responsible for " - "Approval individual or group (in {})" - ).format(self.name) + "It is mandatory to indicate a Responsible for Approval " + "(user or group) (in %s)", + self.name, + ) ) diff --git a/account_move_tier_validation_approver_group/models/res_config_settings.py b/account_move_tier_validation_approver_group/models/res_config_settings.py index 7dfef832a..1c5b9fc96 100644 --- a/account_move_tier_validation_approver_group/models/res_config_settings.py +++ b/account_move_tier_validation_approver_group/models/res_config_settings.py @@ -12,22 +12,21 @@ class ResConfigSettings(models.TransientModel): help="Requires adding an approver group before a vendor bill can be posted. " "Note: If you combine both rules, be sure to configure the appropriate domains " "on the tier definitions", - related="company_id.require_approver_in_vendor_bills", + related="company_id.require_approver_group_in_vendor_bills", readonly=False, ) def set_values(self): - tier_definition = self.company_id.validation_approver_group_tier_definition_id + company = self.company_id + tier_definition = company.validation_approver_group_tier_definition_id if not tier_definition: - field = self.env["ir.model.fields"].search( - [("model", "=", "account.move"), ("name", "=", "approver_group_id")] + field = self.env["ir.model.fields"]._get( + "account.move", "approver_group_id" ) tier_definition = self.env["tier.definition"].create( self._prepare_tier_approver_group_definition_values(field) ) - self.company_id.validation_approver_group_tier_definition_id = ( - tier_definition - ) + company.validation_approver_group_tier_definition_id = tier_definition if self.require_approver_group_in_vendor_bills: tier_definition.action_unarchive() else: @@ -36,13 +35,13 @@ def set_values(self): def _prepare_tier_approver_group_definition_values(self, field): return { - "model_id": self.env["ir.model"] - .search([("model", "=", "account.move")]) - .id, + "model_id": self.env["ir.model"]._get_id("account.move"), "review_type": "field", "name": "Validation with Approver field (group)", "reviewer_field_id": field.id, - "definition_domain": "[('move_type', '=', 'in_invoice')]", + "definition_domain": ( + "[('move_type', '=', 'in_invoice'), ('approver_group_id', '!=', False)]" + ), "approve_sequence": True, - "active": self.require_approver_in_vendor_bills, + "active": self.require_approver_group_in_vendor_bills, } diff --git a/account_move_tier_validation_approver_group/readme/CONFIGURE.md b/account_move_tier_validation_approver_group/readme/CONFIGURE.md index a187f1232..0ee2c7609 100644 --- a/account_move_tier_validation_approver_group/readme/CONFIGURE.md +++ b/account_move_tier_validation_approver_group/readme/CONFIGURE.md @@ -1,5 +1,14 @@ -* NOTE: If you install this module, it is highly likely you will want to configure the domains. Without it, it will require both an individual approver and an approver group -* Create a tier definition that mirrors the base account_move_tier_validation's (automatic if you activate/deactivate the approver option in settings) -* Add domains to the approver tier definitions for prioritizing which field takes over. Suggested configuration: - * Group approver Tier Definition: apply only if the bill has an approver group set - * (Individual) approver Tier Definition: apply only if the bill has no approver group set +1. Go to *Invoicing \> Configuration \> Settings*. +2. In the *Vendor Bills* section, enable *Require Approver Group In + Vendor Bills*. This creates (or activates) the tier definition + "Validation with Approver field (group)". This tier only applies to + bills that have a *Group responsible for approval*. +3. On the vendor, in the *Sales & Purchase* tab, set the *Approver of + Vendor Bills group*. + +If you also enable *Require Approver In Vendor Bills* (the individual +approver), a bill can be posted when it has a user OR a group +responsible for approval. In this case, add a domain to the individual +tier definition ("Validation with Approver field") so that it only +applies when no group is set, for example +`[('move_type', '=', 'in_invoice'), ('approver_group_id', '=', False)]`. diff --git a/account_move_tier_validation_approver_group/readme/DESCRIPTION.md b/account_move_tier_validation_approver_group/readme/DESCRIPTION.md index fe438a905..500e01126 100644 --- a/account_move_tier_validation_approver_group/readme/DESCRIPTION.md +++ b/account_move_tier_validation_approver_group/readme/DESCRIPTION.md @@ -1 +1,6 @@ -This module is an extension of account_move_tier_validation_approver to add groups +This module extends *Account Move Tier Validation Approver* so that a +group of users, and not only a single user, can be responsible for +approving a vendor bill. + +You can set a default *Approver of Vendor Bills group* on the vendor. +New vendor bills take this group as *Group responsible for approval*. diff --git a/account_move_tier_validation_approver_group/readme/ROADMAP.md b/account_move_tier_validation_approver_group/readme/ROADMAP.md index 43e5ee367..1f8e0da61 100644 --- a/account_move_tier_validation_approver_group/readme/ROADMAP.md +++ b/account_move_tier_validation_approver_group/readme/ROADMAP.md @@ -1,2 +1,2 @@ -* Add default valid domains to include/exclude bills depending on whether the approver_id or approver_group_id is set -* More tests +- Add the individual approver tier domain automatically when both + options are enabled. diff --git a/account_move_tier_validation_approver_group/readme/USAGE.md b/account_move_tier_validation_approver_group/readme/USAGE.md index 2f7a5a70a..55c47516e 100644 --- a/account_move_tier_validation_approver_group/readme/USAGE.md +++ b/account_move_tier_validation_approver_group/readme/USAGE.md @@ -1 +1,6 @@ -Once configured, use this module the same way as account_move_tier_validation_approver +1. Create a vendor bill for a vendor with an *Approver of Vendor Bills + group*. The *Group responsible for approval* is filled in. You can + change it if needed. +2. Click on *Request Validation*. Any member of the group can approve + the bill. +3. Once approved, post the bill. diff --git a/account_move_tier_validation_approver_group/static/description/index.html b/account_move_tier_validation_approver_group/static/description/index.html index ee8d9b1e1..0505048ce 100644 --- a/account_move_tier_validation_approver_group/static/description/index.html +++ b/account_move_tier_validation_approver_group/static/description/index.html @@ -360,18 +360,26 @@ -
-

Account Move Tier Validation Approver Group

+
+ + +Odoo Community Association + +
+

Account Move Tier Validation Approver Group

-

Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runboat

-

This module is an extension of account_move_tier_validation_approver to -add groups

+

Beta License: AGPL-3 OCA/tier-validation Translate me on Weblate Try me on Runboat

+

This module extends Account Move Tier Validation Approver so that a +group of users, and not only a single user, can be responsible for +approving a vendor bill.

+

You can set a default Approver of Vendor Bills group on the vendor. +New vendor bills take this group as Group responsible for approval.

Table of contents

    @@ -388,61 +396,65 @@

    Account Move Tier Validation Approver Group

-

Configuration

-
    -
  • NOTE: If you install this module, it is highly likely you will want -to configure the domains. Without it, it will require both an -individual approver and an approver group
  • -
  • Create a tier definition that mirrors the base -account_move_tier_validation’s (automatic if you activate/deactivate -the approver option in settings)
  • -
  • Add domains to the approver tier definitions for prioritizing which -field takes over. Suggested configuration:
      -
    • Group approver Tier Definition: apply only if the bill has an -approver group set
    • -
    • (Individual) approver Tier Definition: apply only if the bill has -no approver group set
    • -
    -
  • -
+

Configuration

+
    +
  1. Go to Invoicing > Configuration > Settings.
  2. +
  3. In the Vendor Bills section, enable Require Approver Group In +Vendor Bills. This creates (or activates) the tier definition +“Validation with Approver field (group)”. This tier only applies to +bills that have a Group responsible for approval.
  4. +
  5. On the vendor, in the Sales & Purchase tab, set the Approver of +Vendor Bills group.
  6. +
+

If you also enable Require Approver In Vendor Bills (the individual +approver), a bill can be posted when it has a user OR a group +responsible for approval. In this case, add a domain to the individual +tier definition (“Validation with Approver field”) so that it only +applies when no group is set, for example +[('move_type', '=', 'in_invoice'), ('approver_group_id', '=', False)].

-

Usage

-

Once configured, use this module the same way as -account_move_tier_validation_approver

+

Usage

+
    +
  1. Create a vendor bill for a vendor with an Approver of Vendor Bills +group. The Group responsible for approval is filled in. You can +change it if needed.
  2. +
  3. Click on Request Validation. Any member of the group can approve +the bill.
  4. +
  5. Once approved, post the bill.
  6. +
-

Known issues / Roadmap

+

Known issues / Roadmap

    -
  • Add default valid domains to include/exclude bills depending on -whether the approver_id or approver_group_id is set
  • -
  • More tests
  • +
  • Add the individual approver tier domain automatically when both +options are enabled.
-

Bug Tracker

-

Bugs are tracked on GitHub Issues. +

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.

-

Credits

+

Credits

-

Authors

+

Authors

  • 360 ERP
-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -450,10 +462,11 @@

Maintainers

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/account-invoicing project on GitHub.

+

This module is part of the OCA/tier-validation project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
diff --git a/account_move_tier_validation_approver_group/tests/__init__.py b/account_move_tier_validation_approver_group/tests/__init__.py new file mode 100644 index 000000000..5eda3546e --- /dev/null +++ b/account_move_tier_validation_approver_group/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_move_tier_validation_approver_group diff --git a/account_move_tier_validation_approver_group/tests/test_account_move_tier_validation_approver_group.py b/account_move_tier_validation_approver_group/tests/test_account_move_tier_validation_approver_group.py new file mode 100644 index 000000000..a1e9729d1 --- /dev/null +++ b/account_move_tier_validation_approver_group/tests/test_account_move_tier_validation_approver_group.py @@ -0,0 +1,157 @@ +# Copyright 2026 360ERP (https://www.360erp.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import Command +from odoo.exceptions import UserError, ValidationError +from odoo.tests import new_test_user, tagged + +from odoo.addons.account.tests.common import AccountTestInvoicingCommon + + +@tagged("post_install", "-at_install") +class TestAccountMoveTierValidationApproverGroup(AccountTestInvoicingCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.approver_group = cls.env["res.groups"].create( + {"name": "Vendor Bill Approvers"} + ) + cls.group_approver = new_test_user( + cls.env, + name="Group Approver", + login="group_approver", + groups="base.group_user,account.group_account_manager", + ) + cls.group_approver.group_ids += cls.approver_group + cls.other_user = new_test_user( + cls.env, + name="Other User", + login="other_user", + groups="base.group_user,account.group_account_manager", + ) + cls.single_approver = new_test_user( + cls.env, + name="Single Approver", + login="single_approver", + groups="base.group_user,account.group_account_manager", + ) + cls.vendor = cls.env["res.partner"].create( + { + "name": "Vendor with approver group", + "approver_group_id": cls.approver_group.id, + } + ) + cls.vendor_no_group = cls.env["res.partner"].create( + {"name": "Vendor without approver group"} + ) + cls.product = cls.env["product.product"].create({"name": "Desk"}) + # Only keep the tiers created by these tests + cls.env["tier.definition"].search([]).action_archive() + + def _create_bill(self, partner, **values): + return self.env["account.move"].create( + { + "move_type": "in_invoice", + "partner_id": partner.id, + "invoice_date": "2025-01-01", + "invoice_line_ids": [ + Command.create( + { + "product_id": self.product.id, + "quantity": 1, + "price_unit": 100, + } + ) + ], + **values, + } + ) + + def _settings(self, **values): + config = self.env["res.config.settings"].create(values) + config.set_values() + return config + + def test_01_compute_approver_group_id(self): + """The group comes from the vendor and can be changed by hand.""" + move = self.env["account.move"].new( + {"move_type": "in_invoice", "partner_id": self.vendor_no_group.id} + ) + self.assertFalse(move.approver_group_id) + move.partner_id = self.vendor + self.assertEqual(move.approver_group_id, self.approver_group) + other_group = self.env["res.groups"].create({"name": "Other approvers"}) + move.approver_group_id = other_group + move._compute_approver_group_id() + self.assertEqual(move.approver_group_id, other_group) + + def test_02_compute_keeps_individual_approver(self): + """Computing the group never removes the individual approver.""" + bill = self._create_bill( + self.vendor_no_group, approver_id=self.single_approver.id + ) + self.assertFalse(bill.approver_group_id) + self.assertEqual(bill.approver_id, self.single_approver) + bill._compute_approver_group_id() + self.assertEqual(bill.approver_id, self.single_approver) + + def test_03_settings_are_independent(self): + """The group option does not change the individual option.""" + self._settings( + require_approver_in_vendor_bills=False, + require_approver_group_in_vendor_bills=True, + ) + company = self.env.company + self.assertTrue(company.require_approver_group_in_vendor_bills) + self.assertFalse(company.require_approver_in_vendor_bills) + tier = company.validation_approver_group_tier_definition_id + self.assertTrue(tier.active) + self.assertEqual(tier.reviewer_field_id.name, "approver_group_id") + self.assertIn("approver_group_id", tier.definition_domain) + self._settings(require_approver_group_in_vendor_bills=False) + self.assertFalse(tier.active) + self.assertFalse(company.require_approver_group_in_vendor_bills) + + def test_04_post_requires_user_or_group(self): + self._settings(require_approver_group_in_vendor_bills=True) + bill = self._create_bill(self.vendor_no_group) + with self.assertRaisesRegex(UserError, "user or group"): + bill._post() + # A group is enough + bill.approver_group_id = self.approver_group + bill._check_has_approver() + # A user is enough too + bill.approver_group_id = False + bill.approver_id = self.single_approver + bill._check_has_approver() + + def test_05_individual_setting_accepts_group(self): + """With the individual option, a bill with only a group can be posted.""" + self._settings(require_approver_in_vendor_bills=True) + bill = self._create_bill(self.vendor) + self.assertFalse(bill.approver_id) + self.assertEqual(bill.approver_group_id, self.approver_group) + bill._check_has_approver() + + def test_06_group_member_validates(self): + self._settings(require_approver_group_in_vendor_bills=True) + bill = self._create_bill(self.vendor) + bill.with_user(self.other_user).request_validation() + bill.invalidate_recordset() + self.assertIn(self.group_approver, bill.reviewer_ids) + self.assertNotIn(self.other_user, bill.reviewer_ids) + bill.with_user(self.other_user).validate_tier() + with self.assertRaises(ValidationError): + bill.action_post() + bill.with_user(self.group_approver).validate_tier() + bill.action_post() + self.assertEqual(bill.state, "posted") + + def test_07_group_tier_skipped_without_group(self): + """The default group tier does not apply to bills without a group.""" + self._settings(require_approver_group_in_vendor_bills=True) + bill = self._create_bill( + self.vendor_no_group, approver_id=self.single_approver.id + ) + self.assertFalse(bill.need_validation) diff --git a/account_move_tier_validation_approver_group/views/account_move.xml b/account_move_tier_validation_approver_group/views/account_move.xml index 182eceb28..3855dd8e8 100644 --- a/account_move_tier_validation_approver_group/views/account_move.xml +++ b/account_move_tier_validation_approver_group/views/account_move.xml @@ -16,4 +16,17 @@ + + account.move.select - approver group + account.move + + + + + + + From 6eb6b3a0cacf8a32512274bf377ac3783aa29ed0 Mon Sep 17 00:00:00 2001 From: Andrea Stirpe Date: Thu, 22 Oct 2026 14:26:15 +0200 Subject: [PATCH 4/4] [DON'T MERGE] test-requirements.txt --- test-requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 test-requirements.txt diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 000000000..b8850d348 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,3 @@ +odoo-addon-base_tier_validation @ git+https://github.com/OCA/tier-validation.git@refs/pull/94/head#subdirectory=base_tier_validation +odoo-addon-account_move_tier_validation @ git+https://github.com/OCA/tier-validation.git@refs/pull/95/head#subdirectory=account_move_tier_validation +odoo-addon-account_move_tier_validation_approver @ git+https://github.com/OCA/tier-validation.git@refs/pull/111/head#subdirectory=account_move_tier_validation_approver