Odoo18-Base/addons/l10n_latam_check/models/l10n_latam_check.py
2025-01-06 10:57:38 +07:00

222 lines
9.3 KiB
Python

# pylint: disable=protected-access
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import stdnum
from odoo import models, fields, api, Command, _
from odoo.exceptions import UserError, ValidationError
from odoo.tools import index_exists
_logger = logging.getLogger(__name__)
class l10nLatamAccountPaymentCheck(models.Model):
_name = 'l10n_latam.check'
_description = 'Account payment check'
_check_company_auto = True
_inherit = ['mail.thread', 'mail.activity.mixin']
payment_id = fields.Many2one(
'account.payment',
required=True,
ondelete='cascade',
)
operation_ids = fields.Many2many(
comodel_name='account.payment',
relation='l10n_latam_check_account_payment_rel',
column1="check_id",
column2="payment_id",
readonly=True,
check_company=True,
)
current_journal_id = fields.Many2one(
comodel_name='account.journal',
compute='_compute_current_journal', store=True,
)
name = fields.Char(string='Number')
bank_id = fields.Many2one(
comodel_name='res.bank',
compute='_compute_bank_id', store=True, readonly=False,
)
issuer_vat = fields.Char(
compute='_compute_issuer_vat', store=True, readonly=False,
)
payment_date = fields.Date(readonly=False, required=True)
amount = fields.Monetary()
outstanding_line_id = fields.Many2one('account.move.line', readonly=True, check_company=True)
issue_state = fields.Selection(
selection=[('handed', 'Handed'), ('debited', 'Debited'), ('voided', 'Voided')],
compute='_compute_issue_state',
store=True
)
# fields from payment
payment_method_code = fields.Char(related='payment_id.payment_method_code')
partner_id = fields.Many2one(related='payment_id.partner_id')
original_journal_id = fields.Many2one(related='payment_id.journal_id')
company_id = fields.Many2one(related='payment_id.company_id', store=True)
currency_id = fields.Many2one(related='payment_id.currency_id')
payment_method_line_id = fields.Many2one(
related='payment_id.payment_method_line_id',
store=True,
)
def _auto_init(self):
super()._auto_init()
if not index_exists(self.env.cr, 'l10n_latam_check_unique'):
# issue_state is used to know that is an own check and also that is posted
self.env.cr.execute("""
CREATE UNIQUE INDEX l10n_latam_check_unique
ON l10n_latam_check(name, payment_method_line_id)
WHERE outstanding_line_id IS NOT NULL
""")
@api.onchange('name')
def _onchange_name(self):
if self.name:
self.name = self.name.zfill(8)
def _prepare_void_move_vals(self):
return {
'ref': 'Void check',
'journal_id': self.outstanding_line_id.move_id.journal_id.id,
'line_ids': [
Command.create({
'name': "Void check %s" % self.outstanding_line_id.name,
'date_maturity': self.outstanding_line_id.date_maturity,
'amount_currency': self.outstanding_line_id.amount_currency,
'currency_id': self.outstanding_line_id.currency_id.id,
'debit': self.outstanding_line_id.debit,
'credit': self.outstanding_line_id.credit,
'partner_id': self.outstanding_line_id.partner_id.id,
'account_id': self.payment_id.destination_account_id.id,
}),
Command.create({
'name': "Void check %s" % self.outstanding_line_id.name,
'date_maturity': self.outstanding_line_id.date_maturity,
'amount_currency': -self.outstanding_line_id.amount_currency,
'currency_id': self.outstanding_line_id.currency_id.id,
'debit': -self.outstanding_line_id.debit,
'credit': -self.outstanding_line_id.credit,
'partner_id': self.outstanding_line_id.partner_id.id,
'account_id': self.outstanding_line_id.account_id.id,
}),
],
}
@api.depends('outstanding_line_id.amount_residual')
def _compute_issue_state(self):
for rec in self:
if not rec.outstanding_line_id:
rec.issue_state = False
elif rec.amount and not rec.outstanding_line_id.amount_residual:
if any(
line.account_id.account_type in ['liability_payable', 'asset_receivable']
for line in rec.outstanding_line_id.matched_debit_ids.debit_move_id.move_id.line_ids
):
rec.issue_state = 'voided'
else:
rec.issue_state = 'debited'
else:
rec.issue_state = 'handed'
def action_void(self):
for rec in self.filtered('outstanding_line_id'):
void_move = rec.env['account.move'].create(rec._prepare_void_move_vals())
void_move.action_post()
(void_move.line_ids[1] + rec.outstanding_line_id).reconcile()
def _get_last_operation(self):
self.ensure_one()
return (self.payment_id + self.operation_ids).filtered(
lambda x: x.state != 'draft').sorted(key=lambda payment: (payment.date, payment._origin.id))[-1:]
@api.depends('payment_id.state', 'operation_ids.state')
def _compute_current_journal(self):
for rec in self:
last_operation = rec._get_last_operation()
if not last_operation:
rec.current_journal_id = False
continue
if last_operation.payment_type == 'inbound':
rec.current_journal_id = last_operation.journal_id
else:
rec.current_journal_id = False
def button_open_payment(self):
self.ensure_one()
return self.payment_id._get_records_action()
def button_open_check_operations(self):
''' Redirect the user to the invoice(s) paid by this payment.
:return: An action on account.move.
'''
self.ensure_one()
operations = ((self.operation_ids + self.payment_id).filtered(lambda x: x.state != 'draft'))
action = {
'name': _("Check Operations"),
'type': 'ir.actions.act_window',
'res_model': 'account.payment',
'views': [
(self.env.ref('l10n_latam_check.view_account_third_party_check_operations_tree').id, 'list'),
(False, 'form')
],
'context': {'create': False},
'domain': [('id', 'in', operations.ids)],
}
return action
def action_show_reconciled_move(self):
self.ensure_one()
move = self._get_reconciled_move()
return move._get_records_action()
def action_show_journal_entry(self):
self.ensure_one()
return self.outstanding_line_id.move_id._get_records_action()
def _get_reconciled_move(self):
reconciled_line = self.outstanding_line_id.full_reconcile_id.reconciled_line_ids - self.outstanding_line_id
return (reconciled_line.move_id.line_ids - reconciled_line).mapped('move_id')
@api.constrains('amount')
def _constrains_min_amount(self):
min_amount_error = self.filtered(lambda x: x.amount <= 0)
if min_amount_error:
raise ValidationError(_('The amount of the check must be greater than 0'))
@api.depends('payment_method_line_id.code', 'payment_id.partner_id')
def _compute_bank_id(self):
new_third_party_checks = self.filtered(lambda x: x.payment_method_line_id.code == 'new_third_party_checks')
for rec in new_third_party_checks:
rec.bank_id = rec.partner_id.bank_ids[:1].bank_id
(self - new_third_party_checks).bank_id = False
@api.depends('payment_method_line_id.code', 'payment_id.partner_id')
def _compute_issuer_vat(self):
new_third_party_checks = self.filtered(lambda x: x.payment_method_line_id.code == 'new_third_party_checks')
for rec in new_third_party_checks:
rec.issuer_vat = rec.payment_id.partner_id.vat
(self - new_third_party_checks).issuer_vat = False
@api.onchange('issuer_vat')
def _clean_issuer_vat(self):
for rec in self.filtered(lambda x: x.issuer_vat and x.company_id.country_id.code):
stdnum_vat = stdnum.util.get_cc_module(rec.company_id.country_id.code, 'vat')
if hasattr(stdnum_vat, 'compact'):
rec.issuer_vat = stdnum_vat.compact(rec.issuer_vat)
@api.constrains('issuer_vat')
def _check_issuer_vat(self):
for rec in self.filtered(lambda x: x.issuer_vat and x.company_id.country_id):
if not self.env['res.partner']._run_vat_test(rec.issuer_vat, rec.company_id.country_id):
error_message = self.env['res.partner']._build_vat_error_message(
rec.company_id.country_id.code.lower(), rec.issuer_vat, 'Check Issuer VAT'
)
raise ValidationError(error_message)
@api.ondelete(at_uninstall=False)
def _unlink_if_payment_is_draft(self):
if any(check.payment_id.state != 'draft' for check in self):
raise UserError("Can't delete a check if payment is In Process!")