trytond-party_credit_limit/party.py

184 lines
7.2 KiB
Python

# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
from decimal import Decimal
from sql import Literal
from sql.aggregate import Sum
from sql.conditionals import Coalesce, Case
from trytond.pyson import Eval
from trytond.model import fields
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
__all__ = ['Party']
class Party(metaclass=PoolMeta):
__name__ = 'party.party'
unpayed_amount = fields.Function(fields.Numeric('Unpayed',
digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits'], help="Due amount until today."),
'get_accounting_amount')
pending_amount = fields.Function(fields.Numeric('Pending',
digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits'],
help="Amount to be due in the future."),
'get_accounting_amount')
draft_invoices_amount = fields.Function(fields.Numeric(
'Draft invoices', digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits'],
help="Total amount of invoices to be posted."),
'get_draft_invoices_amount')
uninvoiced_amount = fields.Function(fields.Numeric('Uninvoiced',
digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits'],
help="Amount in processing sales still not invoiced."),
'get_uninvoiced_amount')
amount_to_limit = fields.Function(fields.Numeric('To limit',
digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits']),
'get_amounts')
limit_percent = fields.Function(fields.Numeric('% Limit', digits=(5, 2)),
'get_amounts')
@classmethod
def get_accounting_amount(cls, parties, names):
res = {}
pool = Pool()
MoveLine = pool.get('account.move.line')
Account = pool.get('account.account')
Date = pool.get('ir.date')
User = pool.get('res.user')
for name in names:
if name not in ('unpayed_amount', 'pending_amount'):
raise Exception('Bad argument')
res[name] = dict((p.id, Decimal('0.0')) for p in parties)
line = MoveLine.__table__()
account = Account.__table__()
today = Date.today()
transaction = Transaction()
cursor = transaction.connection.cursor()
line_query, _ = MoveLine.query_get(line)
user_id = transaction.user
if user_id == 0 and 'user' in Transaction().context:
user_id = transaction.context['user']
user = User(user_id)
if not user.company:
return res
company_id = user.company.id
amount = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
# TODO where account active with ActivePeriodMixin
cursor.execute(*line.join(account,
condition=account.id == line.account
).select(line.party,
Sum(Case((line.maturity_date < today, amount),
else_=Literal(0))).as_('unpayed_amount'),
Sum(Case((line.maturity_date == None or
line.maturity_date >= today, amount),
else_=Literal(0))).as_('pending_amount'),
where=(account.kind == 'receivable')
& (line.reconciliation == None)
& (account.company == company_id)
& line_query,
group_by=(line.party)))
for party_id, unpayed, pending in cursor.fetchall():
for name, value in (('unpayed_amount', unpayed),
('pending_amount', pending)):
if name not in names:
continue
# SQLite uses float for SUM
if not isinstance(value, Decimal):
value = Decimal(str(value))
res[name][party_id] = value
return res
@classmethod
def get_draft_invoices_amount(cls, parties, name):
pool = Pool()
Invoice = pool.get('account.invoice')
res = {}.fromkeys([p.id for p in parties], Decimal('0.0'))
without_sales = Transaction().context.get('without_sales', False)
domain = [
('type', '=', ('out')),
('party', 'in', [p.id for p in parties]),
('state', 'in', ['draft', 'validated']),
]
for invoice in Invoice.search(domain):
if without_sales and invoice.sales:
continue
res[invoice.party.id] += invoice.untaxed_amount
return res
@classmethod
def get_uninvoiced_amount(cls, parties, name):
pool = Pool()
Sale = pool.get('sale.sale')
Currency = pool.get('currency.currency')
User = pool.get('res.user')
amounts = {}.fromkeys([p.id for p in parties], Decimal('0.0'))
sales = Sale.search([
('party', 'in', [p.id for p in parties]),
('state', '=', 'processing'),
])
user = User(Transaction().user)
if not user.company:
return amounts
currency = user.company.currency
for sale in sales:
amount = 0
for line in sale.lines:
amount += Currency.compute(sale.currency, line.amount,
currency, round=False)
for invoice_line in line.invoice_lines:
invoice = invoice_line.invoice
if invoice:
amount -= Currency.compute(invoice.currency,
invoice_line.amount, currency)
amounts[sale.party.id] += amount
return amounts
@classmethod
def get_amounts(cls, parties, names):
res = {}
for name in names:
if name not in ('amount_to_limit', 'limit_percent'):
raise Exception('Bad argument')
res[name] = {}.fromkeys([p.id for p in parties],
Decimal('100.0') if name == 'limit_percent'
else Decimal('0.00'))
for party in parties:
if 'amount_to_limit' in names:
limit_amount = party.credit_limit_amount or Decimal('0.0')
res['amount_to_limit'][party.id] = (
limit_amount - party.credit_amount)
if party.credit_limit_amount and 'limit_percent' in names:
percent = (Decimal('100.0') *
party.credit_amount / party.credit_limit_amount)
percent = percent.quantize(Decimal('0.01'))
res['limit_percent'][party.id] = percent
for key in res.keys():
if key not in names:
del res[key]
return res
@classmethod
def get_credit_amount(cls, parties, name):
amounts = super(Party, cls).get_credit_amount(parties, name)
with Transaction().set_context(without_sales=True):
uninvoiced = cls.get_draft_invoices_amount(parties, name)
for party, value in uninvoiced.items():
amounts[party] += value
return amounts
@classmethod
def _credit_limit_to_lock(cls):
models = super(Party, cls)._credit_limit_to_lock()
return models + ['account.invoice', 'sale.sale']