trytond-account_reports/common.py

211 lines
7.6 KiB
Python

# This file is part of account_reports for tryton. The COPYRIGHT file
# at the top level of this repository contains the full copyright notices and
# license terms.
from decimal import Decimal
from datetime import datetime
from sql.aggregate import Sum
from sql.conditionals import Coalesce
from sql.operators import In
from trytond.model import fields
from trytond.pool import Pool, PoolMeta
from trytond.tools import reduce_ids
from trytond.transaction import Transaction
class TimeoutException(Exception):
pass
class TimeoutChecker:
def __init__(self, timeout, callback):
self._timeout = timeout
self._callback = callback
self._start = datetime.now()
@property
def elapsed(self):
return (datetime.now() - self._start).seconds
def check(self):
if self.elapsed > self._timeout:
self._callback()
class Configuration(metaclass=PoolMeta):
__name__ = 'account.configuration'
default_timeout = fields.Integer('Timeout (s)')
class FiscalYear(metaclass=PoolMeta):
__name__ = 'account.fiscalyear'
def get_periods(self, start_period, end_period):
pool = Pool()
Period = pool.get('account.period')
domain = [('fiscalyear', '=', self)]
if start_period:
domain += [('start_date', '>=', start_period.start_date)]
domain += [('end_date', '>=', start_period.end_date)]
if end_period:
domain += [('start_date', '<=', end_period.start_date)]
domain += [('end_date', '<=', end_period.end_date)]
periods = Period.search(domain)
return periods
class AccountTemplate(metaclass=PoolMeta):
__name__ = 'account.account.template'
@classmethod
def __setup__(cls):
super(AccountTemplate, cls).__setup__()
# hide because used in odt report
cls.general_ledger_balance.states['invisible'] = True
class Account(metaclass=PoolMeta):
__name__ = 'account.account'
@classmethod
def __setup__(cls):
super(Account, cls).__setup__()
# hide because used in odt report
cls.general_ledger_balance.states['invisible'] = True
@classmethod
def html_read_account_vals(cls, accounts, company, with_moves=False,
exclude_party_moves=False, final_accounts=False):
pool = Pool()
Account = pool.get('account.account')
Move = pool.get('account.move')
MoveLine = pool.get('account.move.line')
line = MoveLine.__table__()
move = Move.__table__()
table_a = Account.__table__()
table_c = Account.__table__()
in_max = 3000
values = {}
transaction = Transaction()
cursor = transaction.connection.cursor()
move_join = 'INNER' if with_moves else 'LEFT'
if not accounts:
accounts = Account.search([
('company', '=', company),
])
if final_accounts:
account_ids = [a.id for a in accounts if not a.childs]
else:
account_ids = [a.id for a in accounts]
group_by = (table_a.id,)
columns = (group_by + (Sum(Coalesce(line.debit, 0)).as_('debit'),
Sum(Coalesce(line.credit, 0)).as_('credit'),
(Sum(Coalesce(line.debit, 0)) -
Sum(Coalesce(line.credit, 0))).as_('balance')))
for i in range(0, len(account_ids), in_max):
sub_ids = account_ids[i:i + in_max]
red_sql = reduce_ids(table_a.id, sub_ids)
where = red_sql
periods = transaction.context.get('periods', False)
if periods:
periods.append(0)
where = (where & In(Coalesce(move.period, 0), periods))
date = transaction.context.get('date')
if date:
where &= (move.date <= date)
where &= (move.date <= date)
where &= (move.company == company.id)
if exclude_party_moves:
# This "where" not use account kind (before a change use it)
# because there are some companies that the accounts kind and
# party_required use in a different way that "standard".
# For example if you check the prty_required an account with
# the kind equal to 'other'
where = (where & (line.party == None))
cursor.execute(*table_a.join(table_c,
condition=(table_c.left >= table_a.left)
& (table_c.right <= table_a.right)
).join(line, move_join,
condition=line.account == table_c.id
).join(move, move_join,
condition=move.id == line.move
).select(*columns, where=where, group_by=group_by))
for account, debit, credit, balance in cursor.fetchall():
# SQLite uses float for SUM
if not isinstance(credit, Decimal):
credit = Decimal(str(credit))
if not isinstance(debit, Decimal):
debit = Decimal(str(debit))
if not isinstance(balance, Decimal):
balance = Decimal(str(balance))
values[account] = {
'credit': credit,
'debit': debit,
'balance': balance,
}
return values
class Party(metaclass=PoolMeta):
__name__ = 'party.party'
@classmethod
def html_get_account_values_by_party(cls, parties, accounts, company):
'''
Function to compute credit,debit and balance for party ids.
'''
res = {}
pool = Pool()
Move = pool.get('account.move')
MoveLine = pool.get('account.move.line')
Account = pool.get('account.account')
transaction = Transaction()
context = transaction.context
cursor = transaction.connection.cursor()
move = Move.__table__()
line = MoveLine.__table__()
account = Account.__table__()
group_by = (line.party, line.account,)
columns = (group_by + (Sum(Coalesce(line.debit, 0)).as_('debit'),
Sum(Coalesce(line.credit, 0)).as_('credit'),
(Sum(Coalesce(line.debit, 0)) -
Sum(Coalesce(line.credit, 0))).as_('balance')))
line_query, _ = MoveLine.query_get(line)
if context.get('date'):
# Cumulate data from previous fiscalyears
line_query = line.move.in_(move.select(move.id,
where=move.date <= context.get('date')))
where = (line_query &
(account.company == company.id))
if accounts:
where = where & line.account.in_([a.id for a in accounts])
if parties:
where = where & line.party.in_([p.id for p in parties])
cursor.execute(*line.join(account,
condition=(line.account == account.id)
).select(*columns, where=where, group_by=group_by))
for party, account, debit, credit, balance in cursor.fetchall():
# SQLite uses float for SUM
if not isinstance(credit, Decimal):
credit = Decimal(str(credit))
if not isinstance(debit, Decimal):
debit = Decimal(str(debit))
if not isinstance(balance, Decimal):
balance = Decimal(str(balance))
if account not in res:
res[account] = {}
res[account][party] = {
'credit': credit,
'debit': debit,
'balance': balance,
}
return res