Sélectionner une révision Git
scop_cotisation_cg.py 34,30 Kio
# © 2021 Le Filament (<http://www.le-filament.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import base64
import json
import logging
from datetime import datetime
from io import BytesIO
import xlsxwriter
from odoo import _, api, exceptions, fields, models
_logger = logging.getLogger(__name__)
class ScopCotisation(models.Model):
_inherit = "scop.cotisation"
_name = "scop.cotisation.cg"
_description = "Base des cotisations CG"
name = fields.Char(string="Nom", compute="_compute_name", store=True)
simul_ids = fields.One2many(
comodel_name="scop.cotisation.cg.simulation",
inverse_name="cotisation_id",
string="Simulations",
)
invoice_ids = fields.One2many(
comodel_name="account.move",
inverse_name="cotisation_cg_id",
string="Factures",
domain=[
("move_type", "in", ("out_invoice", "out_refund")),
("is_contribution", "=", True),
],
)
bordereau_ids = fields.One2many(
comodel_name="scop.bordereau",
inverse_name="base_cotisation_cg",
string="Bordereau",
)
state = fields.Selection(
[("new", "Brouillon"), ("ongoing", "En cours"), ("end", "Clôturé")],
string="Statut",
default="new",
compute="_compute_state",
store=True,
)
invoice_count = fields.Integer(
string="Appels de cotisations émis", compute="_compute_real"
)
invoice_valid_count = fields.Integer(
string="Appels de cotisations validés", compute="_compute_real"
)
amount_called = fields.Monetary(
"Montant appelé",
compute="_compute_amount_called",
currency_field="company_currency_id",
)
amount_residual = fields.Monetary(
"Montant restant à réglé",
compute="_compute_amount_residual",
currency_field="company_currency_id",
)
amount_paid = fields.Monetary(
"Montant payé",
compute="_compute_amount_paid",
currency_field="company_currency_id",
)
percent_cotiz_paid = fields.Float("% Payées", compute="_compute_percent_cotiz_paid")
graph_values = fields.Text(compute="_compute_graph_values")
batch_id = fields.Many2one(
comodel_name="queue.job.batch", name="Files d'attente en cours"
)
batch_ids_str = fields.Char()
batch_count = fields.Integer(compute="_compute_batch_count")
# ------------------------------------------------------
# Contraintes SQL
# ------------------------------------------------------
# Unicité d'année' (company - année)
_sql_constraints = [
(
"contribution_unique",
"unique(year, company_id)",
"La gestion des cotisations pour cette année existe déjà",
),
]
# ------------------------------------------------------
# Compute fields
# ------------------------------------------------------
@api.depends("year", "state")
def _compute_name(self):
for cotiz in self:
cotiz.name = "Cotisations %s - %s" % (
cotiz.year,
dict(self._fields["state"].selection).get(cotiz.state),
)
def _compute_amount_called(self):
for cotiz in self:
cotiz.amount_called = sum(
cotiz.invoice_ids.filtered(lambda i: i.state == "posted").mapped(
"amount_total_signed"
)
)
def _compute_amount_residual(self):
for cotiz in self:
cotiz.amount_residual = sum(
cotiz.invoice_ids.mapped("amount_residual_signed")
)
def _compute_amount_paid(self):
"""
montant déjà payé = différence entre le reste à payer du total validé,
le reste à payer étant égal à 0 pour les factures en brouillon
"""
for cotiz in self:
cotiz.amount_paid = cotiz.amount_called - cotiz.amount_residual
def _compute_percent_cotiz_paid(self):
for cotiz in self:
if cotiz.amount_called != 0:
percent = cotiz.amount_paid / cotiz.amount_called * 100
if 0 < percent < 1:
cotiz.percent_cotiz_paid = 1
elif 99 < percent < 100:
cotiz.percent_cotiz_paid = 99
else:
cotiz.percent_cotiz_paid = percent
else:
cotiz.percent_cotiz_paid = 0
@api.depends(
"invoice_ids",
"invoice_ids.state",
"invoice_ids.amount_total_signed",
)
def _compute_real(self):
for cotiz in self:
cotiz.invoice_count = len(cotiz.invoice_ids)
cotiz.invoice_valid_count = len(
cotiz.invoice_ids.filtered(lambda i: i.state == "posted")
)
def _compute_graph_values(self):
for cotiz in self:
draft_count = len(cotiz.invoice_ids.filtered(lambda i: i.state == "draft"))
opened_count = len(
cotiz.invoice_ids.filtered(
lambda i: i.state == "posted"
and i.payment_state not in ("paid", "reversed")
)
)
paid_count = len(
cotiz.invoice_ids.filtered(
lambda i: i.state == "posted" and i.payment_state == "paid"
)
)
cotiz.graph_values = json.dumps(
[
{
"values": [
{"label": "Brouillons", "value": draft_count},
{"label": "Ouverts", "value": opened_count},
{"label": "Payés", "value": paid_count},
],
"area": True,
"title": "",
"key": "Cotisations",
}
]
)
@api.depends("invoice_ids", "invoice_ids.state")
def _compute_state(self):
for cotiz in self:
if len(cotiz.invoice_ids) == 0:
cotiz.state = "new"
elif (
len(cotiz.invoice_ids)
== len(cotiz.invoice_ids.filtered(lambda i: i.state == "posted"))
and cotiz.state != "end"
):
cotiz.state = "end"
elif cotiz.state != "ongoing":
cotiz.state = "ongoing"
def _compute_batch_count(self):
for base in self:
if not base.batch_ids_str:
base.batch_count = 0
else:
base.batch_count = len(base.batch_ids_str.split(","))
# ------------------------------------------------------
# Button functions
# ------------------------------------------------------
def cotiz_generate(self):
"""
Open wizard to generate cotisations for renew members
"""
if not self.env.company.is_contribution_cg:
raise exceptions.UserError(_("La cotisation CG Scop n'est pas configurée."))
if (
not self.trimester_1
or not self.trimester_2
or not self.trimester_3
or not self.trimester_4
):
raise exceptions.UserError(
_("Les trimestres doivent être configurés pour lancer les cotisations.")
)
if not self.date_cotisation:
raise exceptions.UserError(
_(
"La date de calcul des cotisations doit être renseignée pour "
"lancer les cotisations."
)
)
# List of members already invoiced
member_invoiced = self.invoice_ids.mapped("partner_id")
# List of members
members = self.get_members()
# List of not invoiced members
members_to_invoice = members - member_invoiced
if len(members_to_invoice) > 0:
message = (
"<h3>Appels de cotisation "
+ self.year
+ "</h3> <hr/>"
+ "Nombre d'adhérents renouvelés : "
+ str(self.member_count)
+ "<br/>Bordereaux déjà générées non vides : "
+ str(self.invoiced_member_count)
+ "<br/>Bordereaux à générer : "
+ str(len(members_to_invoice))
+ "<p>Les appels de cotisation vont être générés.</p> "
)
return {
"type": "ir.actions.act_window.message",
"title": _("Génération des appels de cotisation annuels"),
"is_html_message": True,
"message": _(message),
"close_button_title": False,
"buttons": [
{
"type": "method",
"name": "Lancer le calcul",
"model": self._name,
"method": "action_cotiz_generate",
"args": [self.id, members_to_invoice.ids],
"classes": "btn-primary",
},
{
"type": "ir.actions.act_window_close",
"name": "Fermer",
},
],
}
else:
message = (
"<p class='text-center'>Tous les appels de "
+ "cotisations annuels ont déjà été créés !</p>"
)
return {
"type": "ir.actions.act_window.message",
"title": _("Génération des appels de cotisation annuels"),
"is_html_message": True,
"message": _(message),
"close_button_title": False,
"buttons": [
{
"type": "ir.actions.act_window_close",
"name": "Fermer",
},
],
}
def action_cotiz_generate(self, member_ids):
"""
Create contribution for members
:params member_ids: list of member ids
"""
# Vérifie que des tâches ne sont pas en cours
self.ensure_one()
if self.self.batch_ids_str:
jobs = [int(x) for x in self.batch_ids_str.split(",")]
else:
jobs = []
queue_ids = self.env["queue.job"].search(
[
("job_batch_id", "in", jobs),
("state", "not in", ["done", "cancelled", "failed"]),
]
)
if queue_ids:
raise exceptions.UserError(
_("Des tâches de création sont encore en cours.")
)
member_to_invoice = self.env["res.partner"].browse(member_ids)
# Job queue
batch_name = (
fields.Datetime.to_string(fields.Datetime.now())
+ " Génération des bordereaux "
+ self.year
)
batch = self.env["queue.job.batch"].get_new_batch(batch_name)
# Stocke le dernier batch en cours
self.batch_id = batch
# Stocke la liste des batchs au format texte
if not self.batch_ids_str:
self.batch_ids_str = str(batch.id)
else:
self.batch_ids_str += ", %s" % (str(batch.id),)
# Ajoute les bordereaux au batch
for member in member_to_invoice:
liasse_id = self.get_liasse(member)
description = "%s - Adh %s - %s" % (
self.year,
member.member_number,
member.name,
)
self.with_context(job_batch=batch).with_delay(
max_retries=3, description=description
).create_bordereau(
member=member, liasse=liasse_id, nb_quarter="4", date=False
)
batch.enqueue()
def cotiz_generate_wizard(self):
"""
Open wizard to generate cotisations for new members
"""
if (
not self.trimester_1
or not self.trimester_2
or not self.trimester_3
or not self.trimester_4
):
raise exceptions.UserError(
_("Les trimestres doivent être configurés pour lancer les cotisations.")
)
if not self.date_cotisation:
raise exceptions.UserError(
_(
"La date de calcul des cotisations doit être renseignée pour lancer "
"les cotisations."
)
)
wizard = self.env["scop.cotisation.cg.wizard"].create(
{
"year": self.year,
"cotisation_cg_id": self.id,
}
)
return {
"name": "Génération des appels de cotisation nouveaux adhérents",
"type": "ir.actions.act_window",
"view_type": "form",
"view_mode": "form",
"res_model": "scop.cotisation.cg.wizard",
"res_id": wizard.id,
"context": {
"id_cotisation_cg": self.id,
},
"target": "new",
}
def cotiz_view(self):
"""
Button to open view for Appels de cotisations
"""
tree_id = self.env.ref("cgscop_cotisation.account_move_tree_scop_inherited").id
form_id = self.env.ref("cgscop_cotisation_cg.invoice_form_scop_cg_inherited").id
search_id = (
self.env.ref("cgscop_cotisation_cg.invoice_search_scop_cg_inherited").id,
"view_account_invoice_cotiz_cg_search",
)
return {
"name": "Appels de cotisation " + self.year,
"type": "ir.actions.act_window",
"res_model": "account.move",
"view_mode": "tree,form",
"view_type": "form",
"search_view_id": search_id,
"views": [
[tree_id, "tree"],
[form_id, "form"],
[False, "pivot"],
[False, "graph"],
],
"domain": [("cotisation_cg_id", "=", self.id)],
"context": {
"create": False,
"default_year": self.year,
"default_is_contribution": True,
"default_cotisation_cg_id": self.id,
},
"target": "current",
}
def bordereaux_view(self):
"""
Button to open view for Bordereaux
"""
return {
"name": "Bordereaux de cotisation " + self.year,
"type": "ir.actions.act_window",
"res_model": "scop.bordereau",
"view_mode": "tree,form",
"view_type": "form",
"domain": [("base_cotisation_cg", "=", self.id)],
"target": "current",
}
def create_bordereau(self, member, nb_quarter, liasse=None, date=False):
"""
Création du bordereau de cotisations
:param member:
:param nb_quarter:
:param liasse:
:param date:
:return bordereau_id:
"""
# 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
else:
staff_shareholder_count = 0
staff_count = 0
staff_average = 0
date_invoice = date if date else self.date_cotisation
existing_bdx = self.env["scop.bordereau"].search(
[
("partner_id", "=", member.id),
("base_cotisation_cg", "=", self.id),
]
)
if not existing_bdx:
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()
else:
raise exceptions.UserError(
_("Un bordereau existe déjà pour cette coopérative et cette campagne")
)
return bordereau.id
def bordereau_validate(self):
"""
Open wizard to validate all bordereaux from "base de cotisation"
"""
bordereau_to_validate = self.bordereau_ids.filtered(lambda b: b.state == "new")
bordereau_to_validate.validate_bordereau_multi()
if len(bordereau_to_validate) > 0:
message = "%s bordereaux sont en cours de validation" % len(
bordereau_to_validate
)
else:
message = "Tous les bordereaux ont déjà été validés"
return {
"type": "ir.actions.act_window.message",
"title": _("Validation des bordereaux"),
"is_html_message": True,
"message": _(message),
"close_button_title": False,
"buttons": [
{
"type": "ir.actions.act_window_close",
"name": "Fermer",
},
],
}
def add_simul(self):
"""
Ajoute une simulation à la base de cotisation pour l'année donnée.
Cette simulation est sous la forme d'un fichier Excel avec le détail
des cotisations par coopératives.
@return: object scop.cotisation.cg.simulation
"""
# Get context
type_simul = self.env.context.get("type_simul")
# Get contribution type
type_cotisation_cg = self.env.ref("cgscop_partner.riga_14397")
# Get product type
AccountMoveLine = self.env["account.move.line"]
product_cg_id = self.company_id.contribution_cg_id
product_com_id = self.company_id.contribution_fede_com_id
product_cae_id = self.company_id.contribution_fede_cae_id
product_indus_id = self.company_id.contribution_fede_indus_id
product_hdf_id = self.company_id.contribution_hdf_id
product_med_id = self.company_id.contribution_med_id
# List of members
members = self.get_members()
header = [
"N° Adhérent",
"Union Régionale",
"Forme Coopérative",
"Organisme",
"Type de cotisation",
"Cotisation " + self.year,
"Cotisation " + str(int(self.year) - 1),
"Assiette",
"Montant Assiette",
"Année Liasse",
"Durée Exercice",
"CA (L2052 FL)",
"CA + Subvention",
"VA",
"VABDF sens CG Scop",
"Masse Salariale (L2052 FY)",
"Salaires sens CG Scop",
"Résultat",
"Dernier effectif connu",
"Dernier effectif associé connu",
"Moyen de paiement",
]
# Init variables
datas = []
total_cg = total_hdf = total_com = total_med = total_cae = total_indus = 0
count_member = 0
for m in self.web_progress_iter(members, msg="cotisations calculées"):
liasse = self.get_liasse(m)
line_ids = AccountMoveLine.search(
[
("partner_id", "=", m.id),
("move_id.state", "=", "posted"),
("move_id.year", "=", int(self.year) - 1),
("product_id", "!=", False),
]
)
# Calcul Cotisation CG Scop
net_results = 0
if liasse.L2053_HN > 0:
net_results = liasse.L2053_HN
elif liasse.L2051_DI > 0:
net_results = liasse.L2051_DI
contrib_cg = (
self.round_to_closest_multiple(liasse.contribution_cg, 4)
if liasse
else liasse.get_values_for_cotiz_cg(m).get("plancher1")
)
contribution_amount = contrib_cg
contribution_name = type_cotisation_cg.name
# Calcul cotisation N-1
contribution_last_year = sum(
line_ids.filtered(lambda l: l.product_id == product_cg_id).mapped(
lambda c: c.price_total if c.balance > 0 else -c.price_total
)
)
# Construction Tableau
liasse.read(
["L2052_FL", "av_cg", "av_lf", "revenue_cg", "L2052_FY", "wage_cg"]
)
datas_contrib = [
m.member_number,
m.ur_id.name,
m.cooperative_form_id.name,
m.name,
contribution_name,
contribution_amount,
contribution_last_year,
liasse.contribution_base_type.upper() if liasse else "CA",
liasse.contribution_base_amount,
liasse.year,
liasse.dureeExercice,
liasse.L2052_FL,
liasse.revenue_cg,
liasse.av_lf,
liasse.av_cg,
liasse.L2052_FY,
liasse.wage_cg,
net_results,
m.staff_last,
m.staff_shareholder_last,
m.customer_payment_mode_id.name
if m.customer_payment_mode_id
else "Virement/Chèque",
]
# Ajout ligne CG Scop
datas.append(datas_contrib)
if type_simul == "all":
# Ajout ligne UR HDF
ur_hdf = self.env.ref("cgscop_partner.riga_14232")
if m.ur_id == ur_hdf:
datas_contrib_hdf = datas_contrib.copy()
# Calcul cotisation
contrib_hdf = (
self.round_to_closest_multiple(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(
lambda c: c.price_total if c.balance > 0 else -c.price_total
)
)
datas_contrib_hdf[4] = "Cotisation UR HDF"
datas_contrib_hdf[5] = contrib_hdf
datas_contrib_hdf[6] = contribution_last_year
datas.append(datas_contrib_hdf)
total_hdf += contrib_hdf
# Ajout ligne UR Med
ur_med = self.env.ref("cgscop_partner.riga_14243")
if m.ur_id == ur_med:
datas_contrib_med = datas_contrib.copy()
# Calcul cotisation
contrib_med = (
self.round_to_closest_multiple(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(
lambda c: c.price_total if c.balance > 0 else -c.price_total
)
)
datas_contrib_med[4] = "Cotisation UR Méditerranée"
datas_contrib_med[5] = contrib_med
datas_contrib_med[6] = contribution_last_year
datas.append(datas_contrib_med)
total_med += contrib_med
# Ajout ligne Fédération Com
if m.is_federation_com:
datas_contrib_com = datas_contrib.copy()
# Calcul cotisation
if liasse:
contrib_fede_com = self.round_to_closest_multiple(
liasse.contribution_com, 4
)
else:
staff_id = self.get_last_staff_id(liasse.partner_id)
if staff_id.staff_average > 0:
contrib_fede_com = staff_id.staff_average * 108
else:
contrib_fede_com = staff_id.staff_count * 108
if contrib_fede_com < 108:
contrib_fede_com = 108
# Calcul cotisation N-1
contribution_last_year = sum(
line_ids.filtered(
lambda l: l.product_id == product_com_id
).mapped(
lambda c: c.price_total if c.balance > 0 else -c.price_total
)
)
datas_contrib_com[4] = "Cotisation Fédération de la Com"
datas_contrib_com[5] = contrib_fede_com
datas_contrib_com[6] = contribution_last_year
datas.append(datas_contrib_com)
total_com += contrib_fede_com
# Ajout ligne Fédération CAE
# TODO: Mettre à jour avec is_federation_cae après la maj des
# périodes par la CG
if m.cae:
datas_contrib_cae = datas_contrib.copy()
if liasse:
contrib_fede_cae = self.round_to_closest_multiple(
liasse.contribution_cae, 4
)
else:
contrib_fede_cae = 300
# Calcul cotisation N-1
contribution_last_year = sum(
line_ids.filtered(
lambda l: l.product_id == product_cae_id
).mapped(
lambda c: c.price_total if c.balance > 0 else -c.price_total
)
)
datas_contrib_cae[4] = "Cotisation Fédération des CAE"
datas_contrib_cae[5] = contrib_fede_cae
datas_contrib_cae[6] = contribution_last_year
datas.append(datas_contrib_cae)
total_cae += contrib_fede_cae
if m.is_federation_indus:
datas_contrib_indus = datas_contrib.copy()
if liasse:
contrib_fede_indus = self.round_to_closest_multiple(
liasse.contribution_indus, 4
)
else:
contrib_fede_indus = 100
# Calcul cotisation N-1
contribution_last_year = sum(
line_ids.filtered(
lambda l: l.product_id == product_indus_id
).mapped(
lambda c: c.price_total if c.balance > 0 else -c.price_total
)
)
datas_contrib_indus[4] = "Cotisation Fédération Industrie"
datas_contrib_indus[5] = contrib_fede_indus
datas_contrib_indus[6] = contribution_last_year
datas.append(datas_contrib_indus)
total_indus += contrib_fede_indus
count_member += 1
total_cg += contrib_cg
# Génération du ficher Excel
file_name = (
datetime.strftime(datetime.now(), "%Y-%m-%d_%Hh%M")
+ " Simulation cotisations "
+ self.year
+ ".xlsx"
)
output = BytesIO()
workbook = xlsxwriter.Workbook(output, {"in_memory": True})
worksheet1 = workbook.add_worksheet("Données brutes")
# Intégration Header
format_header = workbook.add_format(
{"bg_color": "#eeeeee", "font_color": "#333333", "bold": True}
)
for col, h in enumerate(header):
worksheet1.write(0, col, h, format_header)
# Intégration datas
format_monetary = workbook.add_format({"num_format": "#,##0 [$€-407]"})
for row, contrib in enumerate(datas):
for col, value in enumerate(contrib):
worksheet1.write(row + 1, col, value)
# Format columns
worksheet1.set_column(0, 0, 10, None)
worksheet1.set_column(1, 1, 30, None)
worksheet1.set_column(2, 2, 15, None)
worksheet1.set_column(3, 3, 50, None)
worksheet1.set_column(4, 4, 25, None)
worksheet1.set_column(5, 6, 20, format_monetary)
worksheet1.set_column(8, 8, 20, format_monetary)
worksheet1.set_column(11, 17, 20, format_monetary)
worksheet1.set_column(20, 20, 20, None)
# Enregistrement du fichier dans la base
workbook.close()
output.seek(0)
mem_bytes = output.read()
file_base64 = base64.encodebytes(mem_bytes)
simul_id = self.simul_ids.create(
{
"cotisation_id": self.id,
"file": file_base64,
"filename": file_name,
"type_simul": type_simul,
"total_member": count_member,
"total_cg": total_cg,
"total_hdf": total_hdf,
"total_com": total_com,
"total_cae": total_cae,
"total_indus": total_indus,
"total_med": total_med,
}
)
return {
"name": "Simulation cotisation",
"type": "ir.actions.act_window",
"view_mode": "form",
"res_model": "scop.cotisation.cg.simulation",
"res_id": simul_id.id,
"target": "new",
"flags": {"mode": "readonly", "default_buttons": False},
}
def show_batch(self):
self.ensure_one()
batch_ids = self.env["queue.job.batch"].browse(
[int(x) for x in self.batch_ids_str.split(",")]
)
return {
"name": "Files d'attente",
"type": "ir.actions.act_window",
"view_type": "form",
"view_mode": "tree,form",
"res_model": "queue.job.batch",
"domain": [("id", "in", batch_ids.ids)],
}
# ------------------------------------------------------
# Global functions
# ------------------------------------------------------
def get_liasse(self, member):
self.ensure_one()
Liasse = self.env["scop.liasse.fiscale"]
# Recherche bordereau précédent
bdx = self.env["scop.bordereau"].search(
[("partner_id", "=", member.id), ("year", "=", str(int(self.year) - 1))]
)
liasse_ids = None
if bdx:
# Recherche liasse année suivante
liasse_ids = Liasse.search(
[
("partner_id", "=", member.id),
("is_qualified", "=", True),
("year", "=", bdx.liasse_fiscale_id.year + 1),
],
order="type_id",
)
# Si pas de liasse année suivante, on cherche la dernière liasse éligible
if not liasse_ids:
liasse_ids = Liasse.search(
[
("partner_id", "=", member.id),
("is_qualified", "=", True),
("year", ">", 0),
"|",
"|",
("L2052_FL", ">", 0),
("av_lf", "!=", 0),
("av_cg", "!=", 0),
],
order="year desc",
)
# Si pas de liasse cette année, on cherche les liasses sans année
if not liasse_ids:
liasse_ids = Liasse.search(
[
("partner_id", "=", member.id),
("is_qualified", "=", True),
"|",
"|",
("L2052_FL", ">", 0),
("av_lf", "!=", 0),
("av_cg", "!=", 0),
],
order="id desc",
)
if liasse_ids:
last_liasse = liasse_ids[0]
if last_liasse.year > 0:
# Filtre les liasses de la dernière année disponible
last_liasse_all = liasse_ids.filtered(
lambda l: l.year == last_liasse.year
)
# Si une seule liasse de cette année => on retourne cette liasse
if len(last_liasse_all) == 1:
return last_liasse
# Priorité ensuite sur les liasses type LM
elif last_liasse_all.filtered(lambda l: l.type_id == "lm"):
return last_liasse_all.filtered(lambda l: l.type_id == "lm")[0]
else:
return last_liasse
# Sinon on retourne la première liasse
return liasse_ids[0]
else:
return Liasse
# ------------------------------------------------------
# Calcul des cotisations
# ------------------------------------------------------
def get_va(self, liasse):
"""
VA saisie ou VA au sens CGSCOP
si pas de VA renseignée sur la liasse fiscale
:param liasse:
:return:
"""
liasse.read(["av_lf", "av_cg"])
if liasse.av_lf != 0:
if liasse.av_lf > liasse.av_cg:
va = liasse.av_cg
else:
va = liasse.av_lf
else:
va = liasse.av_cg
return va
def get_last_staff_id(self, partner):
"""
Return last known staff_id line
:param partner:
: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
def get_type_assiette(self, ca, va):
"""
Find type assiette for bordereau
:param ca:
:param va:
:return: type
"""
if ca > 0 and va > 0:
if ca <= va * 7 / 3:
type_cotiz = "ca"
else:
type_cotiz = "va"
else:
if va > 0:
type_cotiz = "va"
else:
type_cotiz = "ca"
return type_cotiz