diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..75bb2042901930f807a588241612a29dd75e34d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.* +*.pyc +!.gitignore diff --git a/__init__.py b/__init__.py index 8871070722787374e6f51f62f0f636847d986285..f88059b176b68c16a1a1024c635a43d555e9229c 100755 --- a/__init__.py +++ b/__init__.py @@ -1,6 +1,7 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. +# © 2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import controllers from . import models +from . import report from . import wizard diff --git a/__manifest__.py b/__manifest__.py index 09e96df5398bb489d9cb21320540f9bbce31175a..54db846ab3ed89ec29ec6d4d43cd494a2f74f33f 100755 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,3 +1,5 @@ +# © 2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "CG SCOP - Cotisations CG", "summary": "CG SCOP - Cotisations CG Scop", @@ -11,25 +13,39 @@ "l10n_fr", "lefilament_export_journal_tool", "cgscop_cotisation", + "queue_job_batch", ], "data": [ + # Security "security/security_rules.xml", "security/ir.model.access.csv", + # Datas + "datas/bordereau_refund_wizard_quarter_data.xml", "datas/mail_data.xml", + "datas/queue_job_data.xml", "datas/ir_sequence_data.xml", - "wizard/scop_cotisation_cg_regul_wizard.xml", - "report/report_scop_bordereau.xml", - "report/report_scop_bordereau_payments.xml", - "report/report_scop_bordereau_refund.xml", + # Reports + "templates/report_scop_bordereau.xml", + "templates/report_scop_bordereau_payments.xml", + "templates/report_scop_bordereau_refund.xml", + # Views "views/account_invoice.xml", "views/res_config_settings.xml", - "views/scop_bordereau_cg.xml", "views/scop_cotisation_cg.xml", "views/scop_cotisation_simulation.xml", "views/scop_liasse_fiscale.xml", + # Wizards "wizard/export_journal_wizard_view.xml", + "wizard/scop_cotisation_cg_regul_wizard.xml", + "wizard/scop_bordereau_update_liasse_wizard.xml", + "wizard/scop_bordereau_payment_mode_wizard.xml", + "wizard/scop_bordereau_refund_wizard.xml", "wizard/scop_cotisation_cg_wizard.xml", "wizard/scop_bordereau_update_confirm_view.xml", "wizard/scop_bordereau_validate_confirm_view.xml", + # Wizard dependency view + "views/scop_bordereau_cg.xml", + # Menus + "views/menus.xml", ] } diff --git a/datas/bordereau_refund_wizard_quarter_data.xml b/datas/bordereau_refund_wizard_quarter_data.xml new file mode 100644 index 0000000000000000000000000000000000000000..213e9e1c3c7d87a3994aa33dae104d47dce78123 --- /dev/null +++ b/datas/bordereau_refund_wizard_quarter_data.xml @@ -0,0 +1,24 @@ +<?xml version="1.0"?> +<!-- Copyright 2021 Le Filament (<https://www.le-filament.com>) + License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). --> +<odoo> + <data noupdate="1"> + + <record id="bordereau_wizard_quarter1" model="scop.bordereau.refund.wizard.quarter"> + <field name="quarter">1</field> + </record> + + <record id="bordereau_wizard_quarter2" model="scop.bordereau.refund.wizard.quarter"> + <field name="quarter">2</field> + </record> + + <record id="bordereau_wizard_quarter3" model="scop.bordereau.refund.wizard.quarter"> + <field name="quarter">3</field> + </record> + + <record id="bordereau_wizard_quarter4" model="scop.bordereau.refund.wizard.quarter"> + <field name="quarter">4</field> + </record> + + </data> +</odoo> \ No newline at end of file diff --git a/datas/queue_job_data.xml b/datas/queue_job_data.xml new file mode 100644 index 0000000000000000000000000000000000000000..01f22859e7e811dfc5d607083dc3a157edc9db19 --- /dev/null +++ b/datas/queue_job_data.xml @@ -0,0 +1,24 @@ +<?xml version="1.0"?> +<!-- Copyright 2021 Le Filament (<https://www.le-filament.com>) + License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). --> +<odoo> + <data noupdate="1"> + + <record id="job_function_cotisation_cg_create_bordereau" model="queue.job.function"> + <field name="model_id" ref="cgscop_cotisation_cg.model_scop_cotisation_cg" /> + <field name="method">create_bordereau</field> + <field name="channel_id" ref="queue_job.channel_root" /> + <field name="related_action" eval='{"func_name": "related_action_custom", "kwargs": {"model": "scop.bordereau", "record": "result"}}' /> + <field name="retry_pattern" eval="{1: 10, 5: 30, 10: 300}" /> + </record> + + <record id="job_function_cotisation_cg_validate_bordereau" model="queue.job.function"> + <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau" /> + <field name="method">validate_bordereau</field> + <field name="channel_id" ref="queue_job.channel_root" /> + <field name="related_action" eval='{"func_name": "related_action_custom", "kwargs": {"record": "result"}}' /> + <field name="retry_pattern" eval="{1: 10, 5: 30, 10: 300}" /> + </record> + + </data> +</odoo> \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py index 14a36e45970b8b7d70501ae26d038f07fc6e1863..c34981509f9c6ecfd42f18c4df619cc48a4f5832 100755 --- a/models/__init__.py +++ b/models/__init__.py @@ -5,6 +5,7 @@ from . import account_invoice from . import res_company from . import res_config_settings from . import scop_bordereau_cg +from . import scop_bordereau_cg_version from . import scop_cotisation_simulation -from . import scop_cotisation_cg from . import scop_liasse_fiscale +from . import scop_cotisation_cg diff --git a/models/account_invoice.py b/models/account_invoice.py index ba7f3aebc1ec29a5b474512c1e11fb810bd10c48..b8c7e828bc0d64344d0483b78f2232874293c81d 100644 --- a/models/account_invoice.py +++ b/models/account_invoice.py @@ -7,104 +7,33 @@ from odoo import models, fields, api class ScopAccountInvoiceCG(models.Model): _inherit = "account.invoice" - cotisation_cg_id = fields.Many2one( - comodel_name='scop.cotisation.cg', - string='Base de cotisation CG Scop') bordereau_id = fields.Many2one( comodel_name='scop.bordereau', string='Bordereau de rattachement', + ondelete='cascade', required=False,) + cotisation_cg_id = fields.Many2one( + related='bordereau_id.base_cotisation_cg') amount_cg_calculated = fields.Monetary( - string="Montant calculé annuel", currency_field='company_currency_id', - readonly=True) - amount_cg_prorata = fields.Monetary( - string="Montant calculé proratisé", - currency_field='company_currency_id', - compute='_compute_amount_cg_prorata', - store=True, readonly=True) + string="Montant cotisation annuel", + currency_field='company_currency_id', readonly=True) + nb_quarter = fields.Selection(related='bordereau_id.nb_quarter') + cotiz_quarter = fields.Selection( + string='Trimestre', + selection=[(1, 1), (2, 2), (3, 3), (4, 4)], + required=False, ) # ------------------------------------------------------ # Compute fields # ------------------------------------------------------ - @api.depends('amount_cg_calculated', 'nb_quarter') - @api.multi - def _compute_amount_cg_prorata(self): - for i in self: - prorata = i.nb_quarter / 4 - i.amount_cg_prorata = i.cotisation_cg_id.\ - round_to_closest_multiple( - i.amount_cg_calculated * prorata, i.nb_quarter) # ------------------------------------------------------ # Override parent # ------------------------------------------------------ - @api.multi - def set_scop_contribution(self): - contrib_id = super(ScopAccountInvoiceCG, self).set_scop_contribution() - if self.cotisation_cg_id: - i = 4 - self.nb_quarter - schedule = { - 'quarter_1': self.cotisation_cg_id.trimester_1, - 'quarter_2': self.cotisation_cg_id.trimester_2, - 'quarter_3': self.cotisation_cg_id.trimester_3, - 'quarter_4': self.cotisation_cg_id.trimester_4, - } - while i != 0: - key = 'quarter_' + str(i) - schedule.pop(key, None) - i -= 1 - contrib_id.update(schedule) - return contrib_id - - def set_scop_contribution_hook(self, contrib_id): - """ - Compute contribution line amount if bordereau regul - :param contrib_id: - :return: - """ - super(ScopAccountInvoiceCG, self).set_scop_contribution_hook(contrib_id) - if self.bordereau_id: - invoice_ids = self.bordereau_id.invoice_ids.filtered( - lambda i: i.type_contribution_id == contrib_id.type_id - ) - contrib_id.update({ - 'amount_calculated': sum(invoice_ids.mapped('amount_total_signed')), - 'amount_called': sum(invoice_ids.mapped('amount_total_signed')), - }) - return contrib_id # ------------------------------------------------------ # Global functions # ------------------------------------------------------ - def recalcul_cotiz_cg(self): - """ - Affecte la valeur calculée à la première ligne cotisation - existante dans la facture - """ - cotisation_cg = self.company_id.contribution_cg_id - cotisation_fede_com = self.company_id.contribution_fede_com_id - cotisation_fede_cae = self.company_id.contribution_fede_cae_id - cotisation_hdf = self.company_id.contribution_hdf_id - cotisation_med = self.company_id.contribution_med_id - invoice_line_cotisation_id = self.invoice_line_ids.filtered( - lambda r: r.product_id in (cotisation_cg, - cotisation_fede_com, - cotisation_fede_cae, - cotisation_hdf, cotisation_med)) - if len(invoice_line_cotisation_id) == 0: - message_id = self.env['message.wizard'].create( - {'message': "Pas d'article cotisation sélectionné !"}) - return { - 'name': 'Action impossible', - 'type': 'ir.actions.act_window', - 'view_mode': 'form', - 'res_model': 'message.wizard', - 'res_id': message_id.id, - 'target': 'new' - } - else: - invoice_line_cotisation_id[0].price_unit = self.amount_cg_prorata - def view_cotiz(self): form_view = self.env.ref( 'cgscop_cotisation_cg.invoice_form_scop_cg_inherited').id diff --git a/models/scop_bordereau_cg.py b/models/scop_bordereau_cg.py index f205842f627be8cbc0ea5e5bf4417d386b69a446..3328aceeaf8d5ff2434d1a4f7f72f9fabd06afb2 100644 --- a/models/scop_bordereau_cg.py +++ b/models/scop_bordereau_cg.py @@ -1,10 +1,9 @@ # © 2021 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import fields, models, api, registry +from odoo import fields, models, api from odoo.exceptions import UserError import logging -import threading _logger = logging.getLogger(__name__) @@ -48,6 +47,10 @@ class Bordereau(models.Model): string='Liasse fiscale de référence', readonly=True, track_visibility='onchange') + liasse_count = fields.Integer( + string='Nombre de liasses', + compute='_compute_liasse_count' + ) type_liasse_fiscale = fields.Selection( string='Type de liasse', related='liasse_fiscale_id.type_id') @@ -65,12 +68,6 @@ class Bordereau(models.Model): related='partner_id.ur_id', store=True ) - payment_term_id = fields.Many2one( - comodel_name='account.payment.term', - string="Conditions de paiement", - required=True, - track_visibility = 'onchange' - ) payment_mode_id = fields.Many2one( comodel_name='account.payment.mode', string="Mode de paiment", ondelete='restrict', @@ -82,16 +79,21 @@ class Bordereau(models.Model): inverse_name='bordereau_id', string='Cotisations', required=False) + invoice_count = fields.Integer( + string='Nombre d\'appels', + compute='_compute_invoice_count' + ) state = fields.Selection([ ('new', 'Brouillon'), + ('ongoing', 'En cours de modification'), ('validated', 'Validé'), ('paid', 'Payé'), ('cancel', 'Annulé')], string="Statut", default='new', compute='_compute_state', store=True, - track_visibility='onchange',) + track_visibility='onchange', ) company_id = fields.Many2one( comodel_name='res.company', @@ -113,90 +115,53 @@ class Bordereau(models.Model): compute='_compute_amount_residual', store=True) nb_quarter = fields.Selection( - string='Nombre de trimestres de cotisation', + string='Nb de trimestres de cotisation', selection=[(1, '1'), (2, '2'), (3, '3'), (4, '4')], default='4', required=True, track_visibility='onchange') + details = fields.Html('Détails', compute='_compute_details') # Assiettes for cotiz calcul - year_liasse_connu = fields.Integer( - string='Année de la liasse calculée', + year_liasse = fields.Integer( + string='Année de la liasse', related='liasse_fiscale_id.year', - track_visibility='onchange' + track_visibility='onchange', + oldname='year_liasse_retenue' ) - year_liasse_retenue = fields.Integer( - string='Année de la liasse retenue', - track_visibility='onchange' - ) - type_assiette_connu = fields.Selection( - string='Type d\'assiette de cotisation calculé', - selection=[('ca', 'CA'), - ('va', 'VA'), ], - compute='_compute_type_assiette_connu', - readonly=True) - type_assiette_retenu = fields.Selection( - string='Type d\'assiette de cotisation retenu', - selection=[('ca', 'CA'), - ('va', 'VA'), ], - compute='_compute_type_assiette_retenu', - store=True, - track_visibility='onchange') - montant_assiette = fields.Float( - string='Montant assiette de cotisation', - compute='_compute_montant_assiette') - ca_connu = fields.Float( - string='CA connu', - readonly=True, - track_visibility='onchange') - ca_retenu = fields.Float( - string='CA retenu', - track_visibility='onchange') - va_connu = fields.Float( - string='VA connu', - readonly=True, - track_visibility='onchange') - va_cg_retenu = fields.Float( - string='VA retenu pour cotisation CG', - track_visibility='onchange') - va_fede_com_retenu = fields.Float( - string='VA retenu pour cotisation Fede com', - track_visibility='onchange') - staff_count_connu = fields.Integer( - string='Effectif de la coop connu', - readonly=True) - staff_fede_com_count_retenu = fields.Integer( - string='Effectif de la coop retenu (Fédé Com)', - track_visibility='onchange') - staff_ur_med_count_retenu = fields.Integer( - string='Effectif de la coop retenu (UR)', - track_visibility='onchange') - staff_shareholder_count_connu = fields.Integer( - string='Effectif sociétaires de la coop connu', - readonly=True) - staff_shareholder_count_retenu = fields.Integer( - string='Effectif sociétaires de la coop retenu', - track_visibility='onchange') - staff_average_connu = fields.Float( - string='Effectif moyen de la coop connu', - readonly=True) - staff_average_retenu = fields.Float( - string='Effectif moyen de la coop retenu', - track_visibility='onchange') - net_results_connu = fields.Float( - string='Résultat net connu', - readonly=True) - net_results_retenu = fields.Float( - string='Résultat net retenu', - track_visibility='onchange') - wage_cg_connu = fields.Float( - string='Montant masse salariale connu', - readonly=True) - wage_cg_retenu = fields.Float( - string='Montant masse salariale retenu', - track_visibility='onchange') + type_assiette = fields.Selection( + related='liasse_fiscale_id.contribution_base_type', store=True) + montant_assiette = fields.Integer( + related='liasse_fiscale_id.contribution_base_amount', store=True) + ca = fields.Float( + string='CA', + compute='_compute_values_calculation', store=True, + track_visibility='onchange', + oldname='ca_retenu') + va = fields.Float( + string='VA', + compute='_compute_values_calculation', store=True, + track_visibility='onchange', + oldname='va_cg_retenu') + staff_count = fields.Integer( + string='Effectif de la coop', + readonly=True, oldname='staff_count_connu') + staff_shareholder_count = fields.Integer( + string='Effectif sociétaires de la coop', + readonly=True, oldname='staff_shareholder_count_retenu') + staff_average = fields.Float( + string='Effectif moyen de la coop', + readonly=True, oldname='staff_average_retenu') + net_results = fields.Float( + string='Résultat net', + compute='_compute_values_calculation', store=True, + oldname='net_results_retenu') + wage_cg = fields.Float( + string='Montant masse salariale', + compute='_compute_values_calculation', store=True, + oldname='wage_cg_retenu') is_sdd = fields.Boolean( 'Au prélèvement', compute='compute_is_sdd', @@ -211,10 +176,6 @@ class Bordereau(models.Model): comodel_name='scop.bordereau', string='Bordereau initial', ) - move_reconciled = fields.Boolean( - 'Paiements en cours', - compute='_compute_move_reconciled' - ) # Emails management recipient_ids = fields.One2many( comodel_name='res.partner', @@ -227,24 +188,48 @@ class Bordereau(models.Model): # compute='_compute_emails' # ) + # Historique des versions + bordereau_version_ids = fields.One2many( + comodel_name='scop.bordereau.version', + inverse_name='bordereau_id', + string='Historique' + ) + has_outstanding = fields.Boolean( + string='Paiements en circulation', + compute='_compute_has_outstanding', store=True + ) + # ------------------------------------------------------ # Compute # ------------------------------------------------------ + @api.depends('liasse_fiscale_id') @api.multi - def _compute_name(self): - for bordereau in self: - bordereau.name = "Bordereau %s - %s" % ( - bordereau.base_cotisation_cg.year, - bordereau.partner_id.name) + def _compute_values_calculation(self): + for r in self: + if r.liasse_fiscale_id: + liasse = r.liasse_fiscale_id + r.ca = liasse.revenue_cgsubv + r.va = r.base_cotisation_cg.get_va(liasse) + r.net_results = liasse.L2053_HN \ + if liasse.L2053_HN > 0 else liasse.L2051_DI + r.wage_cg = liasse.wage_cg + else: + r.ca = r.va = r.net_results = r.wage_cg = 0 - @api.depends('invoice_ids.state') + @api.depends('invoice_ids.state', + 'bordereau_version_ids', 'bordereau_version_ids.state') @api.multi def _compute_state(self): for bordereau in self: draft_cotiz = bordereau.invoice_ids.filtered( lambda cotiz: cotiz.state == 'draft' ) - if draft_cotiz: + ongoing_version = bordereau.bordereau_version_ids.filtered( + lambda v: v.state == 'new' + ) + if ongoing_version: + bordereau.state = 'ongoing' + elif draft_cotiz: bordereau.state = 'new' else: unpaid_cotiz = bordereau.invoice_ids.filtered( @@ -261,7 +246,17 @@ class Bordereau(models.Model): for bordereau in self: bordereau.year = bordereau.base_cotisation_cg.year - @api.depends('invoice_ids', 'invoice_ids.amount_total_signed') + @api.multi + def _compute_invoice_count(self): + for r in self: + r.invoice_count = len(r.invoice_ids) + + @api.multi + def _compute_liasse_count(self): + for r in self: + r.liasse_count = len(r.partner_id.liasse_fiscale_ids) + + @api.depends('invoice_count', 'invoice_ids.amount_total_signed') @api.multi def _compute_amount_total_cotiz(self): for r in self: @@ -288,35 +283,19 @@ class Bordereau(models.Model): bordereau.recipient_ids = child_ids @api.multi - def _compute_type_assiette_connu(self): - for bordereau in self: - bordereau.type_assiette_connu = \ - bordereau.base_cotisation_cg.get_type_assiette( - bordereau.ca_connu, bordereau.va_connu) - - @api.depends('ca_retenu', 'va_cg_retenu') - @api.multi - def _compute_type_assiette_retenu(self): + def _compute_type_assiette(self): for bordereau in self: - bordereau.type_assiette_retenu = \ + bordereau.type_assiette = \ bordereau.base_cotisation_cg.get_type_assiette( - bordereau.ca_retenu, bordereau.va_cg_retenu) + bordereau.ca, bordereau.va) @api.multi - def _search_type_assiette_retenu(self, operator, value): + def _search_type_assiette(self, operator, value): recs = self.search([]).filtered( - lambda r: r.type_assiette_retenu == value) + lambda r: r.type_assiette == value) if recs: return [('id', 'in', [x.id for x in recs])] - @api.multi - def _compute_montant_assiette(self): - for bordereau in self: - if bordereau.type_assiette_retenu == 'ca': - bordereau.montant_assiette = bordereau.ca_retenu - else: - bordereau.montant_assiette = bordereau.va_cg_retenu - @api.multi def compute_is_sdd(self): sdd_id = self.env.ref( @@ -333,20 +312,6 @@ class Bordereau(models.Model): if recs: return [('id', 'in', [x.id for x in recs])] - @api.multi - def _compute_move_reconciled(self): - self.ensure_one() - payments_ids = self.env['account.payment'].search([ - ('partner_id', '=', self.partner_id.id)]) - payments = list( - map(lambda p: self.get_move_reconciled(p), payments_ids)) - if True in payments: - payments.remove(True) - if payments: - self.move_reconciled = False - else: - self.move_reconciled = True - @api.multi def _compute_is_liasse_previ(self): for bordereau in self: @@ -355,18 +320,60 @@ class Bordereau(models.Model): else: bordereau.is_liasse_previ = False + @api.depends('invoice_ids.residual') + @api.multi + def _compute_has_outstanding(self): + for r in self: + if r.invoice_ids: + if r.invoice_ids.filtered(lambda i: i.has_outstanding is True): + r.has_outstanding = True + else: + r.has_outstanding = False + else: + r.has_outstanding = False + + @api.multi + def _compute_details(self): + for r in self: + contribs = r.invoice_ids.read_group( + [('id', 'in', r.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></tr>') + detail += "</table><table class='o_group o_inner_group'>" + for i in range(1, 5): + amount_echeance = round(sum(self.invoice_ids.filtered( + lambda inv: inv.cotiz_quarter == i + ).mapped('amount_total_signed')), 2) + detail += '<tr><td class="o_td_label font-weight-bold">' \ + + 'Trimestre ' + str(i) \ + + '</td><td style="width: 100%;">' \ + + str(amount_echeance) + ' €</td></tr>' + detail += '</table>' + r.details = detail + # ------------------------------------------------------ # Button functions # ------------------------------------------------------ - @api.multi def validate_cotiz_cg(self): - for bordereau in self: - for inv in bordereau.invoice_ids: + self.ensure_one() + for inv in self.invoice_ids: + if inv.state == 'draft': + if not inv.date_invoice: + inv.update({ + 'date_invoice': self.date_cotisation, + }) inv.update({ - 'date_invoice': bordereau.date_cotisation, - 'payment_mode_id': bordereau.payment_mode_id, + 'payment_mode_id': self.payment_mode_id, }) - if bordereau.is_sdd and not inv.mandate_id: + if self.is_sdd and not inv.mandate_id: raise UserError( "Vous ne pouvez pas valider une cotisation au " "prélèvement sans mandat" @@ -374,18 +381,29 @@ class Bordereau(models.Model): else: inv.action_invoice_open() - @api.multi def validate_bordereau(self): + """ + Affecte le nom du bordereau avec la séquence et valide les invoices + """ self.ensure_one() if self.name == 'Brouillon': self.name = self.env['ir.sequence'].next_by_code('scop.bordereau') self.validate_cotiz_cg() + return self.id @api.multi def validate_bordereau_multi(self): - threaded_validate_bordereau = threading.Thread( - target=self.process_bordereau_validate) - threaded_validate_bordereau.start() + """ + Utilise job queue pour valider les bordereaux + """ + batch_name = (fields.Datetime.to_string(fields.Datetime.now()) + + " Validation bordereaux ") + batch = self.env['queue.job.batch'].get_new_batch(batch_name) + for bordereau in self: + bordereau.with_context( + job_batch=batch + ).with_delay().validate_bordereau() + batch.enqueue() @api.multi def print_bordereau(self): @@ -425,30 +443,139 @@ class Bordereau(models.Model): 'context': ctx, } - @api.multi - def update_cotiz_and_lines(self): - for bordereau in self: - if bordereau.state == 'new': - bordereau.base_cotisation_cg.create_cotiz_and_lines(bordereau) + def update_bordereau_with_liasse(self): + """ + Supprime ou ajoute les appels de cotisations en fonction du + nombre de trimestres choisi et de la liasse + :return: + """ + self.create_cotiz_and_lines() @api.multi def open_payment(self): - self.ensure_one() - payments_ids = self.env['account.payment'].search([ - ('partner_id', '=', self.partner_id.id)]) - - payments = list(map(lambda p: self.get_move_reconciled(p) if self.get_move_reconciled(p) != True else None, payments_ids)) - if True in payments: - payments.remove(True) - # Return Action - action = { - 'name': 'Liste des paiements en cours - ' + self.partner_id.name, + action_context = { + 'company_ids': [self.company_id.id], + 'partner_ids': [ + self.partner_id.commercial_partner_id.id], + 'mode': 'customers' + } + return { + 'type': 'ir.actions.client', + 'tag': 'manual_reconciliation_view', + 'context': action_context, + } + + def action_show_cotiz(self): + """ + :return: Ouvre la vue des cotisations liées au bordereau + """ + tree_view_id = self.env.ref( + 'cgscop_cotisation.invoice_tree_scop_inherited').id + form_view_id = self.env.ref( + 'cgscop_cotisation_cg.invoice_form_scop_cg_inherited').id + return { + 'type': 'ir.actions.act_window', + 'name': 'Appels de cotisations', + 'views': [ + [tree_view_id, "tree"], + [form_view_id, "form"] + ], + 'view_mode': 'form', + 'res_model': 'account.invoice', + 'target': 'current', + 'domain': [('id', 'in', self.invoice_ids.ids)], + } + + def action_show_liasse(self): + """ + :return: Ouvre la vue des liasses liées au partenaire + """ + return { 'type': 'ir.actions.act_window', - 'res_model': 'account.payment', - 'view_mode': 'tree,form', - 'domain': [('id', 'in', payments)] + 'name': 'Liasses fiscales - ' + self.partner_id.name, + 'views': [ + [False, "tree"], + [False, "form"] + ], + 'view_mode': 'form', + 'res_model': 'scop.liasse.fiscale', + 'target': 'current', + 'domain': [('partner_id', '=', self.partner_id.id)], } - return action + + def action_change_liasse(self): + """ + :return: Ouvre le wizard pour changer la liasse + """ + wizard = self.env['scop.bordereau.change.liasse.wizard']. \ + create({ + 'bordereau_id': self.id, + }) + return { + 'type': 'ir.actions.act_window', + 'name': 'Changer la liasse', + 'views': [[False, "form"]], + 'view_mode': 'form', + 'res_model': 'scop.bordereau.change.liasse.wizard', + 'target': 'new', + 'res_id': wizard.id + } + + def action_change_payment_mode(self): + """ + :return: Ouvre le wizard pour changer le mode de paiement + """ + wizard = self.env['scop.bordereau.change.payment.mode.wizard']. \ + create({ + 'bordereau_id': self.id, + }) + return { + 'type': 'ir.actions.act_window', + 'name': 'Changer le mode de paiement', + 'views': [[False, "form"]], + 'view_mode': 'form', + 'res_model': 'scop.bordereau.change.payment.mode.wizard', + 'target': 'new', + 'res_id': wizard.id + } + + def validate_ongoing_bordereau(self): + """ + - Valide les invoices en brouillon + - Met à jour le numéro de version du bordereau + - Valide le versionnage en cours + """ + self.ensure_one() + ongoing_version = self.check_ongoing_version() + self.validate_bordereau() + self.write({ + 'name': self.name[0:10] + '-' + str(self.version + 1), + 'version': self.version + 1, + 'date_regul': ongoing_version.date, + 'comment_regul': ongoing_version.comment, + }) + ongoing_version.write({ + 'state': 'validated', + }) + + def cancel_ongoing_bordereau(self): + """ + - Supprime les invoice en brouillon + - Réaffecte la dernière liasse fiscale (enregistrée dans la version) + - Supprime le versionnage en cours + """ + self.ensure_one() + self.invoice_ids.filtered( + lambda i: i.state == 'draft' + ).unlink() + ongoing_version = self.check_ongoing_version() + self.write({ + 'liasse_fiscale_id': ongoing_version.liasse_fiscale_id_old.id + }) + self.invoice_ids.update({ + 'liasse_fiscale_id': ongoing_version.liasse_fiscale_id_old.id + }) + ongoing_version.unlink() # ------------------------------------------------------ # Override ORM @@ -463,19 +590,211 @@ class Bordereau(models.Model): 'avoirs sur les appels de cotisations.' ) else: - for bordereau in self: - for cotiz in bordereau.invoice_ids: - cotiz.unlink() return super(Bordereau, self).unlink() # ------------------------------------------------------ # Global Functions # ------------------------------------------------------ + def create_cotiz_and_lines(self): + """ + - Supprime les factures existantes si besoin + - Crée les appels de cotisation par trimestre + """ + + if self.invoice_ids: + self.invoice_ids.unlink() + + partner = self.partner_id + liasse = self.liasse_fiscale_id + base_cotiz = self.base_cotisation_cg + + # Invoice cotiz CG + # + product_cg_id = self.company_id.contribution_cg_id + type_cotisation_cg = self.env.ref('cgscop_partner.riga_14397').id + + if liasse: + amount_cg = base_cotiz.round_to_closest_multiple( + liasse.contribution_cg, 4) + else: + amount_cg = self.env['scop.liasse.fiscale']. \ + get_values_for_cotiz_cg(partner)['plancher1'] + self.create_contribution( + product_cg_id, type_cotisation_cg, amount_cg + ) + self.env.cr.commit() + + # Invoice UR et Fédé + type_cotisation_ur = self.env.ref('cgscop_partner.riga_14399').id + journal_ur_or_fede = self.company_id.contribution_ur_or_fede_journal_id + + # Invoice cotiz Fede Com + # + if partner.is_federation_com: + product_fede_com_id = self.company_id.contribution_fede_com_id + account_id = self.company_id.receivable_account_fede_com_id + type_cotisation_fede_com = self.env.ref( + 'cgscop_partner.riga_14398').id + + if liasse: + amount_fede_com = base_cotiz.round_to_closest_multiple( + liasse.contribution_com, 4) + else: + amount_fede_com = self.env['scop.liasse.fiscale']. \ + get_plancher_cotiz()['fede_com'] + self.create_contribution( + product_fede_com_id, type_cotisation_fede_com, amount_fede_com, + journal_ur_or_fede, account_id + ) + self.env.cr.commit() + + # Invoice cotiz Fede CAE + # + if partner.cae: + product_fede_cae_id = self.company_id.contribution_fede_cae_id + account_id = self.company_id.receivable_account_fede_cae_id + type_cotisation_fede_cae = self.env.ref( + 'cgscop_partner.cotiz_fede_cae').id + + if liasse: + amount_fede_cae = base_cotiz.round_to_closest_multiple( + liasse.contribution_cae, 4) + else: + amount_fede_cae = self.env['scop.liasse.fiscale']. \ + get_plancher_cotiz()['fede_cae'] + self.create_contribution( + product_fede_cae_id, type_cotisation_fede_cae, amount_fede_cae, + journal_ur_or_fede, account_id + ) + self.env.cr.commit() + + # Invoice cotiz UR HDF + # + ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + if partner.ur_id.id == ur_hdf: + product_hdf_id = self.company_id.contribution_hdf_id + account_id = self.company_id.receivable_account_ur_hdf_id + + if liasse: + amount_hdf = base_cotiz.round_to_closest_multiple( + liasse.contribution_hdf, 4) + else: + amount_hdf = self.env['scop.liasse.fiscale']. \ + get_plancher_cotiz()['ur_hdf'] + self.create_contribution( + product_hdf_id, type_cotisation_ur, amount_hdf, + journal_ur_or_fede, account_id + ) + self.env.cr.commit() + + # Invoice cotiz UR Med + # + ur_med = self.env.ref('cgscop_partner.riga_14243').id + if partner.ur_id.id == ur_med: + product_med_id = self.company_id.contribution_med_id + account_id = self.company_id.receivable_account_ur_med_id + + if liasse: + amount_med = base_cotiz.round_to_closest_multiple( + liasse.contribution_med, 4) + else: + amount_med = self.env['scop.liasse.fiscale']. \ + get_plancher_cotiz()['ur_med'] + self.create_contribution( + product_med_id, type_cotisation_ur, amount_med, + journal_ur_or_fede, account_id + ) + self.env.cr.commit() + + def create_contribution( + self, product, type_contribution, amount=0, + journal_id=False, account_id=False, type_invoice='out_invoice'): + """ + Create invoice from Contribution Base + :param product: product_id + :param type_contribution: type_contribution (CG, UR, Fédé) + :param amount: contribution amount (float) + :param journal_id: journal + :param account_id: customer_account_id + :param type_invoice: invoice or refund + :return: invoice + """ + Invoice = self.env['account.invoice'] + InvoiceLine = self.env['account.invoice.line'] + partner = self.partner_id + liasse = self.liasse_fiscale_id + + base_cotiz = self.base_cotisation_cg + quarters = [base_cotiz.trimester_1, base_cotiz.trimester_2, + base_cotiz.trimester_3, base_cotiz.trimester_4] + + # TODO: Specific CAE calculation : to be deleted when 2022 is generated + type_contrib_cae = self.env.ref('cgscop_partner.cotiz_fede_cae').id + if type_contribution == type_contrib_cae and self.year == '2022': + amount = ((amount / 4) * 3) + 25 + # ... End specific CAE 2022 + + for i in range(1, self.nb_quarter + 1): + cotiz_quarter = (4 - self.nb_quarter) + i + + journal_id = self.company_id.contribution_journal_id \ + if not journal_id else journal_id + account_id = partner.property_account_receivable_id \ + if not account_id else account_id + member_invoice = Invoice.create({ + 'partner_id': partner.id, + 'liasse_fiscale_id': liasse.id, + 'type': type_invoice, + 'year': self.year, + 'is_contribution': True, + 'type_contribution_id': type_contribution, + 'journal_id': journal_id.id, + 'state': 'draft', + 'account_id': account_id.id, + 'payment_mode_id': partner.customer_payment_mode_id.id, + 'date_invoice': self.date_cotisation, + 'date_due': quarters[cotiz_quarter - 1], + 'bordereau_id': self.id, + 'amount_cg_calculated': amount, + 'cotiz_quarter': cotiz_quarter, + }) + + # Création de la ligne de cotisation + price_unit = amount / 4 + + # TODO: Specific CAE calculation : to be deleted when 2022 is generated + if type_contribution == type_contrib_cae and self.year == '2022': + if cotiz_quarter == 1: + price_unit = 25 + else: + price_unit = (amount - 25) / 3 + # ... End specific CAE 2022 + + InvoiceLine.create({ + 'invoice_id': member_invoice.id, + 'product_id': product.id, + 'account_id': product.property_account_income_id.id, + 'invoice_line_tax_ids': [(6, 0, product.taxes_id.ids)], + 'name': product.name + " T" + str(cotiz_quarter), + 'price_unit': price_unit + }) + + def check_ongoing_version(self): + """ + :return: le versionnage en cours + """ + ongoing_version = self.bordereau_version_ids.filtered( + lambda v: v.version == self.version and v.state == 'new' + ) + if len(ongoing_version) != 1: + raise ValueError('Erreur sur le versionnage de ce bordereau') + else: + return ongoing_version + @api.multi def get_bordereau_move_line(self): """ - Get payment dates for bordereau according to payment_term in bordereau - Use "compute" function from account.move + Get move line for invoices in bordereau :return: dict with date as key and amount (float) as value """ MoveLine = self.env['account.move.line'] @@ -506,8 +825,7 @@ class Bordereau(models.Model): @api.multi def get_bordereau_move_line_with_payments(self): """ - Get payment dates for bordereau according to payment_term in bordereau - Use "compute" function from account.move + Get payment dates for invoices in bordereau :return: dict with date as key and amount (float) as value """ MoveLine = self.env['account.move.line'] @@ -515,7 +833,7 @@ class Bordereau(models.Model): inv_ids = bordereau.invoice_ids.ids schedule_plan = MoveLine.read_group( [('invoice_id', 'in', inv_ids), - ('account_id.internal_type', '=', 'receivable')], + ('account_id.internal_type', '=', 'receivable')], fields=['date_maturity', 'credit', 'debit', 'amount_residual'], groupby=['date_maturity:day'], orderby='date_maturity', @@ -556,39 +874,7 @@ class Bordereau(models.Model): ['type_contribution_id', 'amount_total_signed'], ['type_contribution_id']) - def get_move_reconciled(self, payment): - """ - Check if payment is reconciled - :param payment: account.payment object - :return: True or payment_id - """ - rec = True - for aml in payment.move_line_ids.filtered(lambda x: x.account_id.reconcile): - if not aml.reconciled: - rec = payment.id - return rec - # Email def get_recipients(self): recipients = ','.join(map(lambda x: str(x), self.recipient_ids.ids)) return recipients - - # ------------------------------------------------------ - # Threading task - # ------------------------------------------------------ - def process_bordereau_validate(self): - with api.Environment.manage(): - with registry(self.env.cr.dbname).cursor() as new_cr: - # Def new env - new_env = api.Environment( - new_cr, self.env.uid, self.env.context - ) - for bordereau in self.with_env(new_env): - try: - bordereau.validate_cotiz_cg() - if bordereau.name == 'Brouillon': - bordereau.name = bordereau.env[ - 'ir.sequence'].next_by_code('scop.bordereau') - bordereau.env.cr.commit() - except Exception as e: - _logger.exception(str(e)) diff --git a/models/scop_bordereau_cg_version.py b/models/scop_bordereau_cg_version.py new file mode 100644 index 0000000000000000000000000000000000000000..ef5e3217445f0f93c3314280e4383a49a91914e5 --- /dev/null +++ b/models/scop_bordereau_cg_version.py @@ -0,0 +1,58 @@ +# © 2021 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from datetime import timedelta + +from odoo import fields, models, api +from odoo.exceptions import ValidationError + + +class BordereauVersion(models.Model): + _name = 'scop.bordereau.version' + _description = 'Historique des versions des bordereaux des cotisations CG' + + bordereau_id = fields.Many2one( + comodel_name='scop.bordereau', + string='Bordereau de rattachement', + required=True, + readonly=True, + ondelete='cascade') + version = fields.Integer('Version du bordereau', readonly=True) + comment = fields.Char(string='Motif') + liasse_fiscale_id_old = fields.Many2one( + comodel_name='scop.liasse.fiscale', + string='Ancienne liasse fiscale', + ondelete='set null', + readonly=True + ) + type_assiette = fields.Selection( + string='Ancien type d\'assiette', + selection=[('ca', 'CA'), + ('va', 'VA'), ], + readonly=True) + date = fields.Date('Date', readonly=True) + montant_assiette = fields.Integer( + string='Ancien montant assiette', readonly=True) + company_currency_id = fields.Many2one( + related='bordereau_id.company_currency_id') + amount_total_cotiz = fields.Monetary( + string='Ancien montant cotisation', + currency_field='company_currency_id', readonly=True) + state = fields.Selection([ + ('new', 'Brouillon'), + ('validated', 'Validée')], + string="Statut", + default='new', readonly=True) + + # ------------------------------------------------------ + # Constrain Functions + # ------------------------------------------------------ + @api.constrains('state') + def check_unique_state_new(self): + """ Une seule version en brouillon par bordereau """ + if len(self.bordereau_id.bordereau_version_ids.filtered( + lambda v: v.state == 'new' + )) > 1: + raise ValidationError( + 'Vous devez valider ou annuler les versions de bordereaux en ' + 'cours de modification avant de créer une nouvelle version !') diff --git a/models/scop_cotisation_cg.py b/models/scop_cotisation_cg.py index a3a368253732728423113f0c527fb9ede80cce02..6ffd9f77cd37adc02e05eb5e119d76f238f39f76 100644 --- a/models/scop_cotisation_cg.py +++ b/models/scop_cotisation_cg.py @@ -4,12 +4,11 @@ import base64 import json import logging -import threading import xlsxwriter from io import BytesIO from datetime import datetime -from odoo import models, fields, api, exceptions, registry +from odoo import models, fields, api, exceptions _logger = logging.getLogger(__name__) @@ -41,7 +40,7 @@ class ScopCotisation(models.Model): bordereau_ids = fields.One2many( comodel_name='scop.bordereau', inverse_name='base_cotisation_cg', - string='Bordereau',) + string='Bordereau', ) state = fields.Selection([ ('new', 'Brouillon'), @@ -49,7 +48,7 @@ class ScopCotisation(models.Model): ('end', 'Clôturé')], string="Statut", default='new', compute='_compute_state', - store=True, track_visibility='onchange',) + store=True, track_visibility='onchange', ) invoice_count = fields.Integer( string='Appels de cotisations émis', @@ -212,25 +211,14 @@ class ScopCotisation(models.Model): str(self.invoiced_member_count) + "<br/>Bordereaux à générer : " + str(len(members_to_invoice)) + - "<p>Les appels de cotisation sont en cours de " - "création...</p> " + "<p>Les appels de cotisation vont être générés.</p> " ) - message_id = self.env['message.wizard'].create( - {'message': message}) - - # Création de la task - cotiz_cg_task = self.env['scop.cotisation.task'].create({ - 'year': self.year, - 'cotiz_to_create': len(members_to_invoice), - 'message': "En cours de création", - 'status': 'in_process' - }) - cotiz_cg_task.env.cr.commit() - # Lancement du calcul en arrière plan - threaded_cotiz = threading.Thread( - target=self.process_cotiz_generate, - args=(members_to_invoice, cotiz_cg_task)) - threaded_cotiz.start() + message_id = self.env['message.wizard'].create({ + 'message': message, + 'cancel_button': True, + 'action': 'action_cotiz_generate(' + + str(members_to_invoice.ids) + ')', + }) else: message = ("<p class='text-center'>Tous les appels de " + "cotisations annuels ont déjà été créés !</p>") @@ -246,6 +234,25 @@ class ScopCotisation(models.Model): 'target': 'new' } + def action_cotiz_generate(self, *ids): + """ + Function to be used by message.wizard in "ok" action + """ + members_to_invoice = self.env['res.partner'].browse(*ids) + + # Job queue + batch_name = (fields.Datetime.to_string(fields.Datetime.now()) + + " Génération des bordereaux " + str(self.year)) + batch = self.env['queue.job.batch'].get_new_batch(batch_name) + for member in members_to_invoice: + liasse_id = self.get_liasse(member) + self.with_context( + job_batch=batch + ).with_delay().create_bordereau( + member=member, liasse=liasse_id, + nb_quarter=4, date=False) + batch.enqueue() + def cotiz_generate_wizard(self): if not self.trimester_1 or not self.trimester_2 or not self.trimester_3 or not self.trimester_4: raise exceptions.UserError( @@ -312,8 +319,7 @@ class ScopCotisation(models.Model): 'target': 'current', } - def create_bordereau( - self, member, nb_quarter, liasse=None, date=False, is_regul=False): + def create_bordereau(self, member, nb_quarter, liasse=None, date=False): """ Création du bordereau de cotisations :param member: @@ -322,58 +328,32 @@ class ScopCotisation(models.Model): :param date: :return bordereau_id: """ - existing_bordereau = self.env['scop.bordereau'].search([ - ('partner_id', '=', member.id), - ('year', '=', self.year), - ]) - if not existing_bordereau or is_regul: - # Variables to calculate cotiz - ca = liasse.revenue_cgsubv - va = self.get_va(liasse) - staff_id = self.get_last_staff_id(member) - if staff_id: - staff_shareholder_count = \ - staff_id.staff_shareholder_count - staff_count = \ - staff_id.staff_count - else: - staff_shareholder_count = 0 - staff_count = 0 + # Variables to calculate cotiz + staff_id = self.get_last_staff_id(member) + if staff_id: + staff_shareholder_count = staff_id.staff_shareholder_count + staff_count = staff_id.staff_count staff_average = staff_id.staff_average - net_results = liasse.L2053_HN \ - if liasse.L2053_HN > 0 else liasse.L2051_DI - wage_cg = liasse.wage_cg - date_invoice = date if date else self.date_cotisation - - bordereau = self.env['scop.bordereau'].create({ - 'partner_id': member.id, - 'payment_mode_id': member.customer_payment_mode_id.id, - 'base_cotisation_cg': self.id, - 'payment_term_id': self.payment_term_id.id, - 'liasse_fiscale_id': liasse.id if liasse else None, - 'year_liasse_retenue': liasse.year if liasse else 0, - 'date_cotisation': date_invoice, - 'nb_quarter': nb_quarter, - 'ca_connu': ca, - 'ca_retenu': ca, - 'va_connu': va, - 'va_cg_retenu': va, - 'va_fede_com_retenu': va, - 'staff_count_connu': staff_count, - 'staff_fede_com_count_retenu': staff_count, - 'staff_ur_med_count_retenu': staff_count, - 'staff_shareholder_count_connu': staff_shareholder_count, - 'staff_shareholder_count_retenu': staff_shareholder_count, - 'staff_average_connu': staff_average, - 'staff_average_retenu': staff_average, - 'net_results_connu': net_results, - 'net_results_retenu': net_results, - 'wage_cg_connu': wage_cg, - 'wage_cg_retenu': wage_cg, - }) - return bordereau else: - return existing_bordereau + staff_shareholder_count = 0 + staff_count = 0 + staff_average = 0 + date_invoice = date if date else self.date_cotisation + + bordereau = self.env['scop.bordereau'].create({ + 'partner_id': member.id, + 'payment_mode_id': member.customer_payment_mode_id.id, + 'base_cotisation_cg': self.id, + 'liasse_fiscale_id': liasse.id if liasse else None, + 'year_liasse': liasse.year if liasse else 0, + 'date_cotisation': date_invoice, + 'nb_quarter': nb_quarter, + 'staff_count': staff_count, + 'staff_shareholder_count': staff_shareholder_count, + 'staff_average': staff_average, + }) + bordereau.create_cotiz_and_lines() + return bordereau.id def bordereau_validate(self): bordereau_to_validate = self.bordereau_ids.filtered( @@ -387,7 +367,7 @@ class ScopCotisation(models.Model): ) else: message = ( - "Tous les bordereaux ont déjà été validés" + "Tous les bordereaux ont déjà été validés" ) message_id = self.env['message.wizard'].create( {'message': message}) @@ -468,9 +448,11 @@ class ScopCotisation(models.Model): # Calcul cotisation N-1 contribution_last_year = sum(line_ids.filtered( - lambda l: l.product_id == product_cg_id).mapped('price_subtotal_signed')) + lambda l: l.product_id == product_cg_id).mapped( + 'price_subtotal_signed')) # Construction Tableau + liasse.read(['revenue_cg', 'av_cg', 'wage_cg']) datas_contrib = [ m.member_number, m.ur_id.name, @@ -502,7 +484,8 @@ class ScopCotisation(models.Model): liasse.contribution_hdf, 4) if liasse else 40 # Calcul cotisation N-1 contribution_last_year = sum(line_ids.filtered( - lambda l: l.product_id == product_hdf_id).mapped('price_subtotal_signed')) + lambda l: l.product_id == product_hdf_id).mapped( + 'price_subtotal_signed')) datas_contrib[4] = 'Cotisation UR HDF' datas_contrib[5] = contrib_hdf datas_contrib[6] = contribution_last_year @@ -517,7 +500,8 @@ class ScopCotisation(models.Model): liasse.contribution_med, 4) if liasse else 0 # Calcul cotisation N-1 contribution_last_year = sum(line_ids.filtered( - lambda l: l.product_id == product_med_id).mapped('price_subtotal_signed')) + lambda l: l.product_id == product_med_id).mapped( + 'price_subtotal_signed')) datas_contrib[4] = 'Cotisation UR Méditerranée' datas_contrib[5] = contrib_med datas_contrib[6] = contribution_last_year @@ -540,7 +524,8 @@ class ScopCotisation(models.Model): contrib_fede_com = 108 # Calcul cotisation N-1 contribution_last_year = sum(line_ids.filtered( - lambda l: l.product_id == product_com_id).mapped('price_subtotal_signed')) + lambda l: l.product_id == product_com_id).mapped( + 'price_subtotal_signed')) datas_contrib[4] = 'Cotisation Fédération de la Com' datas_contrib[5] = contrib_fede_com datas_contrib[6] = contribution_last_year @@ -552,7 +537,8 @@ class ScopCotisation(models.Model): contrib_fede_cae = 100 # Calcul cotisation N-1 contribution_last_year = sum(line_ids.filtered( - lambda l: l.product_id == product_cae_id).mapped('price_subtotal_signed')) + lambda l: l.product_id == product_cae_id).mapped( + 'price_subtotal_signed')) datas_contrib[4] = 'Cotisation Fédération des CAE' datas_contrib[5] = contrib_fede_cae datas_contrib[6] = contribution_last_year @@ -563,7 +549,9 @@ class ScopCotisation(models.Model): total_cg += contrib_cg # Génération du ficher Excel - file_name = datetime.strftime(datetime.now(), '%Y-%m-%d_%Hh%M') + ' Simulation cotisations ' + str(self.year) + '.xlsx' + file_name = datetime.strftime(datetime.now(), + '%Y-%m-%d_%Hh%M') + ' Simulation cotisations ' + str( + self.year) + '.xlsx' output = BytesIO() workbook = xlsxwriter.Workbook(output, {'in_memory': True}) worksheet1 = workbook.add_worksheet('Données brutes') @@ -644,314 +632,9 @@ class ScopCotisation(models.Model): else: return Liasse - def create_cotiz_and_lines(self, bordereau, date=False, is_regul=False): - - partner = bordereau.partner_id - liasse = bordereau.liasse_fiscale_id - - # Invoice cotiz CG - # - product_cg_id = self.company_id.contribution_cg_id - type_cotisation_cg = self.env.ref('cgscop_partner.riga_14397').id - - amount_cg = self.round_to_closest_multiple( - self.get_cotiz_cg(partner, bordereau) - , 4) - invoice_cotiz_cg = self.create_contribution( - product_cg_id, partner, type_cotisation_cg, - liasse, amount_cg, date, is_regul=is_regul, bordereau=bordereau) - invoice_cotiz_cg.write({ - 'cotisation_cg_id': self.id, - 'bordereau_id': bordereau.id, - 'amount_cg_calculated': amount_cg, - 'nb_quarter': bordereau.nb_quarter - }) - if bordereau.nb_quarter != 4: - invoice_cotiz_cg.recalcul_cotiz_cg() - invoice_cotiz_cg.env.cr.commit() - - # Invoice UR et Fédé - type_cotisation_ur = self.env.ref('cgscop_partner.riga_14399').id - journal_ur_or_fede = self.company_id.contribution_ur_or_fede_journal_id - - # Invoice cotiz Fede Com - # - if partner.is_federation_com: - product_fede_com_id = self.company_id.contribution_fede_com_id - account_id = self.company_id.receivable_account_fede_com_id - type_cotisation_fede_com = self.env.ref( - 'cgscop_partner.riga_14398').id - - amount_fede_com = self.round_to_closest_multiple( - self.get_cotiz_fede_com(bordereau) - , 4) - invoice_cotiz_fede_com = self.create_contribution( - product_fede_com_id, partner, type_cotisation_fede_com, - liasse, amount_fede_com, date, journal_ur_or_fede, account_id, - is_regul=is_regul, bordereau=bordereau) - invoice_cotiz_fede_com.write({ - 'cotisation_cg_id': self.id, - 'bordereau_id': bordereau.id, - 'amount_cg_calculated': amount_fede_com, - 'nb_quarter': bordereau.nb_quarter - }) - if bordereau.nb_quarter != 4: - invoice_cotiz_fede_com.recalcul_cotiz_cg() - invoice_cotiz_fede_com.env.cr.commit() - - # Invoice cotiz Fede CAE - # - if partner.cae: - product_fede_cae_id = self.company_id.contribution_fede_cae_id - account_id = self.company_id.receivable_account_fede_cae_id - type_cotisation_fede_cae = self.env.ref( - 'cgscop_partner.cotiz_fede_cae').id - - amount_fede_cae = self.round_to_closest_multiple( - self.get_cotiz_fede_cae(), 4) - invoice_cotiz_fede_cae = self.create_contribution( - product_fede_cae_id, partner, type_cotisation_fede_cae, - liasse, amount_fede_cae, date, journal_ur_or_fede, account_id, - is_regul=is_regul, bordereau=bordereau - ) - invoice_cotiz_fede_cae.write({ - 'cotisation_cg_id': self.id, - 'bordereau_id': bordereau.id, - 'amount_cg_calculated': amount_fede_cae, - 'nb_quarter': bordereau.nb_quarter - }) - if bordereau.nb_quarter != 4: - invoice_cotiz_fede_cae.recalcul_cotiz_cg() - invoice_cotiz_fede_cae.env.cr.commit() - - # Invoice cotiz UR HDF - # - ur_hdf = self.env.ref('cgscop_partner.riga_14232').id - if partner.ur_id.id == ur_hdf: - product_hdf_id = self.company_id.contribution_hdf_id - account_id = self.company_id.receivable_account_ur_hdf_id - - amount_hdf = self.round_to_closest_multiple( - self.get_cotiz_hdf(bordereau), 4) - invoice_cotiz_hdf = self.create_contribution( - product_hdf_id, partner, type_cotisation_ur, - liasse, amount_hdf, date, journal_ur_or_fede, account_id, - is_regul=is_regul, bordereau=bordereau - ) - invoice_cotiz_hdf.write({ - 'cotisation_cg_id': self.id, - 'bordereau_id': bordereau.id, - 'amount_cg_calculated': amount_hdf, - 'nb_quarter': bordereau.nb_quarter - }) - if bordereau.nb_quarter != 4: - invoice_cotiz_hdf.recalcul_cotiz_cg() - invoice_cotiz_hdf.env.cr.commit() - - # Invoice cotiz UR Med - # - ur_med = self.env.ref('cgscop_partner.riga_14243').id - if partner.ur_id.id == ur_med: - product_med_id = self.company_id.contribution_med_id - account_id = self.company_id.receivable_account_ur_med_id - - amount_med = self.round_to_closest_multiple( - self.get_cotiz_med(amount_cg, bordereau), 4) - invoice_cotiz_med = self.create_contribution( - product_med_id, partner, type_cotisation_ur, - liasse, amount_med, date, journal_ur_or_fede, account_id, - is_regul=is_regul, bordereau=bordereau) - invoice_cotiz_med.write({ - 'cotisation_cg_id': self.id, - 'bordereau_id': bordereau.id, - 'amount_cg_calculated': amount_med, - 'nb_quarter': bordereau.nb_quarter - }) - if bordereau.nb_quarter != 4: - invoice_cotiz_med.recalcul_cotiz_cg() - invoice_cotiz_med.env.cr.commit() - # ------------------------------------------------------ # Calcul des cotisations # ------------------------------------------------------ - - # TODO: A voir si on conserve ces fonctions. - # -- - # Le montant des cotisations est calculé depuis la liasse, à vois si il est nécessaire - # de conserver ces fonctions : - # - si il y a une liasse, on applique les montants de la liasse - # - si il n'y en a pas, on applique la valeur plancher (auquel cas, on supprime ces fonctions) - # A checker également pour les ajustements, les modifications et les exonérations - # -- - # Hypothèse : - # - soit on a la liasse fiscale et on ajuste via les données de la liasse - # - soit on ajuste manuelement le calcul - # => A valider par Hervé + Christophe (et faire une revue du code de recalcul des cotisations) - - def get_cotiz_cg(self, partner, bordereau): - """ - Appelle la fonction de calcul de la cotisation CG Scop - à partir des variables du bordereau - """ - - type_assiette = bordereau.type_assiette_retenu - ca = self.check_dureeExercice( - bordereau.ca_retenu, bordereau.dureeExercice) - va = self.check_dureeExercice( - bordereau.va_cg_retenu, bordereau.dureeExercice) - return self.calc_cotiz_cg(partner, ca, va, type_assiette) - - def calc_cotiz_cg(self, partner, ca, va, type_assiette): - """ - Calcule la cotisation de la CG Scop : - - VA = VA saisie ou VA au sens CGSCOP si pas de VA renseignée - - 0,3% du CA ou 0,7% de la VA - - 300 € pour CA compris entre 0 et 100 K€ - - 600 € pour CA compris entre 100 et 200 K€ - - Max = 375 K€ - - Utilisation de hack_check_exercice pour avoir la valeur non - proratisée de la cotisation pour le calcul de la cotisation UR med - - @return float : cotisation - """ - values = self.get_values_for_cotiz_cg(partner) - if type_assiette == 'ca': - rate = self.get_rate_ca(partner) - cotiz = ca * rate - else: - rate = self.get_rate_va(partner) - cotiz = va * rate - - if cotiz < values['plancher1']: - return values['plancher1'] - elif cotiz < values['plancher2']: - return values['plancher2'] - elif cotiz >= values['plancher3']: - if type_assiette == 'ca': - cotiz = (25000000 * rate) + (ca - 25000000) * (rate / 2) - else: - cotiz = (10714286 * rate) + (va - 10714286) * (rate / 2) - if cotiz <= values['plafond']: - return cotiz - else: - return values['plafond'] - else: - return cotiz - - def get_cotiz_fede_com(self, bordereau): - """ - Calcule la cotisation de la fédération de la com pour 1 partenaire - - Assiette annuelle : VA saisie ou VA au sens CGSCOP - - Taux : 0.0032 - - Calcul : Valeur Ajoutée * 0.0032 - - Pour les nouvelles coopératives (pas de comptes annuels) : - 108 € par salarié (effectif moyen s’il est saisi, à défaut - effectif), soit 27 € par salarié et par trimestre. - - Plancher annuel : 108 € - - Plafond annuel : 18 428 € - - @return float : cotisation - """ - plancher = 108 - plafond = 18428 - rate = 0.0032 - va = self.check_dureeExercice( - 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: - if staff_average > 0: - cotiz = staff_average * plancher - else: - cotiz = staff_count * plancher - if plancher <= cotiz <= plafond: - return cotiz - elif cotiz < plancher: - return plancher - else: - return plafond - - def get_cotiz_hdf(self, bordereau): - """ - Calcule la cotisation de l'UR HDF pour 1 partenaire : - - 0,1 % de la masse salariale annuelle brute prévisionnelle - ou figurant sur la liasse fiscale. - - @return float : cotisation - """ - rate = 0.001 - plancher = 40 - wage_cg = self.check_dureeExercice( - bordereau.wage_cg_retenu, bordereau.dureeExercice) - if wage_cg > 0: - cotiz = wage_cg * rate - else: - cotiz = 0 - - if cotiz > plancher: - return cotiz - else: - return plancher - - def get_cotiz_med(self, cotiz_cg, bordereau): - """ - Calcule la cotisation de l'UR Med pour 1 partenaire : - - Assiette : Assiette de la cotisation CGSCOP - - Taux d’appel : 1/3 du taux - - Calcul intermédiaire : Assiette * taux - (soit 1/3 de la cotisation CGSCOP sans plancher) - - Complément de cotisation : 1% du résultat net si positif - - Abattement pour sociétariat : - Taux de sociétariat = nombre de salariés sociétaires / - nombre de salariés - Abattement 20 % si >= 50 % - Abattement 30 % si >= 80 % - - Calcul cotisations : - [(Assiette*taux) + (résultat net *0.01)]* (1 ou 0.8 ou 0.7) - - @return float : cotisation - """ - # Assiette CG - assiette_rate = 1/3 - assiette = cotiz_cg - - # Résultat net - net_results_rate = 0.01 - - net_results = self.check_dureeExercice( - bordereau.net_results_retenu, bordereau.dureeExercice) - staff_count = bordereau.staff_shareholder_count_retenu - staff_shareholder_count = bordereau.staff_ur_med_count_retenu - - # Taux d'abattement pour sociétariat - if staff_count > 0: - societariat_rate = staff_shareholder_count / staff_count - else: - societariat_rate = 0 - - if 0.5 <= societariat_rate < 0.8: - abatt_rate = 1 - 0.2 - elif societariat_rate >= 0.8: - abatt_rate = 1 - 0.3 - else: - abatt_rate = 1 - 0 - - # Cotiz - cotiz = ((assiette * assiette_rate) + - (net_results * net_results_rate)) * abatt_rate - final_cotiz = cotiz if cotiz >= 0 else 0 - return final_cotiz - - def get_cotiz_fede_cae(self): - """ - 100€ if partner is CAE - """ - cotiz = 100 - return cotiz - def get_va(self, liasse): """ VA saisie ou VA au sens CGSCOP @@ -959,6 +642,7 @@ class ScopCotisation(models.Model): :param liasse: :return: """ + liasse.read(['av_lf', 'av_cg']) va = liasse.av_lf if liasse.av_lf > 0 else liasse.av_cg return va @@ -989,112 +673,3 @@ class ScopCotisation(models.Model): else: type_cotiz = 'ca' return type_cotiz - - def get_rate_ca(self, partner): - # Calcul des Taux SCOP en fonction du type - if partner.cooperative_form_id in ( - self.env.ref('cgscop_partner.form_coop47'), - self.env.ref('cgscop_partner.form_lamaneur')): - rate_ca = 0.002 - else: - rate_ca = 0.003 - return rate_ca - - def get_rate_va(self, partner): - # Calcul des Taux SCOP en fonction du type - if partner.cooperative_form_id in ( - self.env.ref('cgscop_partner.form_coop47'), - self.env.ref('cgscop_partner.form_lamaneur')): - rate_va = 0.0047 - else: - rate_va = 0.007 - return rate_va - - def get_values_for_cotiz_cg(self, partner): - # Calcul des Taux SCOP en fonction du type - plancher1 = 300 - plancher2 = 600 - plancher3 = 75000 - plafond = 375000 - if partner.cooperative_form_id in ( - self.env.ref('cgscop_partner.form_coop47'), - self.env.ref('cgscop_partner.form_lamaneur')): - plancher1 = 1/3 * plancher1 - plancher2 = 1/3 * plancher2 - plancher3 = 1/3 * plancher3 - plafond = 1/3 * plafond - - return { - 'plancher1': plancher1, - 'plancher2': plancher2, - 'plancher3': plancher3, - 'plafond': plafond, - } - - def check_dureeExercice(self, amount, duree_exercice): - if duree_exercice and duree_exercice not in (0, 12): - return amount * (12/duree_exercice) - else: - return amount - - # ------------------------------------------------------ - # Threading task - # ------------------------------------------------------ - def process_cotiz_generate(self, partner_ids, cotiz_cg_task, - nb_quarter=4, date=False): - """ - Process de génération des cotiz CG en background - """ - cotiz_created = 0 - cotiz_to_create = len(partner_ids) - - # Creation of cotiz invoice for each member - with api.Environment.manage(): - with registry(self.env.cr.dbname).cursor() as new_cr: - # Def new env - new_env = api.Environment( - new_cr, self.env.uid, self.env.context - ) - # Def new var in new env - task = cotiz_cg_task.with_env(new_env) - new_self = self.with_env(new_env) - - # Creation of cotiz invoice for each member - for member_id in partner_ids: - member = member_id.with_env(new_env) - liasse_id = new_self.get_liasse(member) - - try: - # Bordereau - bordereau = new_self.create_bordereau( - member=member, liasse=liasse_id, - nb_quarter=nb_quarter, date=date) - bordereau.env.cr.commit() - - # Cotisations - new_self.create_cotiz_and_lines( - bordereau=bordereau, date=date - ) - - cotiz_created += 1 - task.write({ - 'cotiz_created': cotiz_created, - 'message': "%d / %d ont été créés" % ( - cotiz_created, cotiz_to_create), - }) - if cotiz_created == cotiz_to_create: - task.write({ - 'message': "Tous les bordereaux ont été créés", - 'status': "done" - }) - task.env.cr.commit() - except Exception as e: - _logger.exception(str(e)) - task.write({ - 'cotiz_created': cotiz_created, - 'message': "%d / %d ont été créés" % ( - cotiz_created, cotiz_to_create), - 'is_error': True, - 'status': "done" - }) - task.env.cr.commit() diff --git a/models/scop_liasse_fiscale.py b/models/scop_liasse_fiscale.py index 98f01816e63848163dd15efde5551391c33b76cb..a736ec9313ce5bf7c1f9877779810d14e39b34cf 100644 --- a/models/scop_liasse_fiscale.py +++ b/models/scop_liasse_fiscale.py @@ -33,6 +33,10 @@ class ScopLiasseFiscale(models.Model): string='Cotisation Fédération Com', compute='_compute_contribution_com', store=True) + contribution_cae = fields.Float( + string='Cotisation Fédération CAE', + compute='_compute_contribution_cae', + store=True) # ------------------------------------------------------ # Compute fields @@ -52,6 +56,7 @@ class ScopLiasseFiscale(models.Model): """ for liasse in self: # Calcul VA + liasse.read(['av_lf', 'av_cg', 'revenue_cg']) va = liasse.av_lf if liasse.av_lf > 0 else liasse.av_cg ca = liasse.revenue_cg if liasse.dureeExercice and liasse.dureeExercice not in (0, 12): @@ -116,9 +121,10 @@ class ScopLiasseFiscale(models.Model): """ ur_hdf = self.env.ref('cgscop_partner.riga_14232') for liasse in self: + plancher = liasse.get_plancher_cotiz()['ur_hdf'] + liasse.read(['wage_cg']) if liasse.partner_id.ur_id == ur_hdf: rate = 0.001 - plancher = 40 if liasse.dureeExercice and liasse.dureeExercice not in (0, 12): wage_cg = liasse.wage_cg * (12/liasse.dureeExercice) else: @@ -133,6 +139,67 @@ class ScopLiasseFiscale(models.Model): else: liasse.contribution_hdf = plancher + @api.depends('contribution_cg', 'L2053_HN') + @api.multi + def _compute_contribution_med(self): + """ + Calcule la cotisation de l'UR Med pour 1 liasse : + - Assiette : Assiette de la cotisation CGSCOP + - Taux d’appel : 1/3 du taux + - Calcul intermédiaire : Assiette * taux + (soit 1/3 de la cotisation CGSCOP sans plancher) + - Complément de cotisation : 1% du résultat net si positif + - Abattement pour sociétariat : + Taux de sociétariat = nombre de salariés sociétaires / + nombre de salariés + Abattement 20 % si >= 50 % + Abattement 30 % si >= 80 % + - Calcul cotisations : + [(Assiette*taux) + (résultat net *0.01)]* (1 ou 0.8 ou 0.7) + + @return float : cotisation + """ + ur_med = self.env.ref('cgscop_partner.riga_14243') + for liasse in self: + plancher = liasse.get_plancher_cotiz()['ur_med'] + if liasse.partner_id.ur_id == ur_med: + # Assiette CG + assiette_rate = 1/3 + assiette = liasse.contribution_cg + + # Résultat net + net_results_rate = 0.01 + net_results = liasse.L2053_HN if liasse.L2053_HN > 0 else liasse.L2051_DI + if liasse.dureeExercice and liasse.dureeExercice not in (0, 12): + net_results = net_results * (12/liasse.dureeExercice) + + # Effectifs + staff_id = self.get_last_staff_id(liasse.partner_id) + if staff_id: + staff_shareholder_count = staff_id.staff_shareholder_count + staff_count = staff_id.staff_count + else: + staff_shareholder_count = staff_count = 0 + + # Taux d'abattement pour sociétariat + if staff_count > 0: + societariat_rate = staff_shareholder_count / staff_count + else: + societariat_rate = 0 + + if 0.5 <= societariat_rate < 0.8: + abatt_rate = 1 - 0.2 + elif societariat_rate >= 0.8: + abatt_rate = 1 - 0.3 + else: + abatt_rate = 1 - 0 + + # Calcul Cotisation + contribution_med = ((assiette * assiette_rate) + + (net_results * net_results_rate)) * abatt_rate + final_contribution_med = contribution_med if contribution_med >= 0 else plancher + liasse.contribution_med = final_contribution_med + @api.depends('av_lf', 'av_cg') @api.multi def _compute_contribution_com(self): @@ -149,10 +216,11 @@ class ScopLiasseFiscale(models.Model): @return float : cotisation """ - plancher = 108 plafond = 18428 rate = 0.0032 for liasse in self: + plancher = liasse.get_plancher_cotiz()['fede_com'] + liasse.read(['av_lf', 'av_cg']) if liasse.partner_id.is_federation_com: # Calcul VA va = liasse.av_lf if liasse.av_lf > 0 else liasse.av_cg @@ -187,69 +255,64 @@ class ScopLiasseFiscale(models.Model): else: liasse.contribution_com = plafond - @api.depends('contribution_cg', 'L2053_HN') + @api.depends('L2052_FY', 'partner_id.cae') @api.multi - def _compute_contribution_med(self): + def _compute_contribution_cae(self): """ - Calcule la cotisation de l'UR Med pour 1 liasse : - - Assiette : Assiette de la cotisation CGSCOP - - Taux d’appel : 1/3 du taux - - Calcul intermédiaire : Assiette * taux - (soit 1/3 de la cotisation CGSCOP sans plancher) - - Complément de cotisation : 1% du résultat net si positif - - Abattement pour sociétariat : - Taux de sociétariat = nombre de salariés sociétaires / - nombre de salariés - Abattement 20 % si >= 50 % - Abattement 30 % si >= 80 % - - Calcul cotisations : - [(Assiette*taux) + (résultat net *0.01)]* (1 ou 0.8 ou 0.7) - + Calcule la cotisation CAE 1 partenaire : + - 0,4 % de la masse salariale annuelle brute + - plancher = 300 + - plafond = 8500 @return float : cotisation """ - ur_med = self.env.ref('cgscop_partner.riga_14243') for liasse in self: - if liasse.partner_id.ur_id == ur_med: - # Assiette CG - assiette_rate = 1/3 - assiette = liasse.contribution_cg + if liasse.partner_id.cae: + plancher = liasse.get_plancher_cotiz()['fede_cae'] + plafond = 8500 + rate = 0.004 + contribution_cae = 0 + if liasse.L2052_FY: + contribution_cae = liasse.L2052_FY * rate - # Résultat net - net_results_rate = 0.01 - net_results = liasse.L2053_HN if liasse.L2053_HN > 0 else liasse.L2051_DI - if liasse.dureeExercice and liasse.dureeExercice not in (0, 12): - net_results = net_results * (12/liasse.dureeExercice) - - # Effectifs - staff_id = self.get_last_staff_id(liasse.partner_id) - if staff_id: - staff_shareholder_count = staff_id.staff_shareholder_count - staff_count = staff_id.staff_count + if contribution_cae < plancher: + liasse.contribution_cae = plancher + elif contribution_cae > plafond: + liasse.contribution_cae = plafond else: - staff_shareholder_count = staff_count = 0 - - # Taux d'abattement pour sociétariat - if staff_count > 0: - societariat_rate = staff_shareholder_count / staff_count - else: - societariat_rate = 0 - - if 0.5 <= societariat_rate < 0.8: - abatt_rate = 1 - 0.2 - elif societariat_rate >= 0.8: - abatt_rate = 1 - 0.3 - else: - abatt_rate = 1 - 0 - - # Calcul Cotisation - contribution_med = ((assiette * assiette_rate) + - (net_results * net_results_rate)) * abatt_rate - final_contribution_med = contribution_med if contribution_med >= 0 else 0 - liasse.contribution_med = final_contribution_med + liasse.contribution_cae = contribution_cae # ------------------------------------------------------ # Business Function # ------------------------------------------------------ + def get_plancher_cotiz(self): + # Calculation plancher med + staff_id = self.get_last_staff_id(self.partner_id) + if staff_id: + staff_shareholder_count = staff_id.staff_shareholder_count + staff_count = staff_id.staff_count + else: + staff_shareholder_count = staff_count = 0 + if staff_count > 0: + societariat_rate = staff_shareholder_count / staff_count + else: + societariat_rate = 0 + if 0.5 <= societariat_rate < 0.8: + abatt_rate_med = 1 - 0.2 + elif societariat_rate >= 0.8: + abatt_rate_med = 1 - 0.3 + else: + abatt_rate_med = 1 - 0 + assiette_rate_med = 1/3 + plancher_cg = self.get_values_for_cotiz_cg( + self.partner_id)['plancher1'] + + return { + 'fede_com': 108, + 'fede_cae': 300, + 'ur_med': plancher_cg * assiette_rate_med * abatt_rate_med, + 'ur_hdf': 40, + } + def get_values_for_cotiz_cg(self, partner): # Calcul des Taux SCOP en fonction du type plancher1 = 300 @@ -298,4 +361,4 @@ class ScopLiasseFiscale(models.Model): :return: """ staff_id = partner.staff_ids.sorted(key=lambda l: l.effective_date) - return staff_id[-1] if len(staff_id) > 1 else staff_id \ No newline at end of file + return staff_id[-1] if len(staff_id) > 1 else staff_id diff --git a/report/__init__.py b/report/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..7b5fa1cac97787c466a30f5185a9cc5c65994f75 --- /dev/null +++ b/report/__init__.py @@ -0,0 +1,4 @@ +# © 2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import scop_contribution_report diff --git a/report/scop_contribution_report.py b/report/scop_contribution_report.py new file mode 100644 index 0000000000000000000000000000000000000000..2fed4b43a0ae60c723e3785a5d16feb752f58563 --- /dev/null +++ b/report/scop_contribution_report.py @@ -0,0 +1,44 @@ +# © 2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class ScopContributionReport(models.Model): + _inherit = "scop.contribution.report" + + def _select_invoice(self): + select_str = super(ScopContributionReport, self)._select_invoice() + select_str = """ + SELECT + CAST(i.year AS VARCHAR), + i.type_contribution_id, + i.partner_id, + SUM(i.amount_total_signed) AS amount_called, + SUM(i.amount_total_signed - i.residual_company_signed) AS amount_paid, + SUM(i.residual_company_signed) AS amount_due, + ( + CASE WHEN max(refund.id) IS NOT NULL THEN true ELSE FALSE END OR + CASE WHEN max(refund_b.id) IS NOT NULL THEN true ELSE FALSE END + ) AS is_loss + """ + return select_str + + def _from_invoice(self): + from_str = super(ScopContributionReport, self)._from_invoice() + from_str += """ + LEFT JOIN + scop_bordereau b ON i.bordereau_id = b.id + LEFT JOIN + scop_bordereau refund_b ON b.id = refund_b.refund_id + """ + return from_str + + def _where_invoice(self): + where_str = super(ScopContributionReport, self)._where_invoice() + where_str += "AND (i.bordereau_id IS NULL OR b.is_regul = false OR b.is_regul IS NULL)" + return where_str + + # ------------------------------------------------------ + # Computed fields + # ------------------------------------------------------ diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index 68bfdfbe3ce217b57d6aa7c62b09d3b8bf20a629..658ea35458a8044306a690cf97ee97080993a6c5 100755 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -1,7 +1,11 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_scop_cotisation_simulation,access_scop_cotisation_simulation,model_scop_cotisation_cg_simulation,account.group_account_manager,1,1,1,1 +access_scop_cotisation_simulation,access_scop_cotisation_simulation,model_scop_cotisation_cg_simulation,cgscop_cotisation_cg.group_cotisation_cg_administrative,1,1,1,1 admin_access_scop_cotisation_simulation,admin_access_scop_cotisation_simulation,model_scop_cotisation_cg_simulation,cgscop_partner.group_cg_administrator,1,1,1,1 -access_scop_cotisation_cg,access_scop_cotisation_cg,model_scop_cotisation_cg,account.group_account_manager,1,1,1,0 +access_scop_cotisation_cg,access_scop_cotisation_cg,model_scop_cotisation_cg,cgscop_cotisation_cg.group_cotisation_cg_administrative,1,1,1,0 admin_access_scop_cotisation_cg,admin_access_scop_cotisation_cg,model_scop_cotisation_cg,cgscop_partner.group_cg_administrator,1,1,1,1 -access_scop_bordereau,access_scop_bordereau,model_scop_bordereau,account.group_account_manager,1,1,1,1 +access_scop_bordereau,access_scop_bordereau,model_scop_bordereau,cgscop_cotisation_cg.group_cotisation_cg_administrative,1,1,1,1 admin_access_scop_bordereau,admin_access_scop_bordereau,model_scop_bordereau,cgscop_partner.group_cg_administrator,1,1,1,1 +access_scop_bordereau_version,access_scop_bordereau_version,model_scop_bordereau_version,cgscop_cotisation_cg.group_cotisation_cg_administrative,1,1,1,1 +admin_access_scop_bordereau_version,admin_access_scop_bordereau_version,model_scop_bordereau_version,cgscop_partner.group_cg_administrator,1,1,1,1 +access_scop_bordereau_refund_wizard_quarter,access_scop_bordereau_refund_wizard_quarter,model_scop_bordereau_refund_wizard_quarter,cgscop_cotisation_cg.group_cotisation_cg_administrative,1,1,1,1 +admin_access_scop_bordereau_refund_wizard_quarter,admin_access_scop_bordereau_refund_wizard_quarter,model_scop_bordereau_refund_wizard_quarter,cgscop_partner.group_cg_administrator,1,1,1,1 diff --git a/security/security_rules.xml b/security/security_rules.xml index c4cd9866467d0f777dd85cd267fd1080dc5a5ac4..a4f7a2f581cf2101a68e0cbb1fd3ebf96e4a448e 100644 --- a/security/security_rules.xml +++ b/security/security_rules.xml @@ -32,6 +32,7 @@ <field name="perm_unlink" eval="True"/> </record> + <!-- Bordereaux only for own company --> <record id="cg_cotisation_cg_rule" model="ir.rule"> <field name="name">Bordereaux consultables que pour sa société</field> <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau"/> @@ -42,5 +43,27 @@ <field name="perm_unlink" eval="True"/> </record> + <!-- Service Admin grant all access --> + <record id="scop_cotisation_cg_admin" model="ir.rule"> + <field name="name">Cotisations - Modification - Service Administratif</field> + <field name="model_id" ref="cgscop_cotisation_cg.model_scop_cotisation_cg"/> + <field name="domain_force">[(1,'=',1)]</field> + <field name="groups" eval="[(6, 0, [ref('group_cotisation_cg_administrative')])]"/> + <field name="perm_read" eval="True"/> + <field name="perm_write" eval="True"/> + <field name="perm_create" eval="True"/> + <field name="perm_unlink" eval="True"/> + </record> + <record id="scop_bordereau_admin" model="ir.rule"> + <field name="name">Bordereaux - Modification - Service Administratif</field> + <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau"/> + <field name="domain_force">[(1,'=',1)]</field> + <field name="groups" eval="[(6, 0, [ref('group_cotisation_cg_administrative')])]"/> + <field name="perm_read" eval="True"/> + <field name="perm_write" eval="True"/> + <field name="perm_create" eval="True"/> + <field name="perm_unlink" eval="True"/> + </record> + </data> </odoo> \ No newline at end of file diff --git a/report/report_scop_bordereau.xml b/templates/report_scop_bordereau.xml similarity index 97% rename from report/report_scop_bordereau.xml rename to templates/report_scop_bordereau.xml index fc504bd551e1ad53ec4648553b773142b2404e5a..2457b564f0b6b858118cb91f5c5c1898611bb337 100644 --- a/report/report_scop_bordereau.xml +++ b/templates/report_scop_bordereau.xml @@ -46,15 +46,15 @@ <div></div> <h5 class="mt8" style="font-weight: 600;">Cotisations annuelles</h5> <p style="font-style: italic; font-size: 13px;"> - <t t-if="o.year_liasse_retenue">Calcul basé sur la liasse fiscale + <t t-if="o.year_liasse">Calcul basé sur la liasse fiscale <t t-if="o.is_liasse_previ"> <span>Prévisionnelle</span><br/> </t> <t t-else=""> - <span t-esc="str(o.year_liasse_retenue)" /><br/> + <span t-esc="str(o.year_liasse)" /><br/> </t> </t> - Assiette base <span t-field="o.type_assiette_retenu" /> : <span t-field="o.montant_assiette" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" />. + Assiette base <span t-field="o.type_assiette" /> : <span t-field="o.montant_assiette" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" />. </p> <p> <t t-set="amount_line" t-value="o.get_contribution_type()" /> diff --git a/report/report_scop_bordereau_payments.xml b/templates/report_scop_bordereau_payments.xml similarity index 89% rename from report/report_scop_bordereau_payments.xml rename to templates/report_scop_bordereau_payments.xml index ef314818c92dbfc42c6b7c18b8e2043360fd844b..decf3acc3de24e28d68d99ae4fb92400745b86e6 100644 --- a/report/report_scop_bordereau_payments.xml +++ b/templates/report_scop_bordereau_payments.xml @@ -37,15 +37,15 @@ <div></div> <h5 class="mt8" style="font-weight: 600;">Cotisations annuelles</h5> <p style="font-style: italic; font-size: 13px;"> - <t t-if="o.year_liasse_retenue">Calcul basé sur la liasse fiscale + <t t-if="o.year_liasse">Calcul basé sur la liasse fiscale <t t-if="o.is_liasse_previ"> <span>Prévisionnelle</span><br/> </t> <t t-else=""> - <span t-esc="str(o.year_liasse_retenue)" /><br/> + <span t-esc="str(o.year_liasse)" /><br/> </t> </t> - Assiette base <span t-field="o.type_assiette_retenu" /> : <span t-field="o.montant_assiette" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" />. + Assiette base <span t-field="o.type_assiette" /> : <span t-field="o.montant_assiette" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" />. </p> <p> <t t-set="amount_line" t-value="o.get_contribution_type()" /> @@ -76,12 +76,14 @@ <tr> <th>Date d'échéance</th> <th class="text-right">Montant appelé</th> - <th class="text-right">Montant réglé</th> + <th class="text-right">Montant dû</th> </tr> <tr t-foreach="schedule_line.get('plan')" t-as="line" style="border-bottom: 1px solid #ccc;"> - <td style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('date_maturity:day')"/></td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('debit')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('debit') - line.get('amount_residual')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></td> + <t t-if="line.get('debit') > 0"> + <td style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('date_maturity:day')"/></td> + <td class="text-right" style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('debit')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></td> + <td class="text-right" style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('amount_residual')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></td> + </t> </tr> </table> <table class="table table-sm table-striped" style="border: none;"> diff --git a/report/report_scop_bordereau_refund.xml b/templates/report_scop_bordereau_refund.xml similarity index 100% rename from report/report_scop_bordereau_refund.xml rename to templates/report_scop_bordereau_refund.xml diff --git a/views/account_invoice.xml b/views/account_invoice.xml index 93531d0b5c03b6d931bafb76c401edde2e79260d..0c0150510081e0efb1f5033d705c7fb1d6343f23 100644 --- a/views/account_invoice.xml +++ b/views/account_invoice.xml @@ -12,11 +12,14 @@ <xpath expr="//header/button[@name='action_invoice_open']" position="attributes"> <attribute name="states"/> - <attribute name="attrs">{'invisible':['|', ('type','=','out_invoice'), ('state','!=','draft')]}</attribute> + <attribute name="invisible">1</attribute> </xpath> <xpath expr="//header/button[@name='preview_invoice']" position="attributes"> <attribute name="invisible">True</attribute> </xpath> + <xpath expr="//header/button[@name='%(account.action_account_invoice_refund)d']" position="attributes"> + <attribute name="invisible">True</attribute> + </xpath> <xpath expr="//field[@name='liasse_fiscale_id']" position="replace"> <group> @@ -25,8 +28,7 @@ </group> <group> <field name="amount_cg_calculated" readonly="1"/> - <field name="nb_quarter" readonly="1"/> - <field name="amount_cg_prorata" readonly="1"/> + <field name="cotiz_quarter" readonly="1"/> </group> </xpath> @@ -35,11 +37,7 @@ </xpath> <xpath expr="//field[@name='payment_term_id']" position="attributes"> - <attribute name="attrs">{'invisible':[('state','=','draft')]}</attribute> - </xpath> - - <xpath expr="//field[@name='date_invoice']" position="attributes"> - <attribute name="attrs">{'invisible':[('state','=','draft')]}</attribute> + <attribute name="invisible">1</attribute> </xpath> <xpath expr="//field[@name='date_due']" position="attributes"> @@ -93,12 +91,5 @@ <field name="act_window_id" ref="action_scop_cg_appel_cotisation"/> </record> - <!-- MENUS --> - <menuitem id="menu_scop_cotisation_cg_appel_cotisation" - name="Appels de cotisations" - parent="cgscop_cotisation.menu_scop_cotisation" - action="action_scop_cg_appel_cotisation" - sequence="20"/> - </data> </odoo> \ No newline at end of file diff --git a/views/menus.xml b/views/menus.xml new file mode 100644 index 0000000000000000000000000000000000000000..cf83ae6744ce629cd8ef5fcbb472b79e762df853 --- /dev/null +++ b/views/menus.xml @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<!-- Copyright 2019 Le Filament + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> + +<odoo> + <data> + <!-- MENUS --> + <menuitem id="menu_scop_cotisation_cg_main" + parent="cgscop_cotisation.menu_scop_cotisation" + name="Gestion des cotisations CG Scop" + groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" + sequence="10"/> + <menuitem id="menu_scop_cotisation_cg_calcul" + name="Campagnes de cotisations" + parent="cgscop_cotisation_cg.menu_scop_cotisation_cg_main" + groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" + action="action_scop_cotisation_cg" + sequence="10"/> + <menuitem name="Bordereaux" + id="scop_bordereau_menu" + parent="cgscop_cotisation_cg.menu_scop_cotisation_cg_main" + groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" + action="scop_bordereau_act_window" + sequence="20"/> + <menuitem id="menu_scop_cotisation_cg_appel_cotisation" + name="Appels de cotisations" + parent="cgscop_cotisation_cg.menu_scop_cotisation_cg_main" + groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" + action="action_scop_cg_appel_cotisation" + sequence="30"/> + </data> +</odoo> diff --git a/views/scop_bordereau_cg.xml b/views/scop_bordereau_cg.xml index 3b5fe37db7c19366d1b88c6cd8bbf6d98503175d..2b3bc49fed20d0010d4b995882efc61af22be1a5 100644 --- a/views/scop_bordereau_cg.xml +++ b/views/scop_bordereau_cg.xml @@ -12,15 +12,25 @@ <form create="false" string="Bordereaux"> <header> <field name="state" widget="statusbar" clickable="False" readonly="True"/> - <button name="validate_bordereau" class="oe_highlight" string="Valider le bordereau" type="object" states="new" confirm="Confirmer la validation du bordereau ?"/> + <button name="validate_bordereau" class="oe_highlight" string="Valider le bordereau" type="object" + states="new" confirm="Confirmer la validation du bordereau ?"/> <button name="print_bordereau" string="Imprimer" type="object" states="validated,paid"/> <button name="action_send_email" string="Envoyer par mail" type="object" states="validated"/> - <button name="update_cotiz_and_lines" class="btn-info" string="Mettre à jour les cotisations" type="object" states="new"/> + <button name="validate_ongoing_bordereau" class="oe_highlight" string="Valider cette nouvelle version" type="object" states="ongoing"/> + <button name="cancel_ongoing_bordereau" class="oe_highlight" string="Annuler cette nouvelle version" type="object" states="ongoing"/> + <button name="action_change_payment_mode" + class="btn-info" string="Changer le mode de paiement" + type="object" states="ongoing,validated"/> <button name="%(cgscop_cotisation_cg.scop_cotisation_regul_wizard_act_window)d" class="btn-warning" string="Effectuer une Régularisation" type="action" states="validated,paid"/> + <button name="%(cgscop_cotisation_cg.scop_bordereau_refund_wizard_act_window)d" + class="btn-warning" + string="Ajouter un avoir" + type="action" + states="ongoing,validated,paid"/> <button name="%(cgscop_bordereau_report_refund)d" class="btn-info" string="Imprimer l'avoir" @@ -29,10 +39,19 @@ </header> <sheet> <div class="oe_button_box" name="button_box"> + <button name="action_show_liasse" type="object" + class="oe_stat_button" icon="fa-bar-chart-o" + attrs="{'invisible':[('liasse_count','=',0)]}"> + <field string="Liasses" name="liasse_count" widget="statinfo"/> + </button> + <button name="action_show_cotiz" type="object" + class="oe_stat_button" icon="fa-pencil-square-o" + attrs="{'invisible':[('invoice_count','=',0)]}"> + <field string="Appels" name="invoice_count" widget="statinfo"/> + </button> <button class="oe_stat_button" name="open_payment" string="Paiements en cours" type="object" - attrs="{'invisible':['|', ('move_reconciled','=',True), ('state', '!=', 'validated')]}" icon="fa-university"/> - <field name="move_reconciled" invisible="1"/> + attrs="{'invisible':['|', ('has_outstanding','=',False), ('state', '!=', 'validated')]}" icon="fa-university"/> </div> <div class="oe_title"> <h1> @@ -43,98 +62,98 @@ Adhérent : <field name="partner_id"/> <br/> UR : <field name="partner_ur_id" readonly="True" options="{'no_open': True}"/> </h4> + <field name="has_outstanding" invisible="1"/> + <div class="alert alert-info mt-3" role="alert" + attrs="{'invisible': ['|', ('has_outstanding','=',False), ('state', '!=', 'validated')]}"> + Vous avez des paiements en circulation pour ce bordereau. + </div> + <div class="alert alert-warning" role="alert" attrs="{'invisible': [('state', '!=', 'ongoing')]}"> + <strong>Bordereau en cours de modification </strong>(Détails dans l'onglet "Historique")<br/> + </div> <field name="is_regul" invisible="1"/> <div class="alert alert-warning" role="alert" attrs="{'invisible': [('is_regul', '!=', True)]}"> <strong>Bordereau annulé le : </strong><field name="date_regul" readonly="True"/><br/> <strong>Motif : </strong><field name="comment_regul" readonly="True"/><br/> <strong>Nouveau bordereau : </strong><field name="refund_id" readonly="True"/> </div> + <div class="alert alert-warning" role="alert" attrs="{'invisible': ['|', ('is_regul', '=', True), ('comment_regul', '=', False)]}"> + <strong>Bordereau version <field name="version" readonly="1"/></strong><br/> + <strong>Modifié le : </strong><field name="date_regul" readonly="True"/><br/> + <strong>Motif : </strong><field name="comment_regul" readonly="True"/><br/> + </div> <group> <group> <field name="base_cotisation_cg"/> <field name="date_cotisation" attrs="{'readonly':[('state','!=','new')]}"/> - <field name="payment_term_id" attrs="{'readonly':[('state','!=','new')]}"/> <field name="payment_mode_id" attrs="{'readonly':[('state','!=','new')]}"/> - <field name="liasse_fiscale_id"/> - <field name="type_liasse_fiscale" options="{'no_open': True}"/> + <hr/> + <field name="nb_quarter" attrs="{'readonly':[('state','!=','new')]}"/> + <button name="update_bordereau_with_liasse" class="btn" string="Recalculer le bordereau" + confirm="Recalculer les valeurs du bordereau en fonction de la liasse et du nombre de trimestres ?" type="object" states="new"/> </group> <group> - <field name="year_liasse_retenue" attrs="{'readonly':[('state','!=','new')]}"/> - <field name="type_assiette_retenu"/> - <field name="montant_assiette"/> + <span class="oe_grey" attrs="{'invisible': [('liasse_fiscale_id', '!=', False)]}">Pas de liasse fiscale pour le calcul</span> + <field name="liasse_fiscale_id" attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}"/> + <field name="year_liasse" attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}"/> + <field name="type_liasse_fiscale" options="{'no_open': True}" attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}"/> + <button name="action_change_liasse" + class="btn-info mb5" + string="Changer de liasse fiscale" + type="object" + states="new"/> + <br/> + <hr/> + <field name="type_assiette" attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}"/> + <field name="montant_assiette" attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}"/> <field name="amount_total_cotiz"/> - <field name="dureeExercice"/> - <field name="nb_quarter" attrs="{'readonly':[('state','!=','new')]}"/> + <field name="details" nolabel="1"/> </group> - <hr/> - <field name="invoice_ids" widget="one2many"> - <tree edit="false" create="false" delete="false"> - <field name="type_contribution_id"/> - <field name="date_invoice"/> - <field name="name"/> - <field name="amount_total_signed" string="Montant total" sum="Total"/> - <field name="residual_signed" string="Montant dû" sum="Total"/> - <field name="state"/> - <button name="view_cotiz" string="Afficher" type="object" icon="fa-pencil-square-o"/> - </tree> - <form> - <group> + </group> + + <notebook> + <page string="Appels de cotisation"> + <field name="invoice_ids" widget="one2many" class="mt-2"> + <tree edit="false" create="false" delete="false" default_order="type_contribution_id, cotiz_quarter" + decoration-info="state == 'draft'" decoration-success="state == 'paid'" + decoration-muted="state == 'cancel'" decoration-danger="type == 'out_refund' and state == 'open'"> + <field name="type" invisible="1"/> <field name="type_contribution_id"/> - <field name="date_invoice"/> + <field name="cotiz_quarter"/> + <field name="date_due"/> <field name="name"/> - <field name="amount_cg_calculated"/> - <field name="amount_total_signed"/> - <field name="residual_signed"/> - <field name="nb_quarter" /> + <field name="amount_total_signed" string="Montant total" sum="Total"/> + <field name="residual_signed" string="Montant dû" sum="Total"/> <field name="state"/> - </group> - </form> - </field> - </group> - <notebook> - <page string="Cotisation CG Scop"> - <group col="2"> - <group string="Données liasse fiscale"> - <field name="type_assiette_connu"/> - <field name="ca_connu"/> - <field name="va_connu"/> - </group> - <group string="Données pour le calcul"> - <field name="type_assiette_retenu"/> - <field name="ca_retenu" attrs="{'readonly':[('state','!=','new')]}"/> - <field name="va_cg_retenu" attrs="{'readonly':[('state','!=','new')]}"/> - </group> - </group> - </page> - <page string="Cotisation Fédé de la com"> - <group col="2"> - <group string="Données liasse fiscale"> - <field name="va_connu"/> - <field name="staff_average_connu"/> - <field name="staff_count_connu"/> - </group> - <group string="Données pour le calcul"> - <field name="va_fede_com_retenu" attrs="{'readonly':[('state','!=','new')]}"/> - <field name="staff_average_retenu" attrs="{'readonly':[('state','!=','new')]}"/> - <field name="staff_fede_com_count_retenu" attrs="{'readonly':[('state','!=','new')]}"/> - </group> - </group> + <button name="view_cotiz" string="Afficher" type="object" icon="fa-pencil-square-o"/> + </tree> + <form> + <group> + <field name="type_contribution_id"/> + <field name="cotiz_quarter"/> + <field name="date_due"/> + <field name="name"/> + <field name="amount_cg_calculated"/> + <field name="amount_total_signed"/> + <field name="residual_signed"/> + <field name="nb_quarter" /> + <field name="state"/> + </group> + </form> + </field> </page> - <page string="Cotisation UR"> - <group col="2"> - <group string="Données liasse fiscale"> - <field name="net_results_connu"/> - <field name="staff_shareholder_count_connu"/> - <field name="staff_count_connu"/> - <field name="wage_cg_connu"/> - </group> - <group string="Données pour le calcul"> - <field name="net_results_retenu" attrs="{'readonly':[('state','!=','new')]}"/> - <field name="staff_shareholder_count_retenu" attrs="{'readonly':[('state','!=','new')]}"/> - <field name="staff_ur_med_count_retenu" attrs="{'readonly':[('state','!=','new')]}"/> - <field name="wage_cg_retenu" attrs="{'readonly':[('state','!=','new')]}"/> - </group> - </group> + <page string="Historique"> + <field name="bordereau_version_ids" widget="one2many" class="mt-2"> + <tree create="false" delete="false" default_order="version desc" editable="top"> + <field name="date"/> + <field name="version"/> + <field name="comment" attrs="{'readonly':[('state','=','validated')]}"/> + <field name="liasse_fiscale_id_old"/> + <field name="type_assiette"/> + <field name="montant_assiette"/> + <field name="amount_total_cotiz"/> + <field name="state" widget="label_selection"/> + </tree> + </field> </page> </notebook> </sheet> @@ -157,7 +176,7 @@ <field name="partner_ur_id"/> <field name="year"/> <field name="state"/> - <field name="type_assiette_retenu"/> + <field name="type_assiette"/> <field name="montant_assiette"/> <field name="amount_total_cotiz" sum="Total"/> <field name="amount_residual" sum="Total"/> @@ -177,10 +196,14 @@ <field name="year" string="Année de cotisation"/> <filter name="state_new" string="Brouillon" domain="[('state', '=', 'new')]"/> + <filter name="state_ongoing" string="En cours de modification" + domain="[('state', '=', 'ongoing')]"/> <filter name="state_validated" string="Validé" domain="[('state', '=', 'validated')]"/> <filter name="state_paid" string="Payé" domain="[('state', '=', 'paid')]"/> + <filter name="has_outstanding" string="Paiements en cours" + domain="[('has_outstanding', '=', True)]"/> <separator/> <filter name="current_year" string="Campagne année en cours" domain="[('year', '=', (context_today()).strftime('%Y'))]"/> @@ -189,9 +212,9 @@ <filter name="next_year" string="Campagne année suivante" domain="[('year', '=', (context_today()+datetime.timedelta(weeks=52)).strftime('%Y'))]"/> <separator/> - <filter name="bordereau_equal_zero" string="Cotisation(s) égale(s) à zéro" - domain="[('amount_total_cotiz', '=', 0)]"/> - <filter name="assiette_equal_zero" string="Assiette(s) égale(s) à zéro" + <filter name="bordereau_without_liasse" string="Pas de liasse de référence" + domain="[('liasse_fiscale_id', '=', False)]"/> + <filter name="assiette_equals_zero" string="Assiette(s) égale(s) à zéro" domain="[('montant_assiette', '=', 0)]"/> <separator/> <filter name="4_quarter" string="Sur 4 trimestres" domain="[('nb_quarter', '=', '4')]"/> @@ -199,8 +222,8 @@ <filter name="2_quarter" string="Sur 2 trimestres" domain="[('nb_quarter', '=', '2')]"/> <filter name="1_quarter" string="Sur 1 trimestre" domain="[('nb_quarter', '=', '1')]"/> <separator/> - <filter name="is_ca" string="Assiette CA" domain="[('type_assiette_retenu', '=', 'ca')]"/> - <filter name="is_va" string="Assiette VA" domain="[('type_assiette_retenu', '=', 'va')]"/> + <filter name="is_ca" string="Assiette CA" domain="[('type_assiette', '=', 'ca')]"/> + <filter name="is_va" string="Assiette VA" domain="[('type_assiette', '=', 'va')]"/> <group string="Group By"> <filter name="by_year" string="Année de cotisation" context="{'group_by':'year'}"/> </group> @@ -210,19 +233,12 @@ <!-- Action --> <record id="scop_bordereau_act_window" model="ir.actions.act_window"> - <field name="name">Bodereaux</field> + <field name="name">Bordereaux</field> <field name="type">ir.actions.act_window</field> <field name="res_model">scop.bordereau</field> <field name="view_mode">tree,form</field> <field name="context">{'search_default_current_year': 1}</field> </record> - <!-- Menu --> - <menuitem name="Bordereaux" - id="scop_bordereau_menu" - parent="cgscop_cotisation.menu_scop_cotisation" - action="scop_bordereau_act_window" - sequence="30"/> - </data> </odoo> \ No newline at end of file diff --git a/views/scop_cotisation_cg.xml b/views/scop_cotisation_cg.xml index 16fdaa47725a247d7773411ede0c4d978c59cba3..cead779d9a90cf06c42c96b83fe29ead09f74f41 100644 --- a/views/scop_cotisation_cg.xml +++ b/views/scop_cotisation_cg.xml @@ -44,7 +44,6 @@ <field name="date_cotisation" attrs="{'readonly': [('state', '!=', 'new')], 'required': [('state', '!=', 'new')]}"/> <field name="create_date"/> <field name="write_date"/> - <field name="payment_term_id"/> </group> <group name="count_contribution" string="Adhérents"> <field name="member_count" readonly="1"/> @@ -156,11 +155,5 @@ <field name="help">Affiche les bases de calcul des cotisations</field> </record> - - <!-- MENUS --> - <menuitem id="menu_scop_cotisation_cg_calcul" - parent="cgscop_cotisation.menu_scop_cotisation" - action="action_scop_cotisation_cg" - sequence="10"/> </data> </odoo> diff --git a/views/scop_liasse_fiscale.xml b/views/scop_liasse_fiscale.xml index 2f5260ea5c31affedcb7e9f3ff0faad1a6a7b363..c9556bfa0e116c32c3e88156cf663dbc2e1cc850 100644 --- a/views/scop_liasse_fiscale.xml +++ b/views/scop_liasse_fiscale.xml @@ -27,6 +27,7 @@ <field name="contribution_hdf" string="Cotisation HDF"/> <field name="contribution_med" string="Cotisation Méditerranée"/> <field name="contribution_com" string="Cotisation Fédération Com"/> + <field name="contribution_cae" string="Cotisation Fédération CAE"/> </group> </group> </xpath> diff --git a/wizard/__init__.py b/wizard/__init__.py index 909809b470b208bb2bf6c457cdac1fea10011631..a23c4c181537020603c1172196a3bd075001c6b0 100644 --- a/wizard/__init__.py +++ b/wizard/__init__.py @@ -3,6 +3,9 @@ from . import account_invoice_refund from . import export_journal_wizard +from . import scop_bordereau_update_liasse_wizard +from . import scop_bordereau_payment_mode_wizard +from . import scop_bordereau_refund_wizard from . import scop_bordereau_update_confirm from . import scop_bordereau_validate_confirm from . import scop_cotisation_cg_regul diff --git a/wizard/scop_bordereau_payment_mode_wizard.py b/wizard/scop_bordereau_payment_mode_wizard.py new file mode 100644 index 0000000000000000000000000000000000000000..45efe0ab20b24c13e42fc35a59855c52e588cedf --- /dev/null +++ b/wizard/scop_bordereau_payment_mode_wizard.py @@ -0,0 +1,58 @@ +# Copyright 2021 Le Filament +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models, api + + +class ScopBordereauChangePaymentMode(models.TransientModel): + _name = 'scop.bordereau.change.payment.mode.wizard' + _description = 'Changement de mode de paiement' + + bordereau_id = fields.Many2one( + comodel_name='scop.bordereau', + string='Bordereau', + readonly=True, + ) + payment_mode_id = fields.Many2one( + comodel_name='account.payment.mode', + string="Nouveau mode de paiment",) + change_type = fields.Selection( + string='Type d\'affectation', + selection=[('punctual', 'Définir pour les échéances non payées de ce ' + 'bordereau uniquement'), + ('all', 'Définir pour toutes les prochaines échéances')], + default='punctual', + required=False, ) + + # ------------------------------------------------------ + # Action Button + # ------------------------------------------------------ + def action_change_payment_mode(self): + bordereau_id = self.bordereau_id + # Link new liasse fiscale to bordereau + if self.change_type == 'all': + bordereau_id.partner_id.update({ + 'customer_payment_mode_id': self.payment_mode_id.id, + }) + bordereau_id.update({ + 'payment_mode_id': self.payment_mode_id.id, + }) + bordereau_id.invoice_ids.update({ + 'payment_mode_id': self.payment_mode_id.id, + }) + contrib_cg_journal = self.env.user.company_id.contribution_journal_id + contrib_ur_fede_journal = \ + self.env.user.company_id.contribution_ur_or_fede_journal_id + move_line_ids = self.env['account.move.line'].search([ + ('partner_id', '=', self.bordereau_id.partner_id.id), + ('journal_id', 'in', ( + contrib_cg_journal.id, contrib_ur_fede_journal.id)), + ('full_reconcile_id', '=', False), + ('balance', '!=', 0), + ('account_id.reconcile', '=', True), + ('account_id.internal_type', '=', 'receivable') + ]) + move_line_ids.update({ + 'payment_mode_id': self.payment_mode_id.id, + }) + return {'type': 'ir.actions.act_window_close'} diff --git a/wizard/scop_bordereau_payment_mode_wizard.xml b/wizard/scop_bordereau_payment_mode_wizard.xml new file mode 100644 index 0000000000000000000000000000000000000000..4af8d77fe44f19aefa6aac02dae0c207c42122f6 --- /dev/null +++ b/wizard/scop_bordereau_payment_mode_wizard.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <!-- Copyright 2021 Le Filament + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> + <data> + + <record id="scop_bordereau_change_payment_mode_wizard_form_view" model="ir.ui.view"> + <field name="name">scop.bordereau.change.payment.mode.wizard.form</field> + <field name="model">scop.bordereau.change.payment.mode.wizard</field> + <field name="arch" type="xml"> + <form string="Changement de mode de paiement"> + <group> + <field name="payment_mode_id" options="{'no_open': True, 'no_create': True}"/> + <field name="change_type" widget="radio" nolabel="1" class="mt-4"/> + </group> + <footer> + <button name="action_change_payment_mode" string="Ok" type="object" default_focus="1" class="oe_highlight"/> + <button class="oe_highlight" string="Annuler" special="cancel"/> + </footer> + </form> + </field> + </record> + + </data> +</odoo> \ No newline at end of file diff --git a/wizard/scop_bordereau_refund_wizard.py b/wizard/scop_bordereau_refund_wizard.py new file mode 100644 index 0000000000000000000000000000000000000000..53614d19218f20135ec17c2e5150aeb6b42ed92e --- /dev/null +++ b/wizard/scop_bordereau_refund_wizard.py @@ -0,0 +1,200 @@ +# Copyright 2021 Le Filament +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models, api +from odoo.exceptions import ValidationError, UserError + + +class ScopBordereauRefundWizard(models.TransientModel): + _name = 'scop.bordereau.refund.wizard' + _description = 'Avoir sur les bordereaux' + + bordereau_id = fields.Many2one( + comodel_name='scop.bordereau', + string='Bordereau', + readonly=True, + ) + date_refund = fields.Date( + string="Date de régularisation", + default=fields.Date.today(), required=1) + cotiz_reminder = fields.Html('Rappel des cotisations', readonly=1) + amount_refund = fields.Float('Montant (par trimestre)', required=1) + comment = fields.Char('Motif de l\'avoir', required=1) + type_cotiz = fields.Selection( + string='Type de cotisation', + selection=lambda self: self._selection_type_cotiz(), + required=1) + quarter_ids = fields.Many2many( + relation='scop_bordereau_refund_wizard_quarter_rel', + comodel_name='scop.bordereau.refund.wizard.quarter', + string='Trimestres') + + # ------------------------------------------------------ + # Constrains + # ------------------------------------------------------ + @api.constrains('date_refund') + def _check_date_refund(self): + last_date = max(self.bordereau_id.invoice_ids.mapped('date_invoice')) + if self.date_refund > fields.Date.today() or \ + self.date_refund < last_date: + raise ValidationError("La date de l'avoir doit être " + "inférieure ou égale à la date du jour et " + "supérieure à la dernière date de " + "facturation liée au bordereau.") + + # ------------------------------------------------------ + # Override ORM + # ------------------------------------------------------ + @api.model + def default_get(self, fields): + res = super(ScopBordereauRefundWizard, self).default_get(fields) + bordereau_id = self.env['scop.bordereau'].browse( + self.env.context.get('active_id') + ) + res.update({ + 'bordereau_id': bordereau_id.id, + 'cotiz_reminder': bordereau_id.details, + }) + return res + + @api.model + def _selection_type_cotiz(self): + 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']) + + ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + if bordereau_id.partner_id.ur_id.id == ur_hdf: + product_ur = self.env.user.company_id.contribution_hdf_id + else: # ur = ur_med + product_ur = self.env.user.company_id.contribution_med_id + + cotiz_type = { + self.env.ref('cgscop_partner.riga_14397').id: + self.env.user.company_id.contribution_cg_id, + self.env.ref('cgscop_partner.riga_14398').id: + self.env.user.company_id.contribution_fede_com_id, + self.env.ref('cgscop_partner.cotiz_fede_cae').id: + self.env.user.company_id.contribution_fede_cae_id, + self.env.ref('cgscop_partner.riga_14399').id: + product_ur, + } + + type_cotiz_select_i = list() + type_cotiz_select_list = list() + for contrib in contribs: + type_cotiz = contrib.get('type_contribution_id')[0] + if type_cotiz not in type_cotiz_select_i: + type_cotiz_select_i.append(type_cotiz) + type_cotiz_select_list.append( + (type_cotiz, cotiz_type[type_cotiz].name)) + return type_cotiz_select_list + + # ------------------------------------------------------ + # Action + # ------------------------------------------------------ + def create_refund(self): + """ + Create refund + """ + if len(self.quarter_ids) == 0: + raise UserError('Vous devez sélectionner au moins un trimestre.') + if self.amount_refund <= 0: + raise UserError('Le montant de l\'avoir doit être supérieur à 0.') + if not self.type_cotiz: + raise UserError('Vous devez sélectionner un type de cotisation.') + bordereau_id = self.bordereau_id + partner_id = bordereau_id.partner_id + ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + + # CREATE VERSION + bordereau_id.read(['amount_total_cotiz']) + ongoing_version = bordereau_id.bordereau_version_ids.filtered( + lambda v: v.version == bordereau_id.version and v.state == 'new' + ) + if not ongoing_version: + self.env['scop.bordereau.version'].create({ + 'bordereau_id': bordereau_id.id, + 'date': self.date_refund, + 'comment': self.comment, + 'version': bordereau_id.version, + 'liasse_fiscale_id_old': bordereau_id.liasse_fiscale_id.id, + 'type_assiette': bordereau_id.type_assiette, + 'montant_assiette': bordereau_id.montant_assiette, + 'amount_total_cotiz': bordereau_id.amount_total_cotiz, + }) + + # CREATE REFUND + if partner_id.ur_id.id == ur_hdf: + product_ur = self.env.user.company_id.contribution_hdf_id + account_ur = self.env.user.company_id.receivable_account_ur_hdf_id + else: # ur = ur_med + product_ur = self.env.user.company_id.contribution_med_id + account_ur = self.env.user.company_id.receivable_account_ur_med_id + + cotiz_type = { + self.env.ref('cgscop_partner.riga_14397').id: + [self.env.user.company_id.contribution_cg_id, + self.env.user.company_id.contribution_journal_id, + partner_id.property_account_receivable_id], + self.env.ref('cgscop_partner.riga_14398').id: + [self.env.user.company_id.contribution_fede_com_id, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + self.env.user.company_id.receivable_account_fede_com_id], + self.env.ref('cgscop_partner.cotiz_fede_cae').id: + [self.env.user.company_id.contribution_fede_cae_id, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + self.env.user.company_id.receivable_account_fede_cae_id], + self.env.ref('cgscop_partner.riga_14399').id: + [product_ur, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + account_ur], + } + product = cotiz_type.get(int(self.type_cotiz))[0] + + for quarter_id in self.quarter_ids: + refund = self.env['account.invoice'].create({ + 'partner_id': partner_id.id, + 'journal_id': cotiz_type.get(int(self.type_cotiz))[1].id, + 'account_id': cotiz_type.get(int(self.type_cotiz))[2].id, + 'type': 'out_refund', + 'date_invoice': self.date_refund, + 'date': self.date_refund, + 'state': 'draft', + 'number': False, + 'origin': bordereau_id.name, + 'name': self.comment, + 'bordereau_id': bordereau_id.id, + 'is_contribution': True, + 'year': bordereau_id.year, + 'cotiz_quarter': quarter_id.quarter, + 'liasse_fiscale_id': bordereau_id.liasse_fiscale_id.id, + 'type_contribution_id': self.type_cotiz, + 'payment_mode_id': bordereau_id.payment_mode_id.id, + 'date_due': self.date_refund, + }) + self.env['account.invoice.line'].create({ + 'name': self.comment, + 'invoice_id': refund.id, + 'product_id': product.id, + 'account_id': product.property_account_income_id.id, + 'price_unit': self.amount_refund + }) + + +class ScopBordereauRefundWizardQuarer(models.Model): + _name = 'scop.bordereau.refund.wizard.quarter' + _description = 'Trimestres pour échéance de cotisation' + + name = fields.Char('Nom', compute='_compute_name') + quarter = fields.Integer('Trismestre', required=1) + + @api.multi + def _compute_name(self): + for r in self: + r.name = str(r.quarter) diff --git a/wizard/scop_bordereau_refund_wizard.xml b/wizard/scop_bordereau_refund_wizard.xml new file mode 100644 index 0000000000000000000000000000000000000000..e42f5c0268bb3f78a2a2faeb20aead211ea6b5c8 --- /dev/null +++ b/wizard/scop_bordereau_refund_wizard.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <!-- Copyright 2021 Le Filament + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> + <data> + + <record id="scop_bordereau_refund_wizard_form_view" model="ir.ui.view"> + <field name="name">scop.bordereau.refund.wizard.form</field> + <field name="model">scop.bordereau.refund.wizard</field> + <field name="arch" type="xml"> + <form string="Générer un avoir"> + <sheet> + <group> + <field name="cotiz_reminder"/> + <hr/> + <field name="type_cotiz"/> + <field name="date_refund"/> + <separator/> + <field name="amount_refund"/> + <field name="quarter_ids" widget="many2many_checkboxes"/> + <field name="comment"/> + </group> + </sheet> + <footer> + <button name="create_refund" type="object" string="Créer l'avoir" class="oe_highlight"/> + <button string="Annuler" special="cancel" class="oe_link"/> + </footer> + </form> + </field> + </record> + + <record id="scop_bordereau_refund_wizard_act_window" model="ir.actions.act_window"> + <field name="name">Avoir sur le bordereau</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">scop.bordereau.refund.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 diff --git a/wizard/scop_bordereau_update_confirm.py b/wizard/scop_bordereau_update_confirm.py index 0779da108029618241dd39281f1fbc23f5dd1dce..d14e35b55aae3103f33a53f7e3c5e89f5e607790 100644 --- a/wizard/scop_bordereau_update_confirm.py +++ b/wizard/scop_bordereau_update_confirm.py @@ -22,5 +22,5 @@ class ScopBordereauUpdate(models.TransientModel): if record.state != 'new': raise UserError(_("Impossible de mettre à jour un bordereau " "qui n'est pas à l'état de brouillon")) - record.update_cotiz_and_lines() + record.create_cotiz_and_lines() return {'type': 'ir.actions.act_window_close'} diff --git a/wizard/scop_bordereau_update_liasse_wizard.py b/wizard/scop_bordereau_update_liasse_wizard.py new file mode 100644 index 0000000000000000000000000000000000000000..bd0ed6a8e3b00af425a11a042bfc0a1100ee1759 --- /dev/null +++ b/wizard/scop_bordereau_update_liasse_wizard.py @@ -0,0 +1,162 @@ +# Copyright 2021 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 ScopBordereauChangeLiasse(models.TransientModel): + _name = 'scop.bordereau.change.liasse.wizard' + _description = 'Changement de liasse sur le bordereau' + + bordereau_id = fields.Many2one( + comodel_name='scop.bordereau', + string='Bordereau', + readonly=True, + ) + partner_id = fields.Many2one( + related='bordereau_id.partner_id' + ) + company_currency_id = fields.Many2one( + related='bordereau_id.company_currency_id') + + # Ancienne liasse + liasse_fiscale_id = fields.Many2one( + related='bordereau_id.liasse_fiscale_id' + ) + amount_total_cotiz = fields.Monetary( + related='bordereau_id.amount_total_cotiz', + currency_field='company_currency_id' + ) + amount_cg = fields.Float( + 'Cotisation CG Scop actuelle', compute='_compute_amount_cotiz') + amount_ur_med = fields.Float( + 'Cotisation UR Med actuelle', compute='_compute_amount_cotiz') + amount_ur_hdf = fields.Float( + 'Cotisation UR HDF actuelle', compute='_compute_amount_cotiz') + amount_fede_com = fields.Float( + 'Cotisation Fede Com actuelle', compute='_compute_amount_cotiz') + amount_fede_cae = fields.Float( + 'Cotisation Fede CAE actuelle', compute='_compute_amount_cotiz') + type_assiette = fields.Selection( + string='Type d\'assiette', + related='bordereau_id.type_assiette',) + montant_assiette = fields.Integer( + string='Montant de l\'assiette', + related='bordereau_id.montant_assiette') + ca = fields.Float( + related='bordereau_id.ca') + va = fields.Float( + related='bordereau_id.va') + net_results = fields.Float( + related='bordereau_id.net_results') + wage_cg = fields.Float( + related='bordereau_id.wage_cg') + + # Nouvelle liasse + liasse_fiscale_new_id = fields.Many2one( + comodel_name='scop.liasse.fiscale', + string='Nouvelle liasse Fiscale', + ) + amount_total_cotiz_new = fields.Monetary( + string='Nouveau montant total de(s) cotisation(s)', + currency_field='company_currency_id', + readonly=1) + type_id_new = fields.Selection( + 'Type de liasse', related='liasse_fiscale_new_id.type_id') + source_new = fields.Selection( + 'Source de la liasse', related='liasse_fiscale_new_id.source') + amount_cg_new = fields.Float( + 'Cotisation CG Scop', + related='liasse_fiscale_new_id.contribution_cg') + amount_ur_med_new = fields.Float( + 'Cotisation UR Méditerranée', + related='liasse_fiscale_new_id.contribution_med') + amount_ur_hdf_new = fields.Float( + 'Cotisation UR HDF', + related='liasse_fiscale_new_id.contribution_hdf') + amount_fede_com_new = fields.Float( + 'Cotisation Fédé Communication', + related='liasse_fiscale_new_id.contribution_com') + amount_fede_cae_new = fields.Float( + 'Cotisation Fédé CAE', + related='liasse_fiscale_new_id.contribution_cae') + type_assiette_new = fields.Selection( + related='liasse_fiscale_new_id.contribution_base_type') + montant_assiette_new = fields.Integer( + related='liasse_fiscale_new_id.contribution_base_amount') + # ca_new = fields.Float( + # related='liasse_fiscale_new_id.av_cg') + # va_new = fields.Float( + # string='VA', compute='_compute_liasse_new_values') + # net_results_new = fields.Float( + # string='Résultat net', compute='_compute_liasse_new_values') + # wage_cg_new = fields.Float( + # string='Montant masse salariale', compute='_compute_liasse_new_values') + + # ------------------------------------------------------ + # Compute + # ------------------------------------------------------ + def _compute_amount_cotiz(self): + type_cotisation_cg = self.env.ref('cgscop_partner.riga_14397').id + type_cotisation_fede_com = self.env.ref('cgscop_partner.riga_14398').id + type_cotisation_fede_cae = self.env.ref( + 'cgscop_partner.cotiz_fede_cae').id + type_cotisation_ur = self.env.ref('cgscop_partner.riga_14399').id + for r in self: + partner = r.bordereau_id.partner_id + cotiz = r.bordereau_id.invoice_ids + # CG + cotiz_cg = cotiz.filtered( + lambda i: i.type_contribution_id.id == type_cotisation_cg + ) + r.amount_cg = sum(cotiz_cg.mapped('amount_total')) + # Fede Com + cotiz_fede_com = cotiz.filtered( + lambda i: i.type_contribution_id.id == type_cotisation_fede_com + ) + r.amount_fede_com = sum(cotiz_fede_com.mapped('amount_total')) + # Fede CAE + cotiz_fede_cae = cotiz.filtered( + lambda i: i.type_contribution_id.id == type_cotisation_fede_cae + ) + r.amount_fede_cae = sum(cotiz_fede_cae.mapped('amount_total')) + # UR HDF + ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + if partner.ur_id.id == ur_hdf: + cotiz_ur_hdf = cotiz.filtered( + lambda i: i.type_contribution_id.id == type_cotisation_ur + ) + r.amount_ur_hdf = sum(cotiz_ur_hdf.mapped('amount_total')) + # UR Med + ur_med = self.env.ref('cgscop_partner.riga_14243').id + if partner.ur_id.id == ur_med: + cotiz_ur_med = cotiz.filtered( + lambda i: i.type_contribution_id.id == type_cotisation_ur + ) + r.amount_ur_med = sum(cotiz_ur_med.mapped('amount_total')) + + # ------------------------------------------------------ + # Onchange + # ------------------------------------------------------ + @api.onchange('liasse_fiscale_new_id') + def _compute_liasse_new_values(self): + for r in self: + if r.liasse_fiscale_new_id: + r.amount_total_cotiz_new = \ + r.amount_cg_new + r.amount_ur_hdf_new + \ + r.amount_ur_med_new + r.amount_fede_com + + # ------------------------------------------------------ + # Action Button + # ------------------------------------------------------ + def update_liasse_fiscale(self): + bordereau_id = self.bordereau_id + if not self.liasse_fiscale_new_id: + raise UserError('Merci de choisir une nouvelle liasse fiscale.') + # Link new liasse fiscale to bordereau + bordereau_id.update({ + 'liasse_fiscale_id': self.liasse_fiscale_new_id, + }) + bordereau_id.create_cotiz_and_lines() + return {'type': 'ir.actions.act_window_close'} diff --git a/wizard/scop_bordereau_update_liasse_wizard.xml b/wizard/scop_bordereau_update_liasse_wizard.xml new file mode 100644 index 0000000000000000000000000000000000000000..610d83f7e9506e2d10f870c89c5f857890d92a4d --- /dev/null +++ b/wizard/scop_bordereau_update_liasse_wizard.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <!-- Copyright 2021 Le Filament + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> + <data> + + <record id="scop_bordereau_change_liasse_wizard_form_view" model="ir.ui.view"> + <field name="name">scop.bordereau.change.liasse.wizard.form</field> + <field name="model">scop.bordereau.change.liasse.wizard</field> + <field name="arch" type="xml"> + <form string="Changement de liasse"> + <sheet> + <div class="row"> + <div class="col-6"> + <group string="Données initiales" class="text-muted"> + <field name="liasse_fiscale_id" options='{"no_open": True}'/> + <hr/> + <field name="amount_total_cotiz"/> + <field name="amount_cg"/> + <field name="amount_ur_med" attrs="{'invisible':[('amount_ur_med','=',0)]}"/> + <field name="amount_ur_hdf" attrs="{'invisible':[('amount_ur_hdf','=',0)]}"/> + <field name="amount_fede_com" attrs="{'invisible':[('amount_fede_com','=',0)]}"/> + <field name="amount_fede_cae" attrs="{'invisible':[('amount_fede_cae','=',0)]}"/> + <hr/> + <field name="type_assiette"/> + <field name="montant_assiette"/> + <hr/> + <field name="ca"/> + <field name="va"/> + <field name="net_results"/> + <field name="wage_cg"/> + </group> + </div> + <div class="col-6"> + <group> + <field name="partner_id" invisible="1"/> + <field name="liasse_fiscale_new_id" options='{"no_open": True, "no_create":True}' + domain="[('partner_id', '=', partner_id), + ('is_qualified', '=', True), + ('id', '!=', liasse_fiscale_id)]"/> + <field name="type_id_new" attrs="{'invisible':[('liasse_fiscale_new_id','=',False)]}"/> + <field name="source_new" attrs="{'invisible':[('liasse_fiscale_new_id','=',False)]}"/> + </group> + <group string="Nouveaux calculs (Sans arrondi)" attrs="{'invisible':[('liasse_fiscale_new_id','=',False)]}"> + <field name="amount_total_cotiz_new"/> + <field name="amount_cg_new"/> + <field name="amount_ur_med_new" attrs="{'invisible':[('amount_ur_med_new','=',0)]}"/> + <field name="amount_ur_hdf_new" attrs="{'invisible':[('amount_ur_hdf_new','=',0)]}"/> + <field name="amount_fede_com_new" attrs="{'invisible':[('amount_fede_com_new','=',0)]}"/> + <field name="amount_fede_cae_new" attrs="{'invisible':[('amount_fede_cae_new','=',0)]}"/> + <hr/> + <field name="type_assiette_new"/> + <field name="montant_assiette_new"/> + <hr/> + </group> + </div> + </div> + </sheet> + <footer> + <button name="update_liasse_fiscale" type="object" string="Modifier la liasse" class="oe_highlight" confirm="Confirmer le changement de liasse"/> + <button string="Annuler" special="cancel" class="oe_link"/> + </footer> + </form> + </field> + </record> + + <record id="scop_bordereau_change_liasse_wizard_act_window" model="ir.actions.act_window"> + <field name="name">Changement liasse bordereau</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">scop.bordereau.change.liasse.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 diff --git a/wizard/scop_cotisation_cg_regul.py b/wizard/scop_cotisation_cg_regul.py index 4bc8d4b26ee753594feb8d6bc2417387dafc6660..5f0d6d7a1dc2e469d852148e0399481887e93daa 100644 --- a/wizard/scop_cotisation_cg_regul.py +++ b/wizard/scop_cotisation_cg_regul.py @@ -2,7 +2,8 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import fields, models, api -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError + class ScopCotisationRegul(models.TransientModel): _name = 'scop.cotisation.regul.wizard' @@ -22,17 +23,15 @@ class ScopCotisationRegul(models.TransientModel): string='Adhérent', readonly=True, ) + + # Old liasse 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', + string='Liasse Fiscale de référence', ) year = fields.Integer('Année de la liasse') type_assiette_retenu = fields.Selection( - string='Type assiette de retenu', + string='Type assiette', selection=[('ca', 'CA'), ('va', 'VA'), ], ) @@ -42,21 +41,46 @@ class ScopCotisationRegul(models.TransientModel): 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'), ], + + # New liasse + liasse_fiscale_new_id = fields.Many2one( + comodel_name='scop.liasse.fiscale', + string='Liasse Fiscale', ) - 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') + type_id_new = fields.Selection( + string='Type de liasse', related='liasse_fiscale_new_id.type_id') + source_new = fields.Selection( + string='Source de la liasse', related='liasse_fiscale_new_id.source') + type_assiette_new = fields.Selection( + related='liasse_fiscale_new_id.contribution_base_type') + montant_assiette_new = fields.Integer( + related='liasse_fiscale_new_id.contribution_base_amount') + amount_cg = fields.Float( + related='liasse_fiscale_new_id.contribution_cg') + amount_ur_med = fields.Float( + related='liasse_fiscale_new_id.contribution_med') + amount_ur_hdf = fields.Float( + related='liasse_fiscale_new_id.contribution_hdf') + amount_fede_com = fields.Float( + related='liasse_fiscale_new_id.contribution_com') + amount_fede_cae = fields.Float( + related='liasse_fiscale_new_id.contribution_cae') + is_payment = fields.Boolean('Paiements liés') + # ------------------------------------------------------ + # Constrains + # ------------------------------------------------------ + @api.constrains('date_regul') + def _check_date_regul(self): + last_date = max(self.bordereau_id.invoice_ids.mapped('date_invoice')) + if self.date_regul > fields.Date.today() or \ + self.date_regul < last_date: + raise ValidationError("La date de régulation doit être " + "inférieure ou égale à la date du jour et " + "supérieure à la dernière date de " + "facturation liée au bordereau.") + # ------------------------------------------------------ # Override ORM # ------------------------------------------------------ @@ -67,7 +91,7 @@ class ScopCotisationRegul(models.TransientModel): self.env.context.get('active_id') ) contribs = bordereau_id.invoice_ids.read_group( - [('id', 'in',bordereau_id.invoice_ids.ids)], + [('id', 'in', bordereau_id.invoice_ids.ids)], ['type_contribution_id', 'amount_total_signed'], ['type_contribution_id']) detail = "<table class='o_group o_inner_group'>" @@ -88,8 +112,8 @@ class ScopCotisationRegul(models.TransientModel): '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, + 'year': bordereau_id.year_liasse, + 'type_assiette_retenu': bordereau_id.type_assiette, 'montant_assiette': bordereau_id.montant_assiette, 'amount_total_cotiz': bordereau_id.amount_total_cotiz, 'detail': detail, @@ -98,150 +122,127 @@ class ScopCotisationRegul(models.TransientModel): return res # ------------------------------------------------------ - # Button function + # Action # ------------------------------------------------------ - 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.sudo().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.") + raise UserError("Vous devez renseigner un motif de " + "régularisation pour valider.") bordereau_id = self.bordereau_id + partner_id = bordereau_id.partner_id - # Refunds creation - for invoice in bordereau_id.invoice_ids: - # Remove reconcile payments if exists - if invoice.payment_move_line_ids: - invoice.payment_move_line_ids.remove_move_reconcile() - # Create refund - refund = invoice.refund( - date_invoice=self.date_regul, - date=self.date_regul, - description=self.comment, - journal_id=invoice.journal_id.id, - ) - # Update refund - refund.update({ - 'bordereau_id': self.bordereau_id, - 'is_contribution': True, - 'year': invoice.year, - 'liasse_fiscale_id': invoice.liasse_fiscale_id, - 'type_contribution_id': invoice.type_contribution_id, - 'payment_term_id': invoice.payment_term_id, - 'payment_mode_id': invoice.payment_mode_id, - }) - # Reconcile lines - movelines = invoice.move_id.line_ids - to_reconcile_ids = {} - to_reconcile_lines = self.env['account.move.line'] - for line in movelines: - if line.account_id.id == invoice.account_id.id: - to_reconcile_lines += line - to_reconcile_ids.setdefault(line.account_id.id, []).append( - line.id) - if line.reconciled: - line.remove_move_reconcile() - refund.action_invoice_open() - for tmpline in refund.move_id.line_ids: - if tmpline.account_id.id == invoice.account_id.id: - to_reconcile_lines += tmpline - to_reconcile_lines.filtered( - lambda l: l.reconciled == False).reconcile() - - # Create new bordereau - new_bordereau = bordereau_id.base_cotisation_cg.create_bordereau( - member=self.partner_id, - nb_quarter=bordereau_id.nb_quarter, - liasse=self.liasse_fiscale_new_id, - date=self.date_regul, - is_regul=True - ) - new_bordereau.update({ - 'name': bordereau_id.name[0:10] + '-' + str(bordereau_id.version + 1), - 'version': bordereau_id.version + 1, + # CREATE VERSION + bordereau_id.read(['amount_total_cotiz']) + self.env['scop.bordereau.version'].create({ + 'bordereau_id': bordereau_id.id, + 'date': self.date_regul, + 'comment': self.comment, + 'version': bordereau_id.version, + 'liasse_fiscale_id_old': bordereau_id.liasse_fiscale_id.id, + 'type_assiette': bordereau_id.type_assiette, + 'montant_assiette': bordereau_id.montant_assiette, + 'amount_total_cotiz': bordereau_id.amount_total_cotiz, }) - new_bordereau.base_cotisation_cg.create_cotiz_and_lines( - bordereau=new_bordereau, date=self.date_regul, is_regul=True) - # Link new to old bordereau + + # CREATE REGUL + contribs = bordereau_id.invoice_ids.read_group( + [('id', 'in', bordereau_id.invoice_ids.ids)], + ['type_contribution_id', 'amount_total_signed'], + ['type_contribution_id']) + + ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + if partner_id.ur_id.id == ur_hdf: + amount_ur = self.amount_ur_hdf + product_ur = self.env.user.company_id.contribution_hdf_id + account_ur = self.env.user.company_id.receivable_account_ur_hdf_id + else: # ur = ur_med + amount_ur = self.amount_ur_med + product_ur = self.env.user.company_id.contribution_med_id + account_ur = self.env.user.company_id.receivable_account_ur_med_id + + cotiz_type = { + self.env.ref('cgscop_partner.riga_14397').id: + [self.amount_cg, + self.env.user.company_id.contribution_cg_id, + self.env.user.company_id.contribution_journal_id, + partner_id.property_account_receivable_id.id], + self.env.ref('cgscop_partner.riga_14398').id: + [self.amount_fede_com, + self.env.user.company_id.contribution_fede_com_id, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + self.env.user.company_id.receivable_account_fede_com_id], + self.env.ref('cgscop_partner.cotiz_fede_cae').id: + [self.amount_fede_cae, + self.env.user.company_id.contribution_fede_cae_id, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + self.env.user.company_id.receivable_account_fede_cae_id], + self.env.ref('cgscop_partner.riga_14399').id: + [amount_ur, + product_ur, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + account_ur], + } + + quarters = [bordereau_id.base_cotisation_cg.trimester_1, + bordereau_id.base_cotisation_cg.trimester_2, + bordereau_id.base_cotisation_cg.trimester_3, + bordereau_id.base_cotisation_cg.trimester_4] + + for contrib in contribs: + type_cotiz = contrib.get('type_contribution_id')[0] + amount_cotiz_old = contrib.get('amount_total_signed') + amount_cotiz = cotiz_type.get(type_cotiz)[0] + refund_amount_total = self.env['scop.cotisation'].\ + round_to_closest_multiple(amount_cotiz_old - amount_cotiz, 4) + product = cotiz_type.get(type_cotiz)[1] + type_invoice = False + + if refund_amount_total < 0: + type_invoice = 'out_invoice' + refund_amount_total *= -1 + elif refund_amount_total > 0: + type_invoice = 'out_refund' + refund_amount = refund_amount_total / bordereau_id.nb_quarter + + if type_invoice: + for i in range(0, bordereau_id.nb_quarter): + + if self.date_regul < quarters[i]: + date_due = quarters[i] + else: + date_due = self.date_regul + + refund = self.env['account.invoice'].create({ + 'partner_id': partner_id.id, + 'journal_id': cotiz_type.get(type_cotiz)[2].id, + 'account_id': partner_id.property_account_receivable_id.id, + 'type': type_invoice, + 'date_invoice': self.date_regul, + 'date': self.date_regul, + 'state': 'draft', + 'number': False, + 'origin': bordereau_id.name, + 'name': self.comment, + 'bordereau_id': bordereau_id.id, + 'is_contribution': True, + 'year': bordereau_id.year, + 'liasse_fiscale_id': bordereau_id.liasse_fiscale_id.id, + 'type_contribution_id': type_cotiz, + 'payment_mode_id': bordereau_id.payment_mode_id.id, + 'date_due': date_due, + 'cotiz_quarter': i+1, + }) + self.env['account.invoice.line'].create({ + 'name': self.comment + " - " + str(i+1) + "/" + str(bordereau_id.nb_quarter), + 'invoice_id': refund.id, + 'product_id': product.id, + 'account_id': product.property_account_income_id.id, + 'price_unit': refund_amount + }) bordereau_id.update({ - 'refund_id': new_bordereau.id, - 'is_regul': True, - 'date_regul': self.date_regul, - 'comment_regul': self.comment, - 'state': 'cancel', + 'liasse_fiscale_id': self.liasse_fiscale_new_id.id + }) + bordereau_id.invoice_ids.update({ + 'liasse_fiscale_id': self.liasse_fiscale_new_id.id }) - # Return Action - action = { - 'type': 'ir.actions.act_window', - 'res_model': 'scop.bordereau', - 'view_mode': 'form', - 'res_id': new_bordereau.id, - } - return action diff --git a/wizard/scop_cotisation_cg_regul_wizard.xml b/wizard/scop_cotisation_cg_regul_wizard.xml index 76065d5cd599b343dbf820f1cb2a8c75ab2fab9b..b4a258d27a081f18307278a82d31e235820b3e26 100644 --- a/wizard/scop_cotisation_cg_regul_wizard.xml +++ b/wizard/scop_cotisation_cg_regul_wizard.xml @@ -25,13 +25,6 @@ </h3> </div> </div> - <div class="row" attrs="{'invisible': [('is_payment', '=', False)]}" > - <div class="col-12"> - <div class="alert alert-danger" role="alert"> - Attention, des paiements sont liés aux appels de cotisation de ce bordereau. La régularisation entrainera le "délettrage" de ces paiements qui seront à rapprocher avec le nouveau bordereau émis. - </div> - </div> - </div> <div class="row"> <div class="col-6"> <group string="Données initiales" class="text-muted"> @@ -47,14 +40,16 @@ <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"/> + <field name="liasse_fiscale_new_id" domain="[('partner_id', '=', partner_id)]" + options='{"no_create": True}' help="Nouvelle liasse fiscale à prendre en compte"/> + </group> + <group> + <field name="type_id_new" attrs="{'invisible': [('liasse_fiscale_new_id', '=', False)]}"/> + <field name="source_new" attrs="{'invisible': [('liasse_fiscale_new_id', '=', False)]}"/> </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"/> + <field name="type_assiette_new" attrs="{'invisible': [('amount_cg', '=', 0.0)]}" readonly="1"/> + <field name="montant_assiette_new" attrs="{'invisible': [('amount_cg', '=', 0.0)]}" readonly="1"/> </group> <group> <field name="amount_cg" attrs="{'invisible': [('amount_cg', '=', 0.0)]}" readonly="1"/> @@ -69,7 +64,7 @@ </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 name="update_contribution" type="object" string="Régulariser le bordereau" class="oe_highlight"/> <button string="Annuler" special="cancel" class="oe_link"/> </footer> </form> diff --git a/wizard/scop_cotisation_cg_wizard.py b/wizard/scop_cotisation_cg_wizard.py index a81e48473456e85387c2ce5c1f7a27ca4b6eba88..6d95f817006e42734621f7ecaea64a64b7538949 100644 --- a/wizard/scop_cotisation_cg_wizard.py +++ b/wizard/scop_cotisation_cg_wizard.py @@ -2,7 +2,6 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging -import threading from odoo import api, fields, models, exceptions @@ -87,20 +86,20 @@ class ScopCotisationWizard(models.TransientModel): message_id = self.env['message.wizard'].create( {'message': message}) - # Création de la task - cotiz_cg_task = self.env['scop.cotisation.task'].create({ - 'year': self.year, - 'cotiz_to_create': len(members_to_invoice), - 'message': "En cours de création", - 'status': 'in_process' - }) - cotiz_cg_task.env.cr.commit() - # Lancement du calcul en arrière plan - threaded_cotiz = threading.Thread( - target=self.cotisation_cg_id.process_cotiz_generate, - args=(members_to_invoice, cotiz_cg_task, - self.nb_quarter, self.date)) - threaded_cotiz.start() + # Job queue + batch_name = (fields.Datetime.to_string(fields.Datetime.now()) + + " Génération des bordereaux " + + str(self.cotisation_cg_id.year)) + batch = self.env['queue.job.batch'].get_new_batch(batch_name) + for member in members_to_invoice: + liasse_id = self.cotisation_cg_id.get_liasse(member) + self.cotisation_cg_id.with_context( + job_batch=batch + ).with_delay().create_bordereau( + member=member, liasse=liasse_id, + nb_quarter=self.nb_quarter, date=self.date) + batch.enqueue() + else: message = ("<p class='text-center'>Tous les bordereaux pour les " "coops qui ont adhéré cette année avant le %s ont "