# © 2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo import tools from odoo import models, fields, api 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") _depends = { 'account.invoice': [ 'year', 'type_contribution_id', 'partner_id', 'amount_total_signed', 'residual_company_signed', 'state', 'type', 'is_contribution', 'refund_invoice_id' ], } # ------------------------------------------------------ # 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.residual_company_signed) AS amount_paid, SUM(i.residual_company_signed) AS amount_due """ return select_str def _from_invoice(self): from_str = """ FROM account_invoice i """ return from_str def _where_invoice(self): where_str = """ WHERE i.state in ('open', 'paid', 'in_payment') 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() ) return query @api.model_cr def init(self): tools.drop_view_if_exists(self.env.cr, self._table) self.env.cr.execute("""CREATE or REPLACE VIEW %s as ( %s )""" % ( self._table, self._query())) # ------------------------------------------------------ # Computed fields # ------------------------------------------------------ @api.multi def _compute_is_loss(self): for contribution in self: contribution.is_loss = contribution._get_is_loss() @api.multi def _compute_name(self): for contribution in self: contribution.name = (contribution.year + ' - ' + contribution.type_contribution_id.name + ' - ' + contribution.partner_id.name) @api.multi 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.invoice'].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.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('payment_move_line_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.invoice_id 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.invoice'].sudo().search([ ('year', '=', int(self.year)), ('partner_id', '=', self.partner_id.id), ('type_contribution_id', '=', self.type_contribution_id.id), ('type', '=', 'out_invoice'), '|', ('bordereau_id.state', 'not in', ('cancel',)), ('bordereau_id', '=', False) ]) 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 += """ <tr class='%s'> <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