From 52875c1d642fc1eaef4f008caea47baca3854299 Mon Sep 17 00:00:00 2001 From: Bertrand Chenal Date: Tue, 22 Jul 2008 10:11:28 +0200 Subject: [PATCH] Added Aged balance, WIP. --- account.py | 191 +++++++++++++++++++++++++++++++++++++++++++++++ account.xml | 44 +++++++++++ aged_balance.odt | Bin 0 -> 14093 bytes 3 files changed, 235 insertions(+) create mode 100644 aged_balance.odt diff --git a/account.py b/account.py index f6e87a301d..539e495dc1 100644 --- a/account.py +++ b/account.py @@ -1405,3 +1405,194 @@ class ThirdPartyBalance(Report): return objects ThirdPartyBalance() + + +class OpenAgedBalanceInit(WizardOSV): + _name = 'account.account.open_aged_balance.init' + company = fields.Many2One('company.company', 'Company', required=True) + fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year', + required=True) + balance_type = fields.Selection( + [('customer', 'Customer'), ('supplier', 'Supplier'), ('both', 'Both')], + "Type", required=True) + term1 = fields.Integer("First Term", required=True) + term2 = fields.Integer("Second Term", required=True) + term3 = fields.Integer("Third Term", required=True) + unit = fields.selection( + [('day', 'Day'), ('month', 'Month')], "Unit", required=True) + posted = fields.Boolean('Posted Move', help='Only posted move') + + def default_fiscalyear(self, cursor, user, context=None): + fiscalyear_obj = self.pool.get('account.fiscalyear') + if context is None: + context = {} + fiscalyear_id = fiscalyear_obj.find(cursor, user, + context.get('company', False), exception=False, context=context) + if fiscalyear_id: + return fiscalyear_obj.name_get(cursor, user, fiscalyear_id, + context=context)[0] + return False + + def default_balance_type(self, cursor, user, context=None): + return "customer" + + def default_posted(self, cursor, user, context=None): + return False + + def default_term1(self, cursor, user, context=None): + return 30 + + def default_term2(self, cursor, user, context=None): + return 60 + + def default_term3(self, cursor, user, context=None): + return 90 + + def default_unit(self, cursor, user, context=None): + return 'day' + + def default_company(self, cursor, user, context=None): + if context is None: + context = {} + company_obj = self.pool.get('company.company') + if context.get('company'): + return company_obj.name_get(cursor, user, context['company'], + context=context)[0] + return False + +OpenAgedBalanceInit() + + +class OpenAgedBalance(Wizard): + 'Open Aged Party Balance' + _name = 'account.account.open_aged_balance' + + def __init__(self): + super(OpenAgedBalance, self).__init__() + self._error_messages.update({ + 'warning': 'Warning', + 'term_overlap_desc': 'You cannot define overlapping terms'}) + states = { + 'init': { + 'result': { + 'type': 'form', + 'object': 'account.account.open_aged_balance.init', + 'state': [ + ('end', 'Cancel', 'tryton-cancel'), + ('print', 'Print', 'tryton-ok', True), + ], + }, + }, + 'print': { + 'actions': ['check',], + 'result': { + 'type': 'print', + 'report': 'account.account.aged_balance', + 'state': 'end', + }, + }, + } + + def check(self, cursor, user, datas, context=None): + if not (datas['form']['term1'] < datas['form']['term2'] \ + < datas['form']['term3']): + self.raise_user_error(cursor, error="warning", + error_description="term_overlap_desc") + return {} + +OpenAgedBalance() + + +class AgedBalance(Report): + _name = 'account.account.aged_balance' + + def _get_objects(self, cursor, user, ids, model, datas, context): + import traceback + traceback.print_stack() + party_obj = self.pool.get('relationship.party') + move_line_obj = self.pool.get('account.move.line') + company_obj = self.pool.get('company.company') + company = company_obj.browse(cursor, user, + datas['form']['company'], context=context) + context['company'] = company + context['digits'] = company.currency.digits + context['fiscalyear'] = datas['form']['fiscalyear'] + line_query = move_line_obj.query_get(cursor, user, context=context) + if datas['form']['posted']: + posted_clause = "and m.state = 'posted' " + else: + posted_clause = "" + + terms = (datas['form']['term1'], + datas['form']['term2'], + datas['form']['term3']) + if datas['form']['unit'] == 'month': + coef = 30 + else: + coef = 1 + + kind = {'both': ('payable','receivable'), + 'supplier': ('payable',), + 'customer': ('receivable',), + }[datas['form']['balance_type']] + + res = {} + for position in range(len(terms)): + if position == 0: + term_query = '(l.maturity_date <= %s '\ + 'OR l.maturity_date IS NULL) ' + term_args = (datetime.date.today() + \ + datetime.timedelta(days=terms[position]*coef),) + else: + term_query = '(l.maturity_date <= %s '\ + 'AND l.maturity_date >= %s) ' + term_args = ( + datetime.date.today() + \ + datetime.timedelta(days=terms[position]*coef), + datetime.date.today() + \ + datetime.timedelta(days=terms[position-1]*coef), + ) + + cursor.execute( + 'SELECT l.party, SUM(l.debit) - SUM(l.credit) ' \ + 'FROM account_move_line l ' \ + 'JOIN account_move m on (l.move = m.id) ' + 'JOIN account_account a on (l.account = a.id) ' + 'WHERE l.party is not null '\ + 'AND a.active ' \ + 'AND a.kind in ('+ ','.join('%s' for i in kind) + ") "\ + 'AND l.reconciliation IS NULL ' \ + 'AND a.company = %s ' \ + 'AND '+ term_query+\ + 'AND ' + line_query + ' ' \ + + posted_clause + \ + 'GROUP BY l.party', + kind + (datas['form']['company'],) + term_args) + for party, solde in cursor.fetchall(): + if party in res: + res[party][position] = solde + else: + res[party] = [i == position and solde or Decimal("0.0")\ + for i in range(len(terms))] + + context['main_title'] = { + 'both': u'Suppliers and Customers', + 'supplier': u'Suppliers', + 'customer': u'Customers', + }[datas['form']['balance_type']] + unit = {'day': u'Days', 'month': u'Months'}[datas['form']['unit']] #FIXME: translation ! + for i in range(3): + context['total' + str(i)] = sum((v[i] for v in res.itervalues())) + context['title' + str(i)] = str(terms[i]) + " " + unit + + parties = party_obj.name_get( + cursor, user, [k for k in res.iterkeys()], context=context) + parties.sort(lambda x,y: cmp(x[1],y[1])) + + return ({'name': p[1], + 'amount0': res[p[0]][0], + 'amount1': res[p[0]][1], + 'amount2': res[p[0]][2], + } for p in parties) + +AgedBalance() diff --git a/account.xml b/account.xml index 282083f413..2148a78ced 100644 --- a/account.xml +++ b/account.xml @@ -713,5 +713,49 @@ id="menu_third_party_balance" icon="tryton-print" name="Third Party Balance"/> + + account.account.open_aged_balance.init + form + + + + + Aged Balance + relationship.partner + account.account.aged_balance + account/aged_balance.odt + + + Open Aged Balance + account.account.open_aged_balance + + + diff --git a/aged_balance.odt b/aged_balance.odt new file mode 100644 index 0000000000000000000000000000000000000000..6c1883c16ce6c8aa80d109c3289107b9bd7b7eb2 GIT binary patch literal 14093 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 literal 0 HcmV?d00001