first commit
This commit is contained in:
151
modules/account_dunning_letter/dunning.py
Normal file
151
modules/account_dunning_letter/dunning.py
Normal file
@@ -0,0 +1,151 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user