1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/account_sepa_direct_debit/models/account_invoice.py
2024-12-10 09:04:09 +07:00

103 lines
4.3 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class AccountMove(models.Model):
_inherit = 'account.move'
sdd_mandate_scheme = fields.Selection(related='sdd_mandate_id.sdd_scheme', readonly=True)
sdd_mandate_id = fields.Many2one(
comodel_name='sdd.mandate',
copy=False,
help="Once this invoice has been paid with Direct Debit, contains the mandate that allowed the payment.")
sdd_has_usable_mandate = fields.Boolean(compute='_compute_sdd_has_usable_mandate', search='_search_sdd_has_usable_mandate')
def _post(self, soft=True):
# OVERRIDE
# Register SDD payments on mandates or trigger an error if no mandate is available.
for pay in self.payment_id.filtered(lambda p: p.payment_method_code in p.payment_method_id._get_sdd_payment_method_code()):
usable_mandate = pay.get_usable_mandate()
if not usable_mandate:
raise UserError(_(
"Unable to post payment %(payment)r due to no usable mandate being available at date %(date)s for partner %(partner)r. Please create one before encoding a SEPA Direct Debit payment.",
payment=pay.name,
date=pay.date,
partner=pay.partner_id.name,
))
pay.sdd_mandate_id = usable_mandate
return super()._post(soft)
@api.model
def _search_sdd_has_usable_mandate(self, operator, value):
""" Returns invoice ids for which a mandate exist that can be used to be paid,
as domain : [('id', 'in', '[4,24,89]')]
SQL is used to minimise footprint and is the same as :
res = self.search([]).filtered(lambda rec: rec.sdd_has_usable_mandate is True and not rec.is_outbound())
return [('id', domain_operator, [x['id'] for x in res])]
"""
if (operator == '=' and value) or (operator == '!=' and not value):
domain_operator = 'in'
else:
domain_operator = 'not in'
query = '''
SELECT
move.id
FROM
sdd_mandate mandate
LEFT JOIN
account_move move ON move.company_id = mandate.company_id AND
move.commercial_partner_id = mandate.partner_id
WHERE
move.move_type IN ('out_invoice', 'in_refund') AND
mandate.state NOT IN ('draft', 'revoked') AND
mandate.start_date <= move.invoice_date AND
(mandate.end_date IS NULL OR mandate.end_date > move.invoice_date)
'''
self._cr.execute(query)
return [('id', domain_operator, [x['id'] for x in self._cr.dictfetchall()])]
@api.depends('company_id', 'commercial_partner_id', 'invoice_date')
def _compute_sdd_has_usable_mandate(self):
for rec in self:
rec.sdd_has_usable_mandate = bool(rec._sdd_get_usable_mandate())
def _sdd_get_usable_mandate(self):
""" returns the first mandate found that can be used to pay this invoice,
or none if there is no such mandate.
"""
if self.move_type in ('out_invoice', 'in_refund'):
return self.env['sdd.mandate']._sdd_get_usable_mandate(self.company_id.id, self.commercial_partner_id.id, self.invoice_date)
else:
return None
def _track_subtype(self, init_values):
# OVERRIDE to log a different message when an invoice is paid using SDD.
self.ensure_one()
if 'state' in init_values and self.state in ('in_payment', 'paid') and self.move_type == 'out_invoice' and self.sdd_mandate_id:
return self.env.ref('account_sepa_direct_debit.sdd_mt_invoice_paid_with_mandate')
return super(AccountMove, self)._track_subtype(init_values)
class AccountMoveLine(models.Model):
_inherit = 'account.move.line'
def _reconcile_post_hook(self, data):
# EXTENDS 'account'
super()._reconcile_post_hook(data)
for pay in self.payment_id:
if pay.sdd_mandate_id:
pay.move_id._get_reconciled_invoices().filtered(lambda m: m.sdd_mandate_id != pay.sdd_mandate_id).sdd_mandate_id = pay.sdd_mandate_id
if pay.sdd_mandate_id.one_off:
pay.sdd_mandate_id.action_close_mandate()