From 8a643ab71c4c34e536da3020188deceb42e58317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 28 Mar 2008 17:25:14 +0100 Subject: [PATCH] Add General Legder --- account.py | 198 ++++++++++++++++++++++++++++++++++++++++++++- account.xml | 36 +++++++++ general_ledger.odt | Bin 0 -> 11464 bytes move.py | 51 ++++++++---- 4 files changed, 267 insertions(+), 18 deletions(-) create mode 100644 general_ledger.odt diff --git a/account.py b/account.py index 618ae3962a..1c334b3cb9 100644 --- a/account.py +++ b/account.py @@ -1,8 +1,9 @@ "Account" from trytond.osv import fields, OSV -from trytond.osv.orm import ID_MAX +from trytond.osv.orm import ID_MAX, exclude from trytond.wizard import Wizard, WizardOSV +from trytond.report import Report from decimal import Decimal @@ -335,11 +336,202 @@ class Partner(OSV): relation='account.account', string='Account Payable', group_name='Accounting Properties', view_load=True, domain="[('type', '=', 'payable'), ('company', '=', company)]", - required=True) + states={ + 'required': "company", + 'invisible': "not company", + }) account_receivable = fields.Property('account.account', type='many2one', relation='account.account', string='Account Receivable', group_name='Accounting Properties', view_load=True, domain="[('type', '=', 'receivable'), ('company', '=', company)]", - required=True) + states={ + 'required': "company", + 'invisible': "not company", + }) Partner() + + +class PrintGeneralLegderInit(WizardOSV): + _name = 'account.account.open_general_ledger.init' + fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year', + required=True) + #TODO add domain for start_period <= end_period + start_period = fields.Many2One('account.period', 'Start Period', + domain="[('fiscalyear', '=', fiscalyear)]") + end_period = fields.Many2One('account.period', 'End Period', + domain="[('fiscalyear', '=', fiscalyear)]") + company = fields.Many2One('company.company', 'Company', 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') + fiscalyear_id = fiscalyear_obj.find(cursor, user, exception=False, + context=context) + if fiscalyear_id: + return fiscalyear_obj.name_get(cursor, user, fiscalyear_id, + context=context)[0] + return False + + 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 + + def default_posted(self, cursor, user, context=None): + return False + +PrintGeneralLegderInit() + + +class PrintGeneralLegder(Wizard): + 'Print General Legder' + _name = 'account.account.print_general_ledger' + states = { + 'init': { + 'result': { + 'type': 'form', + 'object': 'account.account.open_general_ledger.init', + 'state': [ + ('end', 'Cancel', 'gtk-cancel'), + ('print', 'Print', 'gtk-print', True), + ], + }, + }, + 'print': { + 'result': { + 'type': 'print', + 'report': 'account.account.general_ledger', + 'state': 'end', + }, + }, + } + +PrintGeneralLegder() + + +class GeneralLegder(Report): + _name = 'account.account.general_ledger' + + def _get_objects(self, cursor, user, ids, model, context): + #Don't browse false account + return None + + def parse(self, cursor, user, content, objects, datas, context): + if context is None: + context = {} + context = context.copy() + account_obj = self.pool.get('account.account') + period_obj = self.pool.get('account.period') + company_obj = self.pool.get('company.company') + + company = company_obj.browse(cursor, user, + datas['form']['company'], context=context) + + account_ids = account_obj.search(cursor, user, [ + ('company', '=', datas['form']['company']), + ], order='code, id', context=context) + + start_period_ids = [] + if datas['form']['start_period']: + start_period = period_obj.browse(cursor, user, + datas['form']['start_period'], context=context) + start_period_ids = period_obj.search(cursor, user, [ + ('fiscalyear', '=', datas['form']['fiscalyear']), + ('end_date', '<=', start_period.start_date), + ], context=context) + if not start_period_ids: + start_period_ids = [0] + + start_context = context.copy() + start_context['fiscalyear'] = datas['form']['fiscalyear'] + start_context['periods'] = start_period_ids + start_context['posted'] = datas['form']['posted'] + start_accounts = account_obj.browse(cursor, user, account_ids, + context=start_context) + id2start_account = {} + for account in start_accounts: + id2start_account[account.id] = account + + end_period_ids = [] + if datas['form']['end_period']: + end_period = period_obj.browse(cursor, user, + datas['form']['end_period'], context=context) + end_period_ids = period_obj.search(cursor, user, [ + ('fiscalyear', '=', datas['form']['fiscalyear']), + ('end_date', '<=', end_period.start_date), + ], context=context) + end_period_ids = exclude(end_period_ids, start_period_ids) + if datas['form']['end_period'] not in end_period_ids: + end_period_ids.append(datas['form']['end_period']) + else: + end_period_ids = period_obj.search(cursor, user, [ + ('fiscalyear', '=', datas['form']['fiscalyear']), + ], context=context) + end_period_ids = exclude(end_period_ids, start_period_ids) + + end_context = context.copy() + end_context['fiscalyear'] = datas['form']['fiscalyear'] + end_context['periods'] = end_period_ids + end_context['posted'] = datas['form']['posted'] + end_accounts = account_obj.browse(cursor, user, account_ids, + context=end_context) + id2end_account = {} + for account in end_accounts: + id2end_account[account.id] = account + + periods = period_obj.browse(cursor, user, end_period_ids, + context=context) + periods.sort(lambda x, y: cmp(x.start_date, y.start_date)) + context['start_period'] = periods[0] + periods.sort(lambda x, y: cmp(x.end_date, y.end_date)) + context['end_period'] = periods[-1] + + context['accounts'] = account_obj.browse(cursor, user, account_ids, + context=context) + context['id2start_account'] = id2start_account + context['id2end_account'] = id2end_account + context['digits'] = company.currency.digits + context['lines'] = lambda account_id: self.lines(cursor, user, + account_id, end_period_ids, datas['form']['posted'], context) + context['company'] = company + + return super(GeneralLegder, self).parse(cursor, user, content, objects, + datas, context) + + def lines(self, cursor, user, account_id, period_ids, posted, context): + move_line_obj = self.pool.get('account.move.line') + period_obj = self.pool.get('account.period') + res = [] + + clause = [ + ('account', '=', account_id), + ('period', 'in', period_ids), + ('state', '!=', 'draft'), + ] + if posted: + clause.append(('move.state', '=', 'posted')) + move_line_ids = move_line_obj.search(cursor, user, clause, + context=context) + move_lines = move_line_obj.browse(cursor, user, move_line_ids, + context=context) + move_lines.sort(lambda x, y: cmp(x.date, y.date)) + + balance = Decimal('0.0') + for line in move_lines: + balance += line.debit - line.credit + res.append({ + 'date': line.date, + 'debit': line.debit, + 'credit': line.credit, + 'balance': balance, + 'name': line.name, + 'state': line.move.state, + }) + return res + +GeneralLegder() diff --git a/account.xml b/account.xml index cb52e78b1f..8f25e87574 100644 --- a/account.xml +++ b/account.xml @@ -319,5 +319,41 @@ + + + account.account.open_general_ledger.init + form + + + + + + Print General Legder + account.account.print_general_ledger + + + + + General Ledger + account.account + account.account.general_ledger + account/general_ledger.odt + diff --git a/general_ledger.odt b/general_ledger.odt new file mode 100644 index 0000000000000000000000000000000000000000..ae9c87db9708fe6e281522d136469ee9c059c1d0 GIT binary patch literal 11464 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 gjrW&blmEjuEGG#K^U@9N`9tx%n}