trytond-account_payment_pro.../payment.py

225 lines
8.3 KiB
Python

# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
from collections import defaultdict
from decimal import Decimal
from trytond.model import ModelView, Workflow, fields
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Bool, Eval
from trytond.transaction import Transaction
__all__ = ['Journal', 'Payment']
class Journal(metaclass=PoolMeta):
__name__ = 'account.payment.journal'
processing_account = fields.Many2One('account.account',
'Processing Account', states={
'required': Bool(Eval('processing_journal')),
},
depends=['processing_journal'])
processing_journal = fields.Many2One('account.journal',
'Processing Journal', states={
'required': Bool(Eval('processing_account')),
},
depends=['processing_account'])
@classmethod
def __setup__(cls):
super().__setup__()
cls.clearing_journal.context = {'company': Eval('company', -1)}
cls.clearing_journal.depends.add('company')
cls.processing_journal.context = {'company': Eval('company', -1)}
cls.processing_journal.depends.add('company')
class Payment(metaclass=PoolMeta):
__name__ = 'account.payment'
processing_move = fields.Many2One('account.move', 'Processing Move',
readonly=True)
@classmethod
@Workflow.transition('processing')
def process(cls, payments, group):
pool = Pool()
Move = pool.get('account.move')
Line = pool.get('account.move.line')
group = super(Payment, cls).process(payments, group)
moves = []
for payment in payments:
move = payment.create_processing_move()
if move:
moves.append(move)
if moves:
Move.save(moves)
cls.write(*sum((([m.origin], {'processing_move': m.id})
for m in moves), ()))
Move.post(moves)
to_reconcile = defaultdict(list)
for payment in payments:
if (payment.line
and not payment.line.reconciliation
and payment.processing_move):
lines = [l for l in payment.processing_move.lines
if l.account == payment.line.account] + [payment.line]
if not sum(l.debit - l.credit for l in lines):
to_reconcile[payment.party].extend(lines)
for lines in list(to_reconcile.values()):
Line.reconcile(lines)
return group
def create_processing_move(self, date=None):
pool = Pool()
Currency = pool.get('currency.currency')
Move = pool.get('account.move')
Line = pool.get('account.move.line')
Period = pool.get('account.period')
Date = pool.get('ir.date')
if not self.line:
return
if (not self.journal.processing_account
or not self.journal.processing_journal):
return
if self.processing_move:
return self.processing_move
if date is None:
date = Date.today()
period = Period.find(self.company.id, date=date)
# compatibility with account_bank_statement_payment
clearing_percent = getattr(
self.journal, 'clearing_percent', Decimal(1)) or Decimal(1)
processing_amount = self.amount * clearing_percent
local_currency = self.journal.currency == self.company.currency
if not local_currency:
with Transaction().set_context(date=self.date):
local_amount = Currency.compute(
self.journal.currency, processing_amount,
self.company.currency)
else:
local_amount = self.company.currency.round(processing_amount)
move = Move(
journal=self.journal.processing_journal,
origin=self,
date=date,
period=period)
line = Line()
if self.kind == 'payable':
line.debit, line.credit = local_amount, 0
else:
line.debit, line.credit = 0, local_amount
line.account = self.line.account
if not local_currency:
line.amount_second_currency = processing_amount
line.second_currency = self.journal.currency
line.party = (self.line.party
if self.line.account.party_required else None)
counterpart = Line()
if self.kind == 'payable':
counterpart.debit, counterpart.credit = 0, local_amount
else:
counterpart.debit, counterpart.credit = local_amount, 0
counterpart.account = self.journal.processing_account
if not local_currency:
counterpart.amount_second_currency = -processing_amount
counterpart.second_currency = self.journal.currency
counterpart.party = (self.line.party
if self.journal.processing_account.party_required else None)
move.lines = (line, counterpart)
return move
@classmethod
@ModelView.button
@Workflow.transition('succeeded')
def succeed(cls, payments):
pool = Pool()
Line = pool.get('account.move.line')
super(Payment, cls).succeed(payments)
for payment in payments:
if (payment.journal.processing_account
and payment.journal.processing_account.reconcile
and payment.processing_move
and payment.journal.clearing_account
and payment.journal.clearing_account.reconcile
and payment.clearing_move):
to_reconcile = defaultdict(list)
lines = (payment.processing_move.lines
+ payment.clearing_move.lines)
for line in lines:
if line.account.reconcile and not line.reconciliation:
key = (
line.account.id,
line.party.id if line.party else None)
to_reconcile[key].append(line)
for lines in list(to_reconcile.values()):
if not sum((l.debit - l.credit) for l in lines):
Line.reconcile(lines)
def _get_clearing_move(self, date=None):
move = super(Payment, self)._get_clearing_move(date=date)
if move and self.processing_move:
for line in move.lines:
if line.account == self.line.account:
line.account = self.journal.processing_account
line.party = (self.line.party
if line.account.party_required else None)
return move
@classmethod
@ModelView.button
@Workflow.transition('failed')
def fail(cls, payments):
pool = Pool()
Move = pool.get('account.move')
Line = pool.get('account.move.line')
Reconciliation = pool.get('account.move.reconciliation')
super(Payment, cls).fail(payments)
to_delete = []
to_reconcile = defaultdict(lambda: defaultdict(list))
to_unreconcile = []
to_post = []
for payment in payments:
if payment.processing_move:
if payment.processing_move.state == 'draft':
to_delete.append(payment.processing_move)
for line in payment.processing_move.lines:
if line.reconciliation:
to_unreconcile.append(line.reconciliation)
else:
cancel_move = payment.processing_move.cancel()
to_post.append(cancel_move)
for line in (payment.processing_move.lines
+ cancel_move.lines):
if line.reconciliation:
to_unreconcile.append(line.reconciliation)
if line.account.reconcile:
to_reconcile[payment.party][line.account].append(
line)
if to_unreconcile:
Reconciliation.delete(to_unreconcile)
if to_delete:
Move.delete(to_delete)
if to_post:
Move.post(to_post)
for party in to_reconcile:
for lines in list(to_reconcile[party].values()):
Line.reconcile(lines)
cls.write(payments, {'processing_move': None})