141 lines
6.3 KiB
Python
141 lines
6.3 KiB
Python
from odoo import api, models, fields
|
|
from odoo.tools.translate import _
|
|
|
|
class AccountMove(models.Model):
|
|
_inherit = 'account.move'
|
|
|
|
@api.depends('amount_residual', 'move_type', 'state', 'company_id')
|
|
def _compute_payment_state(self):
|
|
super()._compute_payment_state()
|
|
for invoice in self:
|
|
if invoice.payment_state == 'paid' and invoice.is_invoice(include_receipts=True):
|
|
term_lines = invoice.line_ids.filtered(lambda l: l.display_type == 'payment_term' and l.account_id.account_type == 'liability_current')
|
|
if term_lines and invoice.currency_id.is_zero(invoice.amount_residual):
|
|
payments = (term_lines.matched_debit_ids.debit_move_id.payment_id |
|
|
term_lines.matched_credit_ids.credit_move_id.payment_id)
|
|
if any(not p.is_matched for p in payments if p):
|
|
invoice.payment_state = invoice._get_invoice_in_payment_state()
|
|
|
|
def _get_all_reconciled_invoice_partials(self):
|
|
reconciled_partials = super()._get_all_reconciled_invoice_partials()
|
|
if isinstance(reconciled_partials, dict):
|
|
reconciled_partials = []
|
|
|
|
self.ensure_one()
|
|
|
|
# If the super() returns something, it might only be for payable/receivable.
|
|
# We also need to fetch partials for liability_current
|
|
reconciled_lines = self.line_ids.filtered(lambda line: line.account_id.account_type == 'liability_current')
|
|
if not reconciled_lines:
|
|
return reconciled_partials
|
|
|
|
self.env['account.partial.reconcile'].flush_model([
|
|
'credit_amount_currency', 'credit_move_id', 'debit_amount_currency',
|
|
'debit_move_id', 'exchange_move_id',
|
|
])
|
|
query = '''
|
|
SELECT
|
|
part.id,
|
|
part.exchange_move_id,
|
|
part.debit_amount_currency AS amount,
|
|
part.credit_move_id AS aml_id,
|
|
'debit' AS direction,
|
|
part.create_date
|
|
FROM account_partial_reconcile part
|
|
WHERE part.debit_move_id IN %s
|
|
UNION ALL
|
|
SELECT
|
|
part.id,
|
|
part.exchange_move_id,
|
|
part.credit_amount_currency AS amount,
|
|
part.debit_move_id AS aml_id,
|
|
'credit' AS direction,
|
|
part.create_date
|
|
FROM account_partial_reconcile part
|
|
WHERE part.credit_move_id IN %s
|
|
ORDER BY create_date DESC
|
|
'''
|
|
self.env.cr.execute(query, [tuple(reconciled_lines.ids), tuple(reconciled_lines.ids)])
|
|
|
|
aml_ids = set()
|
|
exchange_move_ids = set()
|
|
partials_data = []
|
|
for row in self.env.cr.dictfetchall():
|
|
aml_ids.add(row['aml_id'])
|
|
if row['exchange_move_id']:
|
|
exchange_move_ids.add(row['exchange_move_id'])
|
|
partials_data.append(row)
|
|
|
|
if not partials_data:
|
|
return reconciled_partials
|
|
|
|
amls = self.env['account.move.line'].browse(list(aml_ids)).filtered(lambda aml: aml.move_id.id not in exchange_move_ids)
|
|
amls_dict = {aml.id: aml for aml in amls}
|
|
|
|
for partial in partials_data:
|
|
if partial['aml_id'] not in amls_dict:
|
|
continue
|
|
counterpart_aml = amls_dict[partial['aml_id']]
|
|
reconciled_partials.append({
|
|
'aml': counterpart_aml,
|
|
'amount': partial['amount'],
|
|
'currency': self.currency_id,
|
|
'is_exchange': bool(partial['exchange_move_id']),
|
|
'partial_id': partial['id'],
|
|
})
|
|
|
|
return reconciled_partials
|
|
|
|
def _compute_payments_widget_to_reconcile_info(self):
|
|
super()._compute_payments_widget_to_reconcile_info()
|
|
for move in self:
|
|
if move.state != 'posted' or move.payment_state not in ('not_paid', 'partial') or not move.is_invoice(include_receipts=True):
|
|
continue
|
|
|
|
pay_term_lines = move.line_ids.filtered(lambda line: line.account_id.account_type == 'liability_current')
|
|
if not pay_term_lines:
|
|
continue
|
|
|
|
domain = [
|
|
('account_id', 'in', pay_term_lines.account_id.ids),
|
|
('parent_state', '=', 'posted'),
|
|
('partner_id', '=', move.commercial_partner_id.id),
|
|
('reconciled', '=', False),
|
|
'|', ('amount_residual', '!=', 0.0), ('amount_residual_currency', '!=', 0.0),
|
|
]
|
|
|
|
payments_widget_vals = move.invoice_outstanding_credits_debits_widget or {'outstanding': True, 'content': [], 'move_id': move.id}
|
|
|
|
if move.is_inbound():
|
|
domain.append(('balance', '<', 0.0))
|
|
payments_widget_vals['title'] = _('Outstanding credits')
|
|
else:
|
|
domain.append(('balance', '>', 0.0))
|
|
payments_widget_vals['title'] = _('Outstanding debits')
|
|
|
|
for line in self.env['account.move.line'].search(domain):
|
|
if line.currency_id == move.currency_id:
|
|
amount = abs(line.amount_residual_currency)
|
|
else:
|
|
amount = line.company_currency_id._convert(abs(line.amount_residual), move.currency_id, move.company_id, line.date)
|
|
if move.currency_id.is_zero(amount):
|
|
continue
|
|
|
|
# avoid duplicates
|
|
if payments_widget_vals['content'] and any(c['id'] == line.id for c in payments_widget_vals['content']):
|
|
continue
|
|
|
|
payments_widget_vals['content'].append({
|
|
'journal_name': line.ref or line.move_id.name,
|
|
'amount': amount,
|
|
'currency_id': move.currency_id.id,
|
|
'id': line.id,
|
|
'move_id': line.move_id.id,
|
|
'date': fields.Date.to_string(line.date),
|
|
'account_payment_id': line.payment_id.id,
|
|
})
|
|
|
|
if payments_widget_vals['content']:
|
|
move.invoice_outstanding_credits_debits_widget = payments_widget_vals
|
|
move.invoice_has_outstanding = True
|