diff --git a/__manifest__.py b/__manifest__.py index 49e0a1fda8ddcf7e476506eaee406f7a5605d396..7f5f3e0cdd90377505116283eaf04a99deb2182a 100755 --- a/__manifest__.py +++ b/__manifest__.py @@ -16,6 +16,7 @@ "security/ir.model.access.csv", "datas/mail_data.xml", "datas/ir_sequence_data.xml", + "wizard/scop_cotisation_cg_regul_wizard.xml", "views/account_invoice.xml", "views/res_config_settings.xml", "views/scop_bordereau_cg.xml", diff --git a/models/scop_bordereau_cg.py b/models/scop_bordereau_cg.py index 870846e3f7b4657942ead0b3c6e6103d795f8b93..e86da6ce20d6d37fcf334e6e53befd70ed610020 100644 --- a/models/scop_bordereau_cg.py +++ b/models/scop_bordereau_cg.py @@ -17,8 +17,8 @@ class Bordereau(models.Model): name = fields.Char( string='Référence du bordereau', - readonly=True, copy=False) - + readonly=True, copy=False, default="Brouillon") + version = fields.Integer('Version', default=1, track_visibility='onchange') base_cotisation_cg = fields.Many2one( comodel_name='scop.cotisation.cg', string='Base de cotisation', @@ -31,6 +31,9 @@ class Bordereau(models.Model): store=True, required=False) date_cotisation = fields.Date("Date de cotisation") + is_regul = fields.Boolean("Regularisation", defaut=False) + date_regul = fields.Date("Date de régularisation") + comment_regul = fields.Char('Motif de régularisation') partner_id = fields.Many2one( comodel_name='res.partner', string='Adhérent', @@ -59,7 +62,8 @@ class Bordereau(models.Model): payment_term_id = fields.Many2one( comodel_name='account.payment.term', string="Conditions de paiement", - required=True + required=True, + track_visibility = 'onchange' ) payment_mode_id = fields.Many2one( comodel_name='account.payment.mode', string="Mode de paiment", @@ -474,9 +478,13 @@ class Bordereau(models.Model): Get contribution by type :return: dict type_contribution and amount """ - return self.invoice_ids.mapped(lambda c: { - 'type_contribution': c.type_contribution_id.name, - 'amount': c.amount_total_signed}) + return self.invoice_ids.read_group( + [('id', 'in', self.invoice_ids.ids)], + ['type_contribution_id', 'amount_total_signed'], + ['type_contribution_id']) + # return self.invoice_ids.mapped(lambda c: { + # 'type_contribution': c.type_contribution_id.name, + # 'amount': c.amount_total_signed}) # Email def get_recipients(self): diff --git a/models/scop_cotisation_cg.py b/models/scop_cotisation_cg.py index 6639559120102fab7d34c7453d18f09ca5f426cb..aa1a2a941e284075adebf1f90bbb0a1f38f79fde 100644 --- a/models/scop_cotisation_cg.py +++ b/models/scop_cotisation_cg.py @@ -604,7 +604,6 @@ class ScopCotisation(models.Model): bordereau.ca_retenu, bordereau.dureeExercice) va = self.check_dureeExercice( bordereau.va_cg_retenu, bordereau.dureeExercice) - if type_assiette == 'ca': rate = self.get_rate_ca(partner) cotiz = ca * rate @@ -649,7 +648,6 @@ class ScopCotisation(models.Model): bordereau.va_fede_com_retenu, bordereau.dureeExercice) staff_average = bordereau.staff_average_retenu staff_count = bordereau.staff_fede_com_count_retenu - if va != 0: cotiz = va * rate else: @@ -657,7 +655,6 @@ class ScopCotisation(models.Model): cotiz = staff_average * plancher else: cotiz = staff_count * plancher - if plancher <= cotiz <= plafond: return cotiz elif cotiz < plancher: diff --git a/wizard/__init__.py b/wizard/__init__.py index ed12f46ff2559e54fbdfb4e207bf31c231ae3742..b6ea02b566051375f2cfea92ac8f821262def251 100644 --- a/wizard/__init__.py +++ b/wizard/__init__.py @@ -4,4 +4,5 @@ from . import account_invoice_refund from . import scop_bordereau_update_confirm from . import scop_bordereau_validate_confirm +from . import scop_cotisation_cg_regul from . import scop_cotisation_cg_wizard diff --git a/wizard/scop_cotisation_cg_regul.py b/wizard/scop_cotisation_cg_regul.py new file mode 100644 index 0000000000000000000000000000000000000000..a6032076c983c2642c358f879ce3527af66285f5 --- /dev/null +++ b/wizard/scop_cotisation_cg_regul.py @@ -0,0 +1,296 @@ +# Copyright 2020 Le Filament +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models, api +from odoo.exceptions import UserError + +class ScopCotisationRegul(models.TransientModel): + _name = 'scop.cotisation.regul.wizard' + _description = 'Regularisation cotisation CG' + + name = fields.Char() + date_regul = fields.Date( + string="Date de régularisation", + default=fields.Date.today()) + bordereau_id = fields.Many2one( + comodel_name='scop.bordereau', + string='Bordereau', + readonly=True, + ) + partner_id = fields.Many2one( + comodel_name='res.partner', + string='Adhérent', + readonly=True, + ) + liasse_fiscale_id = fields.Many2one( + comodel_name='scop.liasse.fiscale', + string='Liasse Fiscale', + ) + liasse_fiscale_new_id = fields.Many2one( + comodel_name='scop.liasse.fiscale', + string='Liasse Fiscale', + ) + year = fields.Integer('Année de la liasse') + type_assiette_retenu = fields.Selection( + string='Type assiette de retenu', + selection=[('ca', 'CA'), + ('va', 'VA'), ], + ) + montant_assiette = fields.Float( + string='Montant assiette de cotisation',) + amount_total_cotiz = fields.Float( + string='Montant total de(s) cotisation(s)') + detail = fields.Text('Détail cotisation') + comment = fields.Char('Motif de régularisation') + type_assiette_retenu_new = fields.Selection( + string='Nouvelle assiette de retenu', + selection=[('ca', 'CA'), + ('va', 'VA'), ], + ) + ca_new = fields.Float('Montant CA') + va_new = fields.Float('Montant VA') + wage_cg_new = fields.Float('Masse Salariale') + amount_cg = fields.Float('Cotisation CG Scop') + amount_ur_med = fields.Float('Cotisation UR Méditerranée') + amount_ur_hdf = fields.Float('Cotisation UR HDF') + amount_fede_com = fields.Float('Cotisation Fédé Communication') + amount_fede_cae = fields.Float('Cotisation Fédé CAE') + + # ------------------------------------------------------ + # Override ORM + # ------------------------------------------------------ + @api.model + def default_get(self, fields): + res = super(ScopCotisationRegul, self).default_get(fields) + bordereau_id = self.env['scop.bordereau'].browse( + self.env.context.get('active_id') + ) + contribs = bordereau_id.invoice_ids.read_group( + [('id', 'in',bordereau_id.invoice_ids.ids)], + ['type_contribution_id', 'amount_total_signed'], + ['type_contribution_id']) + detail = "<table class='o_group o_inner_group'>" + for contrib in contribs: + detail += ('<tr><td class="o_td_label font-weight-bold">' + + str(contrib.get('type_contribution_id')[1]) + + '</td><td style="width: 100%;">' + + str(contrib.get('amount_total_signed')) + '€</td>') + detail += '</table>' + res.update({ + 'bordereau_id': bordereau_id.id, + 'partner_id': bordereau_id.partner_id.id, + 'liasse_fiscale_id': bordereau_id.liasse_fiscale_id.id, + 'year': bordereau_id.year_liasse_retenue, + 'type_assiette_retenu': bordereau_id.type_assiette_retenu, + 'montant_assiette': bordereau_id.montant_assiette, + 'amount_total_cotiz': bordereau_id.amount_total_cotiz, + 'detail': detail, + }) + return res + + # ------------------------------------------------------ + # Button function + # ------------------------------------------------------ + def get_new_contribution(self): + partner = self.partner_id + liasse = self.liasse_fiscale_new_id + # Duplicate bordereau to use in function + new_bdx = self.bordereau_id.copy(default={'state': 'new'}) + va = new_bdx.base_cotisation_cg.get_va(liasse) + new_bdx.update({ + 'liasse_fiscale_id': liasse.id, + 'ca_retenu': liasse.revenue_cgsubv, + 'va_fede_com_retenu': va, + 'va_cg_retenu': va, + 'wage_cg_retenu': liasse.wage_cg, + 'state': 'new', + }) + # Get variables + new_bdx._compute_type_assiette_retenu() + self.type_assiette_retenu_new = new_bdx.type_assiette_retenu + self.ca_new = new_bdx.ca_retenu + self.va_new = new_bdx.va_cg_retenu + self.wage_cg_new = new_bdx.wage_cg_retenu + + # Calcul CG Scop + amount_cg = new_bdx.base_cotisation_cg.round_to_closest_multiple( + new_bdx.base_cotisation_cg.get_cotiz_cg(partner, new_bdx), 4) + self.amount_cg = amount_cg + + # Calcul Fede Com + if partner.is_federation_com: + amount_fede_com = new_bdx.base_cotisation_cg.round_to_closest_multiple( + new_bdx.base_cotisation_cg.get_cotiz_fede_com(new_bdx), 4) + self.amount_fede_com = amount_fede_com + + # Calcul Fede CAE + if partner.cae: + amount_fede_cae = new_bdx.base_cotisation_cg.round_to_closest_multiple( + new_bdx.base_cotisation_cg.get_cotiz_fede_cae(), 4) + self.amount_fede_cae = amount_fede_cae + + # Calcul UR HDF + ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + if partner.ur_id.id == ur_hdf: + amount_ur_hdf = new_bdx.base_cotisation_cg.round_to_closest_multiple( + new_bdx.base_cotisation_cg.get_cotiz_hdf(new_bdx), 4) + self.amount_ur_hdf = amount_ur_hdf + + # Calcul UR Mediterranee + ur_med = self.env.ref('cgscop_partner.riga_14243').id + if partner.ur_id.id == ur_med: + amount_ur_med = new_bdx.base_cotisation_cg.round_to_closest_multiple( + new_bdx.base_cotisation_cg.get_cotiz_med( + self.amount_cg, new_bdx), 4) + self.amount_ur_med = amount_ur_med + + # Unlink bordereau + new_bdx.state = 'new' + new_bdx.unlink() + # Return Action + action = { + 'name': 'Régularisation Cotisation', + 'type': 'ir.actions.act_window', + 'res_model': 'scop.cotisation.regul.wizard', + 'view_mode': 'form', + 'target': 'new', + 'res_id': self.id, + 'context': self._context, + } + return action + + def update_contribution(self): + if not self.comment: + raise UserError( + "Vous devez renseigner un motif de régularisation pour valider.") + + bordereau_id = self.bordereau_id + + # CG Scop Treatment + product_cg_id = bordereau_id.company_id.contribution_cg_id + type_cotisation_cg = self.env.ref('cgscop_partner.riga_14397').id + journal_cg_id = self.bordereau_id.company_id.contribution_journal_id + invoice_cg = self.regul_invoice( + bordereau_id=self.bordereau_id, amount=self.amount_cg, + type_cotisation=type_cotisation_cg, product_id=product_cg_id, + journal_id=journal_cg_id) + new_invoices = invoice_cg + + # Invoice UR et Fédé + journal_ur_or_fede = self.bordereau_id.company_id.contribution_ur_or_fede_journal_id + type_cotisation_ur = self.env.ref('cgscop_partner.riga_14399').id + ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + ur_med = self.env.ref('cgscop_partner.riga_14243').id + + # Calcul Fede COM + if self.partner_id.is_federation_com: + type_cotisation_fede_com = self.env.ref( + 'cgscop_partner.riga_14398').id + account_id = self.bordereau_id.company_id.receivable_account_fede_com_id + product_fede_com_id = self.bordereau_id.company_id.contribution_fede_com_id + invoice_com = self.regul_invoice( + bordereau_id=self.bordereau_id, amount=self.amount_fede_com, + type_cotisation=type_cotisation_fede_com, + product_id=product_fede_com_id, + journal_id=journal_ur_or_fede, account_id=account_id, + ) + if invoice_com: + new_invoices += invoice_com + + # Calcul Fede CAE + if self.partner_id.cae: + type_cotisation_fede_cae = self.env.ref( + 'cgscop_partner.cotiz_fede_cae').id + account_id = self.bordereau_id.company_id.receivable_account_fede_cae_id + product_fede_cae_id = self.bordereau_id.company_id.contribution_fede_cae_id + invoice_cae = self.regul_invoice( + bordereau_id=self.bordereau_id, amount=self.amount_fede_cae, + type_cotisation=type_cotisation_fede_cae, + product_id=product_fede_cae_id, + journal_id=journal_ur_or_fede, + account_id=account_id, + ) + if invoice_cae: + new_invoices += invoice_cae + + # Calcul UR HDF + if self.partner_id.ur_id.id == ur_hdf: + product_hdf_id = self.bordereau_id.company_id.contribution_hdf_id + account_id = self.bordereau_id.company_id.receivable_account_ur_hdf_id + invoice_hdf = self.regul_invoice( + bordereau_id=self.bordereau_id, amount=self.amount_ur_hdf, + type_cotisation=type_cotisation_ur, product_id=product_hdf_id, + journal_id=journal_ur_or_fede, account_id=account_id, + ) + if invoice_hdf: + new_invoices += invoice_hdf + + # Calcul UR Mediterranee + if self.partner_id.ur_id.id == ur_med: + product_med_id = self.bordereau_id.company_id.contribution_med_id + account_id = self.bordereau_id.company_id.receivable_account_ur_med_id + invoice_med = self.regul_invoice( + bordereau_id=self.bordereau_id, amount=self.amount_ur_med, + type_cotisation=type_cotisation_ur, product_id=product_med_id, + journal_id=journal_ur_or_fede, account_id=account_id, + ) + if invoice_med: + new_invoices += invoice_med + + if new_invoices: + bordereau_id.version += 1 + bordereau_id.is_regul = True + bordereau_id.date_regul = self.date_regul + bordereau_id.comment_regul = self.comment + else: + raise UserError("Les montants des cotisations sont les mêmes que " + "les montants initiaux") + + # ------------------------------------------------------ + # Common function + # ------------------------------------------------------ + def regul_invoice( + self, bordereau_id, amount, type_cotisation, product_id, + journal_id, account_id=False): + """ + Create regularization invoice depending on amount difference and + contribution type + :param bordereau_id: bordereau + :param amount: adjustment amount + :param type_cotisation: contribution type + :param product_id: product for invoice line + :param journal_id: account journal + :param account_id: customer account + :return: invoice + """ + invoice_ids = bordereau_id.invoice_ids.filtered( + lambda i: i.type_contribution_id.id == type_cotisation + ) + if invoice_ids: + if sum(invoice_ids.mapped( + 'amount_total_signed')) != amount: + delta = amount - sum( + invoice_ids.mapped('amount_total_signed')) + if delta < 0: + type_invoice = 'out_refund' + delta = -delta + else: + type_invoice = 'out_invoice' + regul_invoice_id = bordereau_id.base_cotisation_cg.create_contribution( + product=product_id, partner=bordereau_id.partner_id, + type_contribution=type_cotisation, + liasse=self.liasse_fiscale_new_id, + amount=delta, date=self.date_regul, + journal_id=journal_id, account_id=account_id, + type_invoice=type_invoice, is_regul=True) + regul_invoice_id.update({ + 'cotisation_cg_id': bordereau_id.base_cotisation_cg.id, + 'bordereau_id': bordereau_id.id, + 'amount_cg_calculated': amount, + 'nb_quarter': bordereau_id.nb_quarter, + 'name': self.comment, + }) + regul_invoice_id.action_invoice_open() + return regul_invoice_id + return False + return False diff --git a/wizard/scop_cotisation_cg_regul_wizard.xml b/wizard/scop_cotisation_cg_regul_wizard.xml new file mode 100644 index 0000000000000000000000000000000000000000..6f66e57b82e48f2184e0cc9d2c96f57f10b39c65 --- /dev/null +++ b/wizard/scop_cotisation_cg_regul_wizard.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <!-- Copyright 2020 Le Filament + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> + <data> + + <record id="scop_cotisation_regul_wizard_form_view" model="ir.ui.view"> + <field name="name">scop.cotisation.regul.wizard.form</field> + <field name="model">scop.cotisation.regul.wizard</field> + <field name="arch" type="xml"> + <form string="Régularisation Cotisation"> + <sheet> + <div class="row"> + <div class="col-6"> + <label for="bordereau_id"/> + <h1> + <field name="bordereau_id" options='{"no_open": True}'/> + </h1> + </div> + <div class="col-6"> + <label for="partner_id"/> + <h3> + <field name="partner_id" options='{"no_open": True}'/> + </h3> + </div> + </div> + <div class="row"> + <div class="col-6"> + <group string="Données initiales" class="text-muted"> + <field name="liasse_fiscale_id" readonly="1" options='{"no_open": True}'/> + <field name="year" readonly="1"/> + <field name="type_assiette_retenu" readonly="1"/> + <field name="montant_assiette" readonly="1"/> + <field name="amount_total_cotiz" class="font-weight-bold" readonly="1"/> + </group> + <field name="detail" class="text-muted" readonly="1" widget="html"/> + </div> + <div class="col-6"> + <group string="Nouveaux calculs"> + <field name="date_regul" required="1"/> + <separator/> + <field name="liasse_fiscale_new_id" domain="[('partner_id', '=', partner_id)]" options='{"no_create": True}' help="Nouvelle liasse fiscale à prendre en compte"/> + </group> + <button name="get_new_contribution" type="object" string="Nouveau Calcul" class="btn-info"/> + <group> + <field name="type_assiette_retenu_new" attrs="{'invisible': [('amount_cg', '=', 0.0)]}" readonly="1"/> + <field name="ca_new" attrs="{'invisible': [('amount_cg', '=', 0.0)]}" readonly="1"/> + <field name="va_new" attrs="{'invisible': [('amount_cg', '=', 0.0)]}" readonly="1"/> + <field name="wage_cg_new" attrs="{'invisible': [('amount_cg', '=', 0.0)]}" readonly="1"/> + </group> + <group> + <field name="amount_cg" attrs="{'invisible': [('amount_cg', '=', 0.0)]}"/> + <field name="amount_fede_com" attrs="{'invisible': [('amount_fede_com', '=', 0.0)]}"/> + <field name="amount_fede_cae" attrs="{'invisible': [('amount_fede_cae', '=', 0.0)]}"/> + <field name="amount_ur_hdf" attrs="{'invisible': [('amount_ur_hdf', '=', 0.0)]}"/> + <field name="amount_ur_med" attrs="{'invisible': [('amount_ur_med', '=', 0.0)]}"/> + </group> + <label for="comment" attrs="{'invisible': [('amount_cg', '=', 0.0)]}"/> + <field name="comment" attrs="{'invisible': [('amount_cg', '=', 0.0)]}"/> + </div> + </div> + </sheet> + <footer> + <button name="update_contribution" type="object" string="Régulariser le bordereau" class="oe_highlight" confirm="Confirmer la validation de la régularisation"/> + <button string="Annuler" special="cancel" class="oe_link"/> + </footer> + </form> + </field> + </record> + + <record id="scop_cotisation_regul_wizard_act_window" model="ir.actions.act_window"> + <field name="name">Régularisation Cotisation</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">scop.cotisation.regul.wizard</field> + <field name="view_type">form</field> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> + + </data> +</odoo> \ No newline at end of file