Skip to content
Extraits de code Groupes Projets
Valider 260b0f88 rédigé par Jordan - Le Filament's avatar Jordan - Le Filament
Parcourir les fichiers

Merge branch '12.0-refactor-cotiz' into '12.0-dev'

12.0 refactor cotiz

See merge request !1
parents c148fe89 84eb7e94
Aucune branche associée trouvée
Aucune étiquette associée trouvée
1 requête de fusion!112.0 refactor cotiz
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
"views/res_config_settings.xml", "views/res_config_settings.xml",
"views/scop_cotisation_aura.xml", "views/scop_cotisation_aura.xml",
"views/account_invoice.xml", "views/account_invoice.xml",
"views/account_payment_term.xml",
"views/report_cotisation_aura.xml", "views/report_cotisation_aura.xml",
"views/report_cotisation_aura_refund.xml", "views/report_cotisation_aura_refund.xml",
"wizard/scop_cotisation_aura_wizard.xml", "wizard/scop_cotisation_aura_wizard.xml",
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import account_invoice from . import account_invoice
from . import account_payment_term
from . import res_company from . import res_company
from . import res_config_settings from . import res_config_settings
from . import scop_cotisation_aura from . import scop_cotisation_aura
# © 2020 Le Filament (<http://www.le-filament.com>) # © 2020 Le Filament (<http://www.le-filament.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models, fields, api from odoo import models, fields, api, _
from odoo.exceptions import UserError
class ScopAuraAccountInvoice(models.Model): class ScopAuraAccountInvoice(models.Model):
...@@ -25,6 +26,10 @@ class ScopAuraAccountInvoice(models.Model): ...@@ -25,6 +26,10 @@ class ScopAuraAccountInvoice(models.Model):
string="Montant calculé annuel", currency_field='company_currency_id', string="Montant calculé annuel", currency_field='company_currency_id',
compute='_compute_amount_aura_calculated', store=True, readonly=True) compute='_compute_amount_aura_calculated', store=True, readonly=True)
nb_quarter_aura = fields.Selection(
string='Nombre de trimestres de cotisation',
selection=[(1, 1), (2, 2), (3, 3), (4, 4)],
default=4, required=True)
amount_aura_prorata = fields.Monetary( amount_aura_prorata = fields.Monetary(
string="Montant calculé proratisé", string="Montant calculé proratisé",
currency_field='company_currency_id', currency_field='company_currency_id',
...@@ -50,14 +55,16 @@ class ScopAuraAccountInvoice(models.Model): ...@@ -50,14 +55,16 @@ class ScopAuraAccountInvoice(models.Model):
'scop.cotisation.aura'].get_cotiz_aura( 'scop.cotisation.aura'].get_cotiz_aura(
partner_id=i.partner_id, wage_cg=i.wage_cg_retenu) partner_id=i.partner_id, wage_cg=i.wage_cg_retenu)
@api.depends('amount_aura_calculated', 'nb_quarter') @api.depends('amount_aura_calculated', 'nb_quarter_aura')
@api.multi @api.multi
def _compute_amount_aura_prorata(self): def _compute_amount_aura_prorata(self):
for i in self: for i in self:
prorata = int(i.nb_quarter) / 4 if i.cotisation_aura_id \
and i.amount_aura_calculated and i.nb_quarter_aura:
prorata = i.nb_quarter_aura / 4
i.amount_aura_prorata = i.cotisation_aura_id.\ i.amount_aura_prorata = i.cotisation_aura_id.\
round_to_closest_multiple( round_to_closest_multiple(
i.amount_aura_calculated * prorata, int(i.nb_quarter)) i.amount_aura_calculated * prorata, i.nb_quarter_aura)
# ------------------------------------------------------ # ------------------------------------------------------
# Onchange # Onchange
...@@ -82,22 +89,109 @@ class ScopAuraAccountInvoice(models.Model): ...@@ -82,22 +89,109 @@ class ScopAuraAccountInvoice(models.Model):
# Override parent # Override parent
# ------------------------------------------------------ # ------------------------------------------------------
@api.multi @api.multi
def set_scop_contribution(self): def action_move_create(self):
contrib_id = super(ScopAuraAccountInvoice, self).set_scop_contribution() """
if self.cotisation_aura_id: Complete override parent
i = 4 - self.nb_quarter Pass invoice in payment_term.compute function to generate payment
schedule = { schedule
'quarter_1': self.cotisation_aura_id.trimester_1, :return: True
'quarter_2': self.cotisation_aura_id.trimester_2, """
'quarter_3': self.cotisation_aura_id.trimester_3, account_move = self.env['account.move']
'quarter_4': self.cotisation_aura_id.trimester_4,
for inv in self:
if not inv.journal_id.sequence_id:
raise UserError(
_('Please define sequence on the journal related to this invoice.'))
if not inv.invoice_line_ids.filtered(lambda line: line.account_id):
raise UserError(_('Please add at least one invoice line.'))
if inv.move_id:
continue
if not inv.date_invoice:
inv.write({'date_invoice': fields.Date.context_today(self)})
if not inv.date_due:
inv.write({'date_due': inv.date_invoice})
company_currency = inv.company_id.currency_id
# create move lines
# (one per invoice line + eventual taxes and analytic lines)
iml = inv.invoice_line_move_line_get()
iml += inv.tax_line_move_line_get()
diff_currency = inv.currency_id != company_currency
# create one move line for the total and possibly adjust
# the other lines amount
total, total_currency, iml = inv.compute_invoice_totals(
company_currency, iml)
name = inv.name or ''
if inv.payment_term_id:
totlines = inv.payment_term_id.with_context(currency_id=company_currency.id).compute(total, inv.date_invoice, inv)[0]
res_amount_currency = total_currency
for i, t in enumerate(totlines):
if inv.currency_id != company_currency:
amount_currency = company_currency._convert(
t[1],
inv.currency_id,
inv.company_id,
inv._get_currency_rate_date() or fields.Date.today())
else:
amount_currency = False
# last line: add the diff
res_amount_currency -= amount_currency or 0
if i + 1 == len(totlines):
amount_currency += res_amount_currency
iml.append({
'type': 'dest',
'name': name,
'price': t[1],
'account_id': inv.account_id.id,
'date_maturity': t[0],
'amount_currency': diff_currency and amount_currency,
'currency_id': diff_currency and inv.currency_id.id,
'invoice_id': inv.id
})
else:
iml.append({
'type': 'dest',
'name': name,
'price': total,
'account_id': inv.account_id.id,
'date_maturity': inv.date_due,
'amount_currency': diff_currency and total_currency,
'currency_id': diff_currency and inv.currency_id.id,
'invoice_id': inv.id
})
part = self.env['res.partner']._find_accounting_partner(
inv.partner_id)
line = [(0, 0, self.line_get_convert(l, part.id)) for l in iml]
line = inv.group_lines(iml, line)
line = inv.finalize_invoice_move_lines(line)
date = inv.date or inv.date_invoice
move_vals = {
'ref': inv.reference,
'line_ids': line,
'journal_id': inv.journal_id.id,
'date': date,
'narration': inv.comment,
}
move = account_move.create(move_vals)
# Pass invoice in method post: used if you want to get the same
# account move reference when creating the same invoice
# after a cancelled one:
move.post(invoice=inv)
# make the invoice point to that move
vals = {
'move_id': move.id,
'date': date,
'move_name': move.name,
} }
while i != 0: inv.write(vals)
key = 'quarter_' + str(i) return True
schedule.pop(key, None)
i -= 1
contrib_id.update(schedule)
return contrib_id
# ------------------------------------------------------ # ------------------------------------------------------
# Global functions # Global functions
......
# Copyright 2020 Le Filament
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class AccountPaymentTerm(models.Model):
_inherit = 'account.payment.term'
is_contribution = fields.Boolean('Conditions de paiement des cotisations')
def compute(self, value, date_ref=False, invoice=None):
"""
Override la fonction compute du modèle account.payment.term
La fonction initiale checke les conditions de paiement et crée
les lignes de paiement associées
L'héritage permet de créer un échéancier en fonction de celui
défini sur la base de cotisations
"""
date_ref = date_ref or fields.Date.today()
amount = value
sign = value < 0 and -1 or 1
if self.env.context.get('currency_id'):
currency = self.env['res.currency'].browse(
self.env.context['currency_id'])
else:
currency = self.env.user.company_id.currency_id
# si base de cotisation
if self.is_contribution and invoice and invoice.cotisation_aura_id:
result = []
base_contrib_field = 'cotisation_aura_id'
trimesters = {
4: invoice[base_contrib_field].trimester_1,
3: invoice[base_contrib_field].trimester_2,
2: invoice[base_contrib_field].trimester_3,
1: invoice[base_contrib_field].trimester_4,
}
for i in range(invoice.nb_quarter_aura, 0, -1):
# Gestion de l'arrondi de la division
if i == 1:
amt = currency.round(amount)
else:
amt = currency.round(value / invoice.nb_quarter_aura)
result.append((fields.Date.to_string(trimesters.get(i)), amt))
amount -= amt
return [result]
else:
return super(AccountPaymentTerm, self).compute(value, date_ref)
...@@ -15,3 +15,9 @@ class ScopCotisationAuraCompany(models.Model): ...@@ -15,3 +15,9 @@ class ScopCotisationAuraCompany(models.Model):
string='Article de cotisation AURA', string='Article de cotisation AURA',
domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]"
) )
contribution_default_payment_term_id = fields.Many2one(
comodel_name='account.payment.term',
string="Conditions de paiement par défaut pour les cotisations",
domain=[('is_contribution', '=', True)],
)
...@@ -19,6 +19,14 @@ class CotisationsAuraConfigSettings(models.TransientModel): ...@@ -19,6 +19,14 @@ class CotisationsAuraConfigSettings(models.TransientModel):
domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]"
) )
contribution_default_payment_term_id = fields.Many2one(
comodel_name='account.payment.term',
related="company_id.contribution_default_payment_term_id",
readonly=False,
string="Conditions de paiement par défaut pour les cotisations",
domain=[('is_contribution', '=', True)],
)
@api.onchange('is_contribution') @api.onchange('is_contribution')
def _onchange_is_contribution_aura(self): def _onchange_is_contribution_aura(self):
if not self.is_contribution: if not self.is_contribution:
......
...@@ -39,6 +39,13 @@ class ScopCotisationAura(models.Model): ...@@ -39,6 +39,13 @@ class ScopCotisationAura(models.Model):
compute='_compute_state', compute='_compute_state',
store=True) store=True)
payment_term_id = fields.Many2one(
comodel_name='account.payment.term',
string="Conditions de paiement",
domain=[('is_contribution', '=', True)],
required=True,
)
amount_total = fields.Monetary( amount_total = fields.Monetary(
string="Montant total", compute='_compute_amount_total', string="Montant total", compute='_compute_amount_total',
store=True, store=True,
...@@ -308,6 +315,76 @@ class ScopCotisationAura(models.Model): ...@@ -308,6 +315,76 @@ class ScopCotisationAura(models.Model):
# ------------------------------------------------------ # ------------------------------------------------------
# Global functions # Global functions
# ------------------------------------------------------ # ------------------------------------------------------
def create_contribution_aura(
self, partner, amount, nb_quarter_aura, liasse=None, date=False):
"""
Create invoice from Contribution Base
:param partner: partner_id
:param liasse: liasse_fiscale_id (reference)
:param amount: contribution amount (float)
:param nb_quarter_aura: nb_quarter_aura (Selection)
:param date: date invoice
:return: invoice
"""
product_aura = self.company_id.contribution_aura_id
type_contrib_ur = self.env.ref('cgscop_partner.riga_14399').id
Invoice = self.env['account.invoice']
InvoiceLine = self.env['account.invoice.line']
domain = [
('partner_id', '=', partner.id),
('year', '=', self.year),
('type_contribution_id', '=', type_contrib_ur),
]
exisiting_invoice = Invoice.search(domain)
if not exisiting_invoice:
date_invoice = date if date else self.date_cotisation
journal_id = self.company_id.contribution_journal_id
account_id = partner.property_account_receivable_id
member_invoice = Invoice.create({
'partner_id': partner.id,
'liasse_fiscale_id': liasse.id,
'type': 'out_invoice',
'year': self.year,
'is_contribution': True,
'type_contribution_id': type_contrib_ur,
'journal_id': journal_id.id,
'state': 'draft',
'account_id': account_id.id,
'payment_term_id': self.payment_term_id.id,
'payment_mode_id': partner.customer_payment_mode_id.id,
'date_invoice': date_invoice,
'cotisation_aura_id': self.id,
'wage_cg_connu': liasse.wage_cg,
'wage_cg_retenu': liasse.wage_cg,
'nb_quarter_aura': nb_quarter_aura
})
else:
member_invoice = exisiting_invoice
# Création de la ligne CG Scop
exisiting_invoice_line_ids = InvoiceLine.search([
('invoice_id', '=', member_invoice.id),
('product_id', '=', product_aura.id)
])
if not exisiting_invoice_line_ids:
InvoiceLine.create({
'invoice_id': member_invoice.id,
'product_id': product_aura.id,
'account_id': product_aura.property_account_income_id.id,
'invoice_line_tax_ids': [(6, 0, product_aura.taxes_id.ids)],
'name': product_aura.name,
'price_unit': amount
})
else:
exisiting_invoice_line_ids[0].write({
'price_unit': amount
})
return member_invoice
@api.multi @api.multi
def get_members(self): def get_members(self):
self.ensure_one() self.ensure_one()
...@@ -382,14 +459,16 @@ class ScopCotisationAura(models.Model): ...@@ -382,14 +459,16 @@ class ScopCotisationAura(models.Model):
return plancher return plancher
else: else:
return cotiz return cotiz
else:
return plancher
else: else:
return cotiz return cotiz
# ------------------------------------------------------ # ------------------------------------------------------
# Threading task # Threading task
# ------------------------------------------------------ # ------------------------------------------------------
def process_cotiz_generate(self, partner_ids, def process_cotiz_generate(self, partner_ids, cotiz_aura_task,
cotiz_aura_task, nb_quarter=4, date=False): nb_quarter_aura=4, date=False):
""" """
Process de génération des cotiz Aura en background Process de génération des cotiz Aura en background
""" """
...@@ -407,10 +486,7 @@ class ScopCotisationAura(models.Model): ...@@ -407,10 +486,7 @@ class ScopCotisationAura(models.Model):
task = cotiz_aura_task.with_env(new_env) task = cotiz_aura_task.with_env(new_env)
new_self = self.with_env(new_env) new_self = self.with_env(new_env)
# Definition of specific params # Definition of specific param
product_aura_id = new_self.company_id.contribution_aura_id
type_cotisation_ur = new_self.env.ref(
'cgscop_partner.riga_14399').id
# Creation of cotiz invoice for each member # Creation of cotiz invoice for each member
for member_id in partner_ids: for member_id in partner_ids:
...@@ -419,16 +495,10 @@ class ScopCotisationAura(models.Model): ...@@ -419,16 +495,10 @@ class ScopCotisationAura(models.Model):
liasse = new_self.get_liasse(member) liasse = new_self.get_liasse(member)
amount_aura = new_self.round_to_closest_multiple( amount_aura = new_self.round_to_closest_multiple(
new_self.get_cotiz_aura(member, liasse), 4) new_self.get_cotiz_aura(member, liasse), 4)
invoice_cotiz_aura = new_self.create_contribution( invoice_cotiz_aura = \
product_aura_id, member, type_cotisation_ur, new_self.create_contribution_aura(
liasse, amount_aura, date) member, amount_aura, nb_quarter_aura, liasse, date)
invoice_cotiz_aura.write({ if nb_quarter_aura != 4:
'cotisation_aura_id': new_self.id,
'wage_cg_connu': liasse.wage_cg,
'wage_cg_retenu': liasse.wage_cg,
'nb_quarter': nb_quarter
})
if nb_quarter != 4:
invoice_cotiz_aura.recalcul_cotiz_aura() invoice_cotiz_aura.recalcul_cotiz_aura()
cotiz_created += 1 cotiz_created += 1
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
<group> <group>
<field name="wage_cg_retenu" attrs="{'readonly': [('state', '!=', 'draft')], 'invisible': [('cotisation_aura_id', '=', False)]}"/> <field name="wage_cg_retenu" attrs="{'readonly': [('state', '!=', 'draft')], 'invisible': [('cotisation_aura_id', '=', False)]}"/>
<field name="amount_aura_calculated" attrs="{'invisible': [('cotisation_aura_id', '=', False)]}"/> <field name="amount_aura_calculated" attrs="{'invisible': [('cotisation_aura_id', '=', False)]}"/>
<field name="nb_quarter" widget="radio" options="{'horizontal': true}" attrs="{'readonly': [('state', '!=', 'draft')], 'invisible': [('cotisation_aura_id', '=', False)]}"/> <field name="nb_quarter_aura" widget="radio" options="{'horizontal': true}" attrs="{'readonly': [('state', '!=', 'draft')], 'invisible': [('cotisation_aura_id', '=', False)]}"/>
<field name="amount_aura_prorata" attrs="{'invisible': [('cotisation_aura_id', '=', False)]}"/> <field name="amount_aura_prorata" attrs="{'invisible': [('cotisation_aura_id', '=', False)]}"/>
<button name="recalcul_cotiz_aura" type="object" string="Affecter le nouveau montant" attrs="{'invisible': ['|', ('state', '!=', 'draft'), ('cotisation_aura_id', '=', False)]}"/> <button name="recalcul_cotiz_aura" type="object" string="Affecter le nouveau montant" attrs="{'invisible': ['|', ('state', '!=', 'draft'), ('cotisation_aura_id', '=', False)]}"/>
</group> </group>
......
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Copyright 2020 Le Filament
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<data>
<record id="view_payment_term_form_inherited" model="ir.ui.view">
<field name="name">account.payment.term.form</field>
<field name="model">account.payment.term</field>
<field name="inherit_id" ref="account.view_payment_term_form"/>
<field name="arch" type="xml">
<xpath expr="//group" position="after">
<group name="schedule">
<field name="is_contribution" widget="boolean_toggle"/>
</group>
</xpath>
</field>
</record>
</data>
</odoo>
\ No newline at end of file
...@@ -41,14 +41,14 @@ ...@@ -41,14 +41,14 @@
Ce montant est calculé de la façon suivante : Ce montant est calculé de la façon suivante :
</p> </p>
<p> <p>
Masse salariale <span t-esc="str(o.year - 2)"/> : <span t-field="o.wage_cg_retenu" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/> x 0,50% = <span t-field="o.amount_total"/> * Masse salariale <span t-esc="str(o.year - 2)"/> : <span t-field="o.wage_cg_retenu" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/> x 0,50% x prorata du nombre de trimestres de cotisations = <span t-field="o.amount_total"/> *
</p> </p>
<p style="font-style: italic; font-size: 12px;"> <p style="font-style: italic; font-size: 12px;">
* Pour mémoire, à compter de 2016, les masses salariales inférieures à 60 000,00 € génèrent une cotisation forfaitaire annuelle de 300,00 €, soit 75,00 € par trimestre. * Pour mémoire, à compter de 2016, les masses salariales inférieures à 60 000,00 € génèrent une cotisation forfaitaire annuelle de 300,00 €, soit 75,00 € par trimestre.
</p> </p>
<p> <p>
<t t-if="o.is_sdd"> <t t-if="o.is_sdd">
Ce montant vous sera prélevé en <span t-field="o.nb_quarter"/> échéances trimestrielles comme décrit dans le tableau suivant : Ce montant vous sera prélevé en <span t-field="o.nb_quarter_aura"/> échéances trimestrielles comme décrit dans le tableau suivant :
</t> </t>
<t t-else=""> <t t-else="">
En vous remerciant de vos règlements aux échéances suivantes : En vous remerciant de vos règlements aux échéances suivantes :
......
...@@ -24,6 +24,13 @@ ...@@ -24,6 +24,13 @@
<div><label for="contribution_aura_id"/></div> <div><label for="contribution_aura_id"/></div>
<field name="contribution_aura_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_aura', '=', True)]}"/> <field name="contribution_aura_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_aura', '=', True)]}"/>
</div> </div>
<div class="o_setting_right_pane">
<label for="contribution_default_payment_term_id"/>
<div class="text-muted">
Conditions de paiement par défault
</div>
<field name="contribution_default_payment_term_id" options="{'no_open': True, 'no_create': True}"/>
</div>
</div> </div>
</xpath> </xpath>
</field> </field>
......
...@@ -19,13 +19,10 @@ class ScopCotisationWizard(models.TransientModel): ...@@ -19,13 +19,10 @@ class ScopCotisationWizard(models.TransientModel):
cotisation_aura_id = fields.Many2one( cotisation_aura_id = fields.Many2one(
comodel_name='scop.cotisation.aura', comodel_name='scop.cotisation.aura',
string='Base de cotisation') string='Base de cotisation')
nb_quarter = fields.Selection( nb_quarter_aura = fields.Selection(
string='Nombre de trimestres de cotisation', string='Nombre de trimestres de cotisation',
selection=[('1', '1'), selection=[(1, 1), (2, 2), (3, 3), (4, 4)],
('2', '2'), default=4,
('3', '3'),
('4', '4')],
default='4',
required=True) required=True)
type = fields.Selection([ type = fields.Selection([
('all', 'Toutes les coop nouvelles adhérentes'), ('all', 'Toutes les coop nouvelles adhérentes'),
...@@ -100,7 +97,7 @@ class ScopCotisationWizard(models.TransientModel): ...@@ -100,7 +97,7 @@ class ScopCotisationWizard(models.TransientModel):
threaded_cotiz = threading.Thread( threaded_cotiz = threading.Thread(
target=self.cotisation_aura_id.process_cotiz_generate, target=self.cotisation_aura_id.process_cotiz_generate,
args=(members_to_invoice, cotiz_aura_task, args=(members_to_invoice, cotiz_aura_task,
self.nb_quarter, self.date)) self.nb_quarter_aura, self.date))
threaded_cotiz.start() threaded_cotiz.start()
else: else:
message = ("<p class='text-center'>Tous les appels de cotisations " message = ("<p class='text-center'>Tous les appels de cotisations "
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<group> <group>
<field name="year" required="1" invisible="1"/> <field name="year" required="1" invisible="1"/>
<field name="date" required="1"/> <field name="date" required="1"/>
<field name="nb_quarter" required="1"/> <field name="nb_quarter_aura" required="1"/>
<field name="type" widget="radio"/> <field name="type" widget="radio"/>
<field name="partner_ids" attrs="{'invisible': [('type', '=', 'all')]}" widget="many2many_tags" options="{'no_create': True, 'no_open': True}"/> <field name="partner_ids" attrs="{'invisible': [('type', '=', 'all')]}" widget="many2many_tags" options="{'no_create': True, 'no_open': True}"/>
</group> </group>
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Veuillez vous inscrire ou vous pour commenter