diff --git a/models/res_partner.py b/models/res_partner.py index a125d1a2211aa249d1e3bd87aae67bc943e1d5f3..657ffa26a4e7ca283368ca1b194160a04f2267b4 100644 --- a/models/res_partner.py +++ b/models/res_partner.py @@ -8,6 +8,7 @@ from datetime import datetime, timedelta from odoo import _, api, fields, models from odoo.exceptions import UserError, ValidationError +from lxml import etree _logger = logging.getLogger(__name__) @@ -382,7 +383,24 @@ class ScopPartner(models.Model): ) # Révisions - revision_contract = fields.Date("Date du contrat de révision", tracking=True) + revision_contract = fields.Date( + string="Début du contrat de révision", + tracking=True, + ) + revision_contract_end = fields.Date( + string="Fin du contrat de révision", + tracking=True, + ) + revision_contract_tacite = fields.Boolean( + string="Tacite reconduction", + tracking=True, + default=False + ) + revision_mandat_cac = fields.Boolean( + string="Mandat CAC actif", + tracking=True, + default=False + ) revision_company_id = fields.Many2one( "res.partner", string="Organisme de révision", @@ -412,6 +430,9 @@ class ScopPartner(models.Model): revision_person_assign_date = fields.Date( string="Date de nomination du réviseur", tracking=True ) + revision_person_assign_end = fields.Date( + string="Date de fin nomination réviseur", tracking=True + ) revision_type = fields.Selection( [ ("1y", "Annuelle"), @@ -422,6 +443,18 @@ class ScopPartner(models.Model): string="Périodicité de la révision", tracking=True, ) + revision_type_ok = fields.Boolean( + string="Périodicité de révision ok", + tracking=True, + compute="_compute_revision_type_ok", + ) + revision_quinq_exercice = fields.Integer( + "Quinquénnale exercice à réviser", + compute="_compute_revision_quinq_exercice", + store=True, + readonly=False, + tracking=True, + ) revision_next_date = fields.Date("Prochain exercice révisable (old)") revision_format_id = fields.Many2one( "scop.revision.format", @@ -438,6 +471,10 @@ class ScopPartner(models.Model): store=True, readonly=False, ) + revision_same_exercice = fields.Boolean( + string="Révision sur l'année de cloture", + tracking=True, + ) revision_next_year = fields.Integer( "Année prochaine révision", compute="_compute_revision_next_year", @@ -449,8 +486,59 @@ class ScopPartner(models.Model): inverse_name="partner_id", string="Liste des Révisions", ) + revision_quinq_A1 = fields.Integer( + "Quinquénnale année 1", + compute="_compute_revision_quinq_annee", + store=True, + tracking=True, + ) + revision_quinq_A2 = fields.Integer( + "Quinquénnale année 2", + compute="_compute_revision_quinq_annee", + store=True, + tracking=True, + ) + revision_quinq_A3 = fields.Integer( + "Quinquénnale année 3", + compute="_compute_revision_quinq_annee", + store=True, + tracking=True, + ) + revision_quinq_A4 = fields.Integer( + "Quinquénnale année 4", + compute="_compute_revision_quinq_annee", + store=True, + tracking=True, + ) + revision_quinq_A5 = fields.Integer( + "Quinquénnale année 5", + compute="_compute_revision_quinq_annee", + store=True, + tracking=True, + ) + + revision_activite_A1 = fields.Integer( + "Activité révision année 1", + tracking=True, + ) + revision_activite_A2 = fields.Integer( + "Activité révision année 2", + tracking=True, + ) + revision_activite_A3 = fields.Integer( + "Activité révision année 3", + tracking=True, + ) + revision_activite_A4 = fields.Integer( + "Activité révision année 4", + tracking=True, + ) + revision_activite_A5 = fields.Integer( + "Activité révision année 5", + tracking=True, + ) - # Action RSE + # Action RSE et TE action_rse_ids = fields.One2many( comodel_name="scop.action.rse", inverse_name="partner_id", @@ -587,6 +675,34 @@ class ScopPartner(models.Model): def _check_zip(self): return + @api.constrains("revision_contract", "revision_contract_end") + def _check_revision_contract(self): + for rec in self: + if rec.revision_contract and \ + rec.revision_contract_end and \ + rec.revision_contract > rec.revision_contract_end: + raise ValidationError( + _( + "La date de fin du contrat de révision doit être supérieure" + " à la date de début du contrat de révision" + ) + ) + return + + @api.constrains("revision_person_assign_date", "revision_person_assign_end") + def _check_revision_person_assign(self): + for rec in self: + if rec.revision_person_assign_date and \ + rec.revision_person_assign_end and \ + rec.revision_person_assign_date > rec.revision_person_assign_end: + raise ValidationError( + _( + "La date de fin de nomination du réviseur doit être supérieure" + " à la date de début de nomination du réviseur" + ) + ) + return + @api.constrains("siret") def _check_siret(self): if self.siret and not self.env.context.get("import_file"): @@ -1168,10 +1284,15 @@ class ScopPartner(models.Model): else: partner.is_administrative = False - @api.depends("revision_next_exercice") + @api.depends("revision_next_exercice", + "revision_same_exercice", + ) def _compute_revision_next_year(self): for partner in self: - partner.revision_next_year = partner.revision_next_exercice + 1 + if partner.revision_same_exercice: + partner.revision_next_year = partner.revision_next_exercice + else: + partner.revision_next_year = partner.revision_next_exercice + 1 @api.depends( "revision_type", @@ -1203,7 +1324,8 @@ class ScopPartner(models.Model): partner.revision_next_exercice = base_rev + 1 # Cas d'une révision quinquénnale elif partner.revision_type == "5y": - partner.revision_next_exercice = base_rev + 5 + self._compute_revision_quinq_exercice() + partner.revision_next_exercice = partner.revision_quinq_exercice # Cas d'une révision quinquénnale séquencée (annuelle) elif partner.revision_type == "5ys": partner.revision_next_exercice = base_rev + 1 @@ -1221,6 +1343,68 @@ class ScopPartner(models.Model): # Pas de révision précédente partner.revision_next_exercice = base_rev + 2 + @api.depends( + "revision_type", + "revision_ids", + "revision_ids.revision_result_year", + "revision_ids.revision_type", + "first_closeout", + ) + def _compute_revision_quinq_exercice(self): + for partner in self: + # Si aucune périodicité de défini, on n'insiste pas + if not partner.revision_type or not partner.first_closeout: + partner.revision_quinq_exercice = False + else: + # On commence par regarder si l'on a des révisions classique ou speciale + last_rev = partner.revision_ids.filtered( + lambda p: p.revision_type != "int" + ).sorted( + key=lambda r: r.revision_result_year, reverse=True + ) + + # On calcule l'année de référence du calcul + if len(last_rev) > 0: + # si On a déjà révisé un exercice il devient la base du calcul + base_rev = last_rev[0].revision_result_year + else: + base_rev = partner.first_closeout.year - 1 + + # On calcule l'année de révision de la quinquénale + partner.revision_quinq_exercice = base_rev + 5 + + @api.depends( + "revision_quinq_exercice", + "revision_same_exercice", + ) + def _compute_revision_quinq_annee(self): + for partner in self: + if partner.revision_same_exercice: + partner.revision_quinq_A5 = partner.revision_quinq_exercice + partner.revision_quinq_A4 = partner.revision_quinq_exercice -1 + partner.revision_quinq_A3 = partner.revision_quinq_exercice -2 + partner.revision_quinq_A2 = partner.revision_quinq_exercice -3 + partner.revision_quinq_A1 = partner.revision_quinq_exercice -4 + else: + partner.revision_quinq_A5 = partner.revision_quinq_exercice + 1 + partner.revision_quinq_A4 = partner.revision_quinq_exercice + partner.revision_quinq_A3 = partner.revision_quinq_exercice - 1 + partner.revision_quinq_A2 = partner.revision_quinq_exercice - 2 + partner.revision_quinq_A1 = partner.revision_quinq_exercice - 3 + + @api.depends( + "revision_type", + "revision_mandat_cac", + ) + def _compute_revision_type_ok(self): + for partner in self: + partner.revision_type_ok = True + if (partner.revision_type == "1y") and (partner.revision_mandat_cac) : + partner.revision_type_ok = False + if (partner.revision_type == "1y") and (partner.cooperative_form_id.name == "SCIC") : + partner.revision_type_ok = False + + # ------------------------------------------------------ # Button & Action # ------------------------------------------------------ diff --git a/models/scop_revision.py b/models/scop_revision.py index d63bd928ad610602f9e70df0c44d6f0d6d2f2a64..b47a1d568c5c9264ba39b4a96a72c00d619bf95c 100644 --- a/models/scop_revision.py +++ b/models/scop_revision.py @@ -21,6 +21,12 @@ class ScopRevision(models.Model): ) date = fields.Date("Date de révision", index=True) delegate_id = fields.Many2one("res.users", string="Réviseur", ondelete="restrict") + certified_person_id = fields.Many2one( + comodel_name="res.users", + string="Réviseur agréé", + ondelete="restrict", + tracking=True, + ) revision_result_year = fields.Integer("Exercice révisé") revision_staff = fields.Integer("Effectif") revision_format_id = fields.Many2one( @@ -30,6 +36,15 @@ class ScopRevision(models.Model): related="partner_id.revision_format_id", store=True, ) + revision_type = fields.Selection( + [ + ("std", "Classique"), + ("int", "Intermédiaire"), + ("spc", "Spéciale"), + ], + string="Type de révision", + default="std", + ) revision_followup = fields.Selection( [ ("ndef", "Non défini"), diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index bcea7df51584f8b1b3e31de265d5c84c6544b682..c18d005c92983bf910847dd85c43fe9fbbdd81d4 100644 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -15,7 +15,7 @@ access_res_partner_segment1,access_res_partner_segment1,model_res_partner_segmen access_res_partner_segment2,access_res_partner_segment2,model_res_partner_segment2,base.group_user,1,0,0,0 access_res_partner_segment3,access_res_partner_segment3,model_res_partner_segment3,base.group_user,1,0,0,0 access_res_partner_segment4,access_res_partner_segment4,model_res_partner_segment4,base.group_user,1,0,0,0 -access_revision_group_user,access_revision_group_user,model_scop_revision,base.group_user,1,1,1,0 +access_revision_group_user,access_revision_group_user,model_scop_revision,base.group_user,1,1,1,1 access_scop_contribution_type,access_scop_contribution_type,model_scop_contribution_type,base.group_user,1,0,0,0 access_scop_membership_period,access_scop_membership_period,model_scop_membership_period,base.group_user,1,0,0,0 access_scop_membership_reason_end,access_scop_membership_reason_end,model_scop_membership_reason_end,base.group_user,1,0,0,0 diff --git a/views/res_partner.xml b/views/res_partner.xml index 607f6376fe79ec8c606a5b7faf976bcc6e0c36be..d6e68793333451083c668b5e31d70dea5129da93 100644 --- a/views/res_partner.xml +++ b/views/res_partner.xml @@ -1479,9 +1479,19 @@ string="Révisions" attrs="{'invisible': ['|', ('is_cooperative', '!=', True), ('project_status', 'not in', ['4_suivi', '6_decede'])]}" > + <field name="revision_type_ok" invisible="1"/> + <div + class="alert alert-danger" + role="alert" + attrs="{'invisible':[('revision_type_ok','=',True)]}" + > + La périodicité de la révision est incompatible avec les caractéristiques de la coopérative + </div> <group col="3"> <group string="Contrat de révision"> <field name="revision_contract" /> + <field name="revision_contract_end" /> + <field name="revision_contract_tacite" /> <field name="revision_company_id" options="{'no_create': True}" @@ -1499,6 +1509,9 @@ options="{'no_open': True, 'no_create': True}" /> <field name="revision_person_assign_date" /> + <field name="revision_person_assign_end" /> + <br/> + <field name="revision_mandat_cac" /> </group> <group string="Caractéristiques de la révision"> @@ -1515,15 +1528,56 @@ /> </group> - <group string="Prochaine révision"> - <field - name="revision_next_exercice" - attrs="{'required': [('revision_contract','!=', False)]}" - /> - <field - name="revision_next_year" - attrs="{'required': [('revision_contract','!=', False)]}" - /> + <group col="1"> + <group string="Prochaine révision"> + <field + name="revision_quinq_exercice" + attrs="{ + 'required': [('revision_contract','!=', False)], + 'invisible': [('revision_type','not in', ('5y','5ys','5ys23'))] + }" + /> + <field + name="revision_next_exercice" + attrs="{'required': [('revision_contract','!=', False)]}" + /> + <field name="revision_same_exercice" /> + <field + name="revision_next_year" + attrs="{'required': [('revision_contract','!=', False)]}" + /> + </group> + <group + string="Activité quinquénale" + attrs="{ + 'invisible': [('revision_type','not in', ('5y','5ys','5ys23'))] + }"> + <label for="revision_activite_A1"/> + <div class="o_row"> + <field name="revision_activite_A1" class="w-25"/> + <span> (<field name="revision_quinq_A1" nolabel="1" />)</span> + </div> + <label for="revision_activite_A2"/> + <div class="o_row"> + <field name="revision_activite_A2" class="w-25"/> + <span> (<field name="revision_quinq_A2" nolabel="1" />)</span> + </div> + <label for="revision_activite_A3"/> + <div class="o_row"> + <field name="revision_activite_A3" class="w-25"/> + <span> (<field name="revision_quinq_A3" nolabel="1" />)</span> + </div> + <label for="revision_activite_A4"/> + <div class="o_row"> + <field name="revision_activite_A4" class="w-25"/> + <span> (<field name="revision_quinq_A4" nolabel="1" />)</span> + </div> + <label for="revision_activite_A5"/> + <div class="o_row"> + <field name="revision_activite_A5" class="w-25"/> + <span> (<field name="revision_quinq_A5" nolabel="1" />)</span> + </div> + </group> </group> </group> @@ -1535,6 +1589,11 @@ required="1" options="{'no_open': True, 'no_create': True}" /> + <field + name="certified_person_id" + options="{'no_open': True, 'no_create': True}" + /> + <field name="revision_type" required="1" /> <field name="date" required="1" /> <field name="revision_followup" required="1" /> <field name="facture" /> diff --git a/views/res_partner_cooperative.xml b/views/res_partner_cooperative.xml index b67da9edb280a99ec51a04e350c6d482748ff1e9..6d145ab62d0e2961533299355bb570f9ab445400 100644 --- a/views/res_partner_cooperative.xml +++ b/views/res_partner_cooperative.xml @@ -232,6 +232,12 @@ <field name="dissolution_reason_id" optional="hide" /> <field name="membership_status" optional="show" /> <field name="staff_last" optional="hide" /> + <field name="revision_person_id" optional="hide" /> + <field name="revision_certified_person_id" optional="hide" /> + <field name="revision_type" optional="hide" /> + <field name="revision_format_id" optional="hide" /> + <field name="revision_next_exercice" optional="hide" /> + <field name="revision_next_year" optional="hide" /> </tree> </field> </record>