152 lines
5.1 KiB
Python
152 lines
5.1 KiB
Python
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
from itertools import chain, groupby
|
|
from operator import attrgetter
|
|
|
|
from trytond.model import fields
|
|
from trytond.modules.company import CompanyReport
|
|
from trytond.pool import Pool, PoolMeta
|
|
from trytond.tools import grouped_slice
|
|
from trytond.transaction import Transaction
|
|
from trytond.wizard import StateReport
|
|
|
|
|
|
class Level(metaclass=PoolMeta):
|
|
__name__ = 'account.dunning.level'
|
|
print_on_letter = fields.Boolean('Print on Letter')
|
|
|
|
|
|
class ProcessDunning(metaclass=PoolMeta):
|
|
__name__ = 'account.dunning.process'
|
|
print_letter = StateReport('account.dunning.letter')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
cls._actions.append('print_letter')
|
|
|
|
def do_print_letter(self, action):
|
|
dunnings = self.records
|
|
ids = [d.id for d in dunnings
|
|
if d.state == 'waiting'
|
|
and not d.blocked
|
|
and d.party
|
|
and d.level.print_on_letter]
|
|
if ids:
|
|
return action, {
|
|
'id': ids[0],
|
|
'ids': ids,
|
|
}
|
|
|
|
def transition_print_letter(self):
|
|
return self.next_state('print_letter')
|
|
|
|
|
|
class Letter(CompanyReport, metaclass=PoolMeta):
|
|
__name__ = 'account.dunning.letter'
|
|
|
|
@classmethod
|
|
def execute(cls, ids, data):
|
|
with Transaction().set_context(address_with_party=True):
|
|
return super().execute(ids, data)
|
|
|
|
@classmethod
|
|
def get_context(cls, records, header, data):
|
|
report_context = super().get_context(records, header, data)
|
|
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
|
|
dunnings = [d for d in records
|
|
if d.state == 'waiting'
|
|
and not d.blocked
|
|
and d.party]
|
|
parties = list(set((d.party for d in dunnings)))
|
|
payments = cls.get_pending_payments(parties)
|
|
key = attrgetter('party')
|
|
dunnings.sort(key=key)
|
|
dunnings = groupby(dunnings, key)
|
|
|
|
PartyLetter = cls.get_party_letter()
|
|
letters = {}
|
|
for party, current_dunnings in dunnings:
|
|
current_dunnings = list(current_dunnings)
|
|
dunning_amount = sum((d.amount for d in current_dunnings))
|
|
current_payments = list(payments.get(party, []))
|
|
payment_amount = sum((l.credit - l.debit
|
|
for l in current_payments))
|
|
if dunning_amount <= payment_amount:
|
|
continue
|
|
letters[party] = PartyLetter(dunnings=current_dunnings,
|
|
payments=current_payments)
|
|
report_context['letters'] = letters
|
|
with Transaction().set_context(company=header['company'].id):
|
|
report_context['today'] = Date.today()
|
|
report_context['get_payment_amount'] = cls.get_payment_amount
|
|
report_context['get_payment_currency'] = cls.get_payment_currency
|
|
return report_context
|
|
|
|
@staticmethod
|
|
def get_party_letter():
|
|
|
|
class PartyLetter(object, metaclass=PoolMeta):
|
|
__slots__ = ('dunnings', 'payments')
|
|
|
|
def __init__(self, dunnings, payments):
|
|
self.dunnings = dunnings
|
|
self.payments = payments
|
|
|
|
@property
|
|
def fees(self):
|
|
return {}
|
|
|
|
def highest_levels(self):
|
|
'Yield each procedure and the highest level'
|
|
key = attrgetter('procedure')
|
|
dunnings = sorted(self.dunnings, key=key)
|
|
for procedure, dunnings in groupby(dunnings, key):
|
|
i = 0
|
|
for dunning in dunnings:
|
|
i = max(i, procedure.levels.index(dunning.level))
|
|
yield procedure, procedure.levels[i]
|
|
|
|
return PartyLetter
|
|
|
|
@staticmethod
|
|
def get_pending_payments(parties):
|
|
"""
|
|
Return a dictionary with party as key and the list of pending payments
|
|
as value.
|
|
"""
|
|
pool = Pool()
|
|
Line = pool.get('account.move.line')
|
|
payments = []
|
|
for sub_parties in grouped_slice(parties):
|
|
payments.append(Line.search([
|
|
('account.type.receivable', '=', True),
|
|
['OR',
|
|
('debit', '<', 0),
|
|
('credit', '>', 0),
|
|
],
|
|
('party', 'in', [p.id for p in sub_parties]),
|
|
('reconciliation', '=', None),
|
|
],
|
|
order=[('party', 'ASC'), ('id', 'ASC')]))
|
|
payments = list(chain(*payments))
|
|
return dict((party, list(payments))
|
|
for party, payments in groupby(payments, attrgetter('party')))
|
|
|
|
@staticmethod
|
|
def get_payment_amount(payment):
|
|
if payment.amount_second_currency:
|
|
return -payment.amount_second_currency
|
|
else:
|
|
return payment.credit - payment.debit
|
|
|
|
@staticmethod
|
|
def get_payment_currency(payment):
|
|
if payment.second_currency:
|
|
return payment.second_currency
|
|
else:
|
|
return payment.account.company.currency
|