Newer
Older
# © 2022 Le Filament (<http://www.le-filament.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from psycopg2.extensions import AsIs
from odoo import api, fields, models, tools
class ScopContributionReport(models.Model):
_name = "scop.contribution.report"
_description = "Vue cotisations"
_auto = False
_order = "year desc, partner_id"
name = fields.Char(compute="_compute_name")
year = fields.Char("Année de cotisation")
source = fields.Selection(
string="Source",
selection=[("odoo", "Odoo")],
required=True,
)
type_contribution_id = fields.Many2one(
comodel_name="scop.contribution.type",
string="Type de cotisation",
readonly=True,
)
partner_id = fields.Many2one("res.partner", string="Partner", readonly=True)
amount_called = fields.Float("Montant Appelé")
amount_paid = fields.Float("Montant Payé")
amount_due = fields.Float("Montant Restant")
is_loss = fields.Boolean("Exonération/Perte", compute="_compute_is_loss")
payments = fields.Html("Paiements", compute="_compute_payments")
"account.move": [
"year",
"type_contribution_id",
"partner_id",
"amount_total_signed",
"amount_residual_signed",
"state",
"move_type",
"is_contribution",
"payment_state",
],
}
# ------------------------------------------------------
# Sub Query
# ------------------------------------------------------
def _select_invoice(self):
select_str = """
SELECT
'odoo' as source,
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.amount_residual_signed) AS amount_paid,
SUM(i.amount_residual_signed) AS amount_due
"""
return select_str
def _from_invoice(self):
from_str = """
FROM
"""
return from_str
def _where_invoice(self):
where_str = """
WHERE
i.state = 'posted'
AND i.move_type in ('out_invoice', 'out_refund')
AND i.is_contribution = true
"""
return where_str
def _groupby_invoice(self):
groupby_str = """
GROUP BY
i.year,
i.type_contribution_id,
i.partner_id
"""
return groupby_str
def _query_invoice(self):
query = "(%s %s %s %s)" % (
self._select_invoice(),
self._from_invoice(),
self._where_invoice(),
self._groupby_invoice(),
)
return query
def _subquery(self):
return self._query_invoice()
# ------------------------------------------------------
# Main Query
# ------------------------------------------------------
def _select(self):
select_str = """
SELECT
ROW_NUMBER() OVER(ORDER BY c.year, c.partner_id) AS id,
c.source,
c.year,
c.type_contribution_id,
c.partner_id,
SUM(c.amount_called) AS amount_called,
SUM(c.amount_paid) AS amount_paid,
SUM(c.amount_due) AS amount_due
FROM (
"""
return select_str
def _query_groupby(self):
return """
GROUP BY
c.year,
c.type_contribution_id,
c.partner_id,
c.source
"""
def _query_order(self):
return "ORDER BY c.year DESC"
def _query(self):
query = (
self._select()
+ self._subquery()
+ ") c "
+ self._query_groupby()
+ self._query_order()
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute(
"CREATE or REPLACE VIEW %s as (%s)",
(AsIs(self._table), AsIs(self._query())),
)
# ------------------------------------------------------
# Computed fields
# ------------------------------------------------------
def _compute_is_loss(self):
for contribution in self:
contribution.is_loss = contribution._get_is_loss()
def _compute_name(self):
for contribution in self:
contribution.name = (
contribution.year
+ " - "
+ contribution.type_contribution_id.name
+ " - "
+ contribution.partner_id.name
)
def _compute_payments(self):
for contribution in self:
contribution.payments = contribution._get_payment()
# ------------------------------------------------------
# Business functions
# ------------------------------------------------------
def _get_is_loss(self):
invoice_ids = (
self.env["account.move"]
.sudo()
.search(
[
("year", "=", int(self.year)),
("partner_id", "=", self.partner_id.id),
(
"type_contribution_id",
"=",
self.type_contribution_id.id,
),
]
)
)
refund_ids = invoice_ids.filtered(lambda i: i.move_type == "out_refund")
if refund_ids:
return True
else:
return False
def _get_payment(self):
self.ensure_one()
payments_html = False
if self.source == "odoo":
invoice_ids = self.get_invoice_contribution()
payment_ids = invoice_ids.mapped("move_line_payment_ids")
if payment_ids:
payments = payment_ids.mapped(
lambda p: {
"date": p.date,
"name": p.name,
"ref": p.ref,
"credit": p.credit,
"class": "",
}
if not p.move_id.move_type in ("out_invoice", "out_refund")
else {
"date": p.date,
"name": p.name,
"ref": "",
"credit": p.credit,
"class": "text-danger",
}
)
payments_html = self._get_html_table(payments)
if not payments_html:
payments_html = (
"<p>Il n'y a pas de paiements associés à cette cotisation</p>"
)
return payments_html
def get_invoice_contribution(self):
invoice_ids = (
self.env["account.move"]
.sudo()
.search(
return invoice_ids
def _get_html_table(self, payments):
"""
:param payments: list of dict {date, name, ref, amount}
@return: HTML table with payments
"""
start_html = """
<table class='table table-sm table-striped table-hover table-bordered'>
<thead><tr>
<th>Date</th>
<th>Libellé</th>
<th>Référence</th>
<th>Montant</th>
</tr></thead>
<tbody>
"""
content_html = ""
for payment in payments:
content_html += """
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td class='text-right'>%.2f €</td>
</tr>
""" % (
payment.get("class", ""),
payment.get("date", "").strftime("%d/%m/%Y"),
payment.get("name", ""),
payment.get("ref", ""),
payment.get("credit", 0.0),
)
end_html = "</tbody></table>"
return start_html + content_html + end_html
def _get_contribution_domain(self):
return [
("year", "=", int(self.year)),
("partner_id", "=", self.partner_id.id),
(
"type_contribution_id",
"=",
self.type_contribution_id.id,
),
("move_type", "=", "out_invoice"),
("state", "=", "posted"),
]