#This file is part of Tryton.  The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
"Account"
from trytond.model import ModelView, ModelSQL, fields
from trytond.wizard import Wizard
from trytond.report import Report
from trytond.tools import reduce_ids
from trytond.pyson import Equal, Eval, Not, PYSONEncoder, Date
from decimal import Decimal
import datetime
import time
import os


class TypeTemplate(ModelSQL, ModelView):
    'Account Type Template'
    _name = 'account.account.type.template'
    _description = __doc__
    name = fields.Char('Name', required=True, translate=True)
    parent = fields.Many2One('account.account.type.template', 'Parent',
            ondelete="RESTRICT")
    childs = fields.One2Many('account.account.type.template', 'parent', 'Children')
    sequence = fields.Integer('Sequence', required=True)
    balance_sheet = fields.Boolean('Balance Sheet')
    income_statement = fields.Boolean('Income Statement')
    display_balance = fields.Selection([
        ('debit-credit', 'Debit - Credit'),
        ('credit-debit', 'Credit - Debit'),
        ], 'Display Balance', required=True)

    def __init__(self):
        super(TypeTemplate, self).__init__()
        self._constraints += [
            ('check_recursion', 'recursive_types'),
        ]
        self._error_messages.update({
            'recursive_types': 'You can not create recursive types!',
        })
        self._order.insert(0, ('sequence', 'ASC'))

    def default_balance_sheet(self, cursor, user, context=None):
        return False

    def default_income_statement(self, cursor, user, context=None):
        return False

    def default_display_balance(self, cursor, user, context=None):
        return 'debit-credit'

    def get_rec_name(self, cursor, user, ids, name, context=None):
        if not ids:
            return {}
        res = {}
        def _name(type):
            if type.parent:
                return _name(type.parent) + '\\' + type.name
            else:
                return type.name
        for type in self.browse(cursor, user, ids, context=context):
            res[type.id] = _name(type)
        return res

    def _get_type_value(self, cursor, user, template, context=None,
            type=None):
        '''
        Set the values for account creation.

        :param cursor: the database cursor
        :param user: the user id
        :param template: the BrowseRecord of the template
        :param context: the context
        :param type: the BrowseRecord of the type to update
        :return: a dictionary with account fields as key and values as value
        '''
        res = {}
        if not type or type.name != template.name:
            res['name'] = template.name
        if not type or type.sequence != template.sequence:
            res['sequence'] = template.sequence
        if not type or type.balance_sheet != template.balance_sheet:
            res['balance_sheet'] = template.balance_sheet
        if not type or type.income_statement != template.income_statement:
            res['income_statement'] = template.income_statement
        if not type or type.display_balance != template.display_balance:
            res['display_balance'] = template.display_balance
        if not type or type.template.id != template.id:
            res['template'] = template.id
        return res

    def create_type(self, cursor, user, template, company_id, context=None,
            template2type=None, parent_id=False):
        '''
        Create recursively types based on template.

        :param cursor: the database cursor
        :param user: the user id
        :param template: the template id or the BrowseRecord of the template
                used for type creation
        :param company_id: the id of the company for which types are created
        :param context: the context
        :param template2type: a dictionary with template id as key
                and type id as value, used to convert template id
                into type. The dictionary is filled with new types
        :param parent_id: the type id of the parent of the types that must
                be created
        :return: id of the type created
        '''
        type_obj = self.pool.get('account.account.type')
        lang_obj = self.pool.get('ir.lang')

        if template2type is None:
            template2type = {}

        if isinstance(template, (int, long)):
            template = self.browse(cursor, user, template, context=context)

        if template.id not in template2type:
            vals = self._get_type_value(cursor, user, template, context=context)
            vals['company'] = company_id
            vals['parent'] = parent_id

            new_id = type_obj.create(cursor, user, vals, context=context)

            prev_lang = template._context.get('language') or 'en_US'
            prev_data = {}
            for field_name, field in template._columns.iteritems():
                if getattr(field, 'translate', False):
                    prev_data[field_name] = template[field_name]
            ctx = context.copy()
            for lang in lang_obj.get_translatable_languages(cursor, user,
                    context=context):
                if lang == prev_lang:
                    continue
                ctx['language'] = lang
                template.setLang(lang)
                data = {}
                for field_name, field in template._columns.iteritems():
                    if getattr(field, 'translate', False) \
                            and template[field_name] != prev_data[field_name]:
                        data[field_name] = template[field_name]
                if data:
                    type_obj.write(cursor, user, new_id, data, context=ctx)
            template.setLang(prev_lang)
            template2type[template.id] = new_id
        else:
            new_id = template2type[template.id]

        new_childs = []
        for child in template.childs:
            new_childs.append(self.create_type(cursor, user, child, company_id,
                context=context, template2type=template2type, parent_id=new_id))
        return new_id

TypeTemplate()


class Type(ModelSQL, ModelView):
    'Account Type'
    _name = 'account.account.type'
    _description = __doc__
    name = fields.Char('Name', size=None, required=True, translate=True)
    parent = fields.Many2One('account.account.type', 'Parent',
            ondelete="RESTRICT")
    childs = fields.One2Many('account.account.type', 'parent', 'Children')
    sequence = fields.Integer('Sequence', required=True,
            help='Use to order the account type')
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
            'get_currency_digits')
    amount = fields.Function(fields.Numeric('Amount',
        digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']),
        'get_amount')
    balance_sheet = fields.Boolean('Balance Sheet')
    income_statement = fields.Boolean('Income Statement')
    display_balance = fields.Selection([
        ('debit-credit', 'Debit - Credit'),
        ('credit-debit', 'Credit - Debit'),
        ], 'Display Balance', required=True)
    company = fields.Many2One('company.company', 'Company', required=True,
            ondelete="RESTRICT")
    template = fields.Many2One('account.account.type.template', 'Template')

    def __init__(self):
        super(Type, self).__init__()
        self._constraints += [
            ('check_recursion', 'recursive_types'),
        ]
        self._error_messages.update({
            'recursive_types': 'You can not create recursive types!',
        })
        self._order.insert(0, ('sequence', 'ASC'))

    def default_balance_sheet(self, cursor, user, context=None):
        return False

    def default_income_statement(self, cursor, user, context=None):
        return False

    def default_display_balance(self, cursor, user, context=None):
        return 'debit-credit'

    def get_currency_digits(self, cursor, user, ids, name, context=None):
        res = {}
        for type in self.browse(cursor, user, ids, context=context):
            res[type.id] = type.company.currency.digits
        return res

    def get_amount(self, cursor, user, ids, name, context=None):
        account_obj = self.pool.get('account.account')
        currency_obj = self.pool.get('currency.currency')

        if context is None:
            context = {}
        res = {}
        for type_id in ids:
            res[type_id] = Decimal('0.0')

        child_ids = self.search(cursor, user, [
            ('parent', 'child_of', ids),
            ], context=context)
        type_sum = {}
        for type_id in child_ids:
            type_sum[type_id] = Decimal('0.0')

        account_ids = account_obj.search(cursor, user, [
            ('type', 'in', child_ids),
            ], context=context)
        for account in account_obj.browse(cursor, user, account_ids,
                context=context):
            type_sum[account.type.id] += currency_obj.round(cursor, user,
                    account.company.currency, account.debit - account.credit)

        types = self.browse(cursor, user, ids, context=context)
        for type in types:
            child_ids = self.search(cursor, user, [
                ('parent', 'child_of', [type.id]),
                ], context=context)
            for child_id in child_ids:
                res[type.id] += type_sum[child_id]
            res[type.id] = currency_obj.round(cursor, user,
                        type.company.currency, res[type.id])
            if type.display_balance == 'credit-debit':
                res[type.id] = - res[type.id]
        return res

    def get_rec_name(self, cursor, user, ids, name, context=None):
        if not ids:
            return {}
        res = {}
        def _name(type):
            if type.parent:
                return _name(type.parent) + '\\' + type.name
            else:
                return type.name
        for type in self.browse(cursor, user, ids, context=context):
            res[type.id] = _name(type)
        return res

    def delete(self, cursor, user, ids, context=None):
        if isinstance(ids, (int, long)):
            ids = [ids]
        type_ids = self.search(cursor, user, [
            ('parent', 'child_of', ids),
            ], context=context)
        return super(Type, self).delete(cursor, user, type_ids, context=context)

    def update_type(self, cursor, user, type, context=None, template2type=None):
        '''
        Update recursively types based on template.

        :param cursor: the database cursor
        :param user: the user id
        :param type: a type id or the BrowseRecord of the type
        :param context: the context
        :param template2type: a dictionary with template id as key
                and type id as value, used to convert template id
                into type. The dictionary is filled with new types
        '''
        template_obj = self.pool.get('account.account.type.template')
        lang_obj = self.pool.get('ir.lang')

        if template2type is None:
            template2type = {}

        if isinstance(type, (int, long)):
            type = self.browse(cursor, user, type, context=context)

        if type.template:
            vals = template_obj._get_type_value(cursor, user, type.template,
                    context=context, type=type)
            if vals:
                self.write(cursor, user, type.id, vals, context=context)

            prev_lang = type._context.get('language') or 'en_US'
            prev_data = {}
            for field_name, field in type.template._columns.iteritems():
                if getattr(field, 'translate', False):
                    prev_data[field_name] = type.template[field_name]
            ctx = context.copy()
            for lang in lang_obj.get_translatable_languages(cursor, user,
                    context=context):
                if lang == prev_lang:
                    continue
                ctx['language'] = lang
                type.setLang(lang)
                data = {}
                for field_name, field in type.template._columns.iteritems():
                    if (getattr(field, 'translate', False)
                            and type.template[field_name] !=
                            prev_data[field_name]):
                        data[field_name] = type.template[field_name]
                if data:
                    self.write(cursor, user, type.id, data, context=ctx)
            type.setLang(prev_lang)
            template2type[type.template.id] = type.id

        for child in type.childs:
            self.update_type(cursor, user, child, context=context,
                    template2type=template2type)

Type()


class AccountTemplate(ModelSQL, ModelView):
    'Account Template'
    _name = 'account.account.template'
    _description = __doc__

    name = fields.Char('Name', size=None, required=True, translate=True,
            select=1)
    code = fields.Char('Code', size=None, select=1)
    type = fields.Many2One('account.account.type.template', 'Type',
            ondelete="RESTRICT",
            states={
                'invisible': Equal(Eval('kind'), 'view'),
                'required': Not(Equal(Eval('kind'), 'view')),
            }, depends=['kind'])
    parent = fields.Many2One('account.account.template', 'Parent', select=1,
            ondelete="RESTRICT")
    childs = fields.One2Many('account.account.template', 'parent', 'Children')
    reconcile = fields.Boolean('Reconcile',
            states={
                'invisible': Equal(Eval('kind'), 'view'),
            }, depends=['kind'])
    kind = fields.Selection([
        ('other', 'Other'),
        ('payable', 'Payable'),
        ('revenue', 'Revenue'),
        ('receivable', 'Receivable'),
        ('expense', 'Expense'),
        ('view', 'View'),
        ], 'Kind', required=True)
    deferral = fields.Boolean('Deferral', states={
        'invisible': Equal(Eval('kind'), 'view'),
        }, depends=['kind'])

    def __init__(self):
        super(AccountTemplate, self).__init__()
        self._constraints += [
            ('check_recursion', 'recursive_accounts'),
        ]
        self._error_messages.update({
            'recursive_accounts': 'You can not create recursive accounts!',
        })
        self._order.insert(0, ('code', 'ASC'))
        self._order.insert(1, ('name', 'ASC'))

    def default_kind(self, cursor, user, context=None):
        return 'view'

    def default_reconcile(self, cursor, user, context=None):
        return False

    def default_deferral(self, cursor, user, context=None):
        return True

    def get_rec_name(self, cursor, user, ids, name, context=None):
        if not ids:
            return {}
        res = {}
        for template in self.browse(cursor, user, ids, context=context):
            if template.code:
                res[template.id] = template.code + ' - ' + template.name
            else:
                res[template.id] = template.name
        return res

    def search_rec_name(self, cursor, user, name, clause, context=None):
        ids = self.search(cursor, user, [
            ('code',) + clause[1:],
            ], limit=1, context=context)
        if ids:
            return [('code',) + clause[1:]]
        return [(self._rec_name,) + clause[1:]]

    def _get_account_value(self, cursor, user, template, context=None,
            account=None):
        '''
        Set the values for account creation.

        :param cursor: the database cursor
        :param user: the user id
        :param template: the BrowseRecord of the template
        :param context: the context
        :param account: the BrowseRecord of the account to update
        :return: a dictionary with account fields as key and values as value
        '''
        res = {}
        if not account or account.name != template.name:
            res['name'] = template.name
        if not account or account.code != template.code:
            res['code'] = template.code
        if not account or account.kind != template.kind:
            res['kind'] = template.kind
        if not account or account.reconcile != template.reconcile:
            res['reconcile'] = template.reconcile
        if not account or account.deferral != template.deferral:
            res['deferral'] = template.deferral
        if not account or account.template.id != template.id:
            res['template'] = template.id
        return res

    def create_account(self, cursor, user, template, company_id, context=None,
            template2account=None, template2type=None, parent_id=False):
        '''
        Create recursively accounts based on template.

        :param cursor: the database cursor
        :param user: the user id
        :param template: the template id or the BrowseRecord of template
                used for account creation
        :param company_id: the id of the company for which accounts
                are created
        :param context: the context
        :param template2account: a dictionary with template id as key
                and account id as value, used to convert template id
                into account. The dictionary is filled with new accounts
        :param template2type: a dictionary with type template id as key
                and type id as value, used to convert type template id
                into type.
        :param parent_id: the account id of the parent of the accounts
                that must be created
        :return: id of the account created
        '''
        account_obj = self.pool.get('account.account')
        lang_obj = self.pool.get('ir.lang')

        if template2account is None:
            template2account = {}

        if template2type is None:
            template2type = {}

        if isinstance(template, (int, long)):
            template = self.browse(cursor, user, template, context=context)

        if template.id not in template2account:
            vals = self._get_account_value(cursor, user, template, context=context)
            vals['company'] = company_id
            vals['parent'] = parent_id
            vals['type'] = template2type.get(template.type.id, False)

            new_id = account_obj.create(cursor, user, vals, context=context)

            prev_lang = template._context.get('language') or 'en_US'
            prev_data = {}
            for field_name, field in template._columns.iteritems():
                if getattr(field, 'translate', False):
                    prev_data[field_name] = template[field_name]
            ctx = context.copy()
            for lang in lang_obj.get_translatable_languages(cursor, user,
                    context=context):
                if lang == prev_lang:
                    continue
                ctx['language'] = lang
                template.setLang(lang)
                data = {}
                for field_name, field in template._columns.iteritems():
                    if getattr(field, 'translate', False) \
                            and template[field_name] != prev_data[field_name]:
                        data[field_name] = template[field_name]
                if data:
                    account_obj.write(cursor, user, new_id, data, context=ctx)
            template.setLang(prev_lang)
            template2account[template.id] = new_id
        else:
            new_id = template2account[template.id]

        new_childs = []
        for child in template.childs:
            new_childs.append(self.create_account(cursor, user, child,
                company_id, context=context, template2account=template2account,
                template2type=template2type, parent_id=new_id))
        return new_id

    def update_account_taxes(self, cursor, user, template, template2account,
            template2tax, context=None, template_done=None):
        '''
        Update recursively account taxes based on template.

        :param cursor: the database cursor
        :param user: the user id
        :param template: the template id or the BrowseRecord of template
            used for account creation
        :param template2account: a dictionary with template id as key
            and account id as value, used to convert template id into
            account.
        :param template2tax: a dictionary with tax template id as key
            and tax id as value, used to convert tax template id into
            tax.
        :param context: the context
        :param template_done: a list of template id already updated.
            The list is filled.
        '''
        account_obj = self.pool.get('account.account')

        if template2account is None:
            template2account = {}
        if template2tax is None:
            template2tax = {}
        if template_done is None:
            template_done = []

        if isinstance(template, (int, long)):
            template = self.browse(cursor, user, template, context=context)

        if template.id not in template_done:
            if template.taxes:
                account_obj.write(cursor, user, template2account[template.id], {
                    'taxes': [
                        ('add', template2tax[x.id]) for x in template.taxes],
                    }, context=context)
            template_done.append(template.id)

        for child in template.childs:
            self.update_account_taxes(cursor, user, child, template2account,
                    template2tax, context=context, template_done=template_done)

AccountTemplate()


class Account(ModelSQL, ModelView):
    'Account'
    _name = 'account.account'
    _description = __doc__

    name = fields.Char('Name', size=None, required=True, translate=True,
            select=1)
    code = fields.Char('Code', size=None, select=1)
    active = fields.Boolean('Active', select=2)
    company = fields.Many2One('company.company', 'Company', required=True,
            ondelete="RESTRICT")
    currency = fields.Function(fields.Many2One('currency.currency',
        'Currency'), 'get_currency')
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
            'get_currency_digits')
    second_currency = fields.Many2One('currency.currency', 'Secondary currency',
            help='Force all moves for this account \n' \
                    'to have this secondary currency.', ondelete="RESTRICT")
    type = fields.Many2One('account.account.type', 'Type', ondelete="RESTRICT",
            states={
                'invisible': Equal(Eval('kind'), 'view'),
                'required': Not(Equal(Eval('kind'), 'view')),
            }, depends=['kind'])
    parent = fields.Many2One('account.account', 'Parent', select=1,
            left="left", right="right", ondelete="RESTRICT")
    left = fields.Integer('Left', required=True, select=1)
    right = fields.Integer('Right', required=True, select=1)
    childs = fields.One2Many('account.account', 'parent', 'Children')
    balance = fields.Function(fields.Numeric('Balance',
        digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']),
        'get_balance')
    credit = fields.Function(fields.Numeric('Credit',
        digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']),
        'get_credit_debit')
    debit = fields.Function(fields.Numeric('Debit',
        digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']),
        'get_credit_debit')
    reconcile = fields.Boolean('Reconcile',
            help='Allow move lines of this account \n' \
                    'to be reconciled.',
            states={
                'invisible': Equal(Eval('kind'), 'view'),
            }, depends=['kind'])
    note = fields.Text('Note')
    kind = fields.Selection([
        ('other', 'Other'),
        ('payable', 'Payable'),
        ('revenue', 'Revenue'),
        ('receivable', 'Receivable'),
        ('expense', 'Expense'),
        ('view', 'View'),
        ], 'Kind', required=True)
    deferral = fields.Boolean('Deferral', states={
        'invisible': Equal(Eval('kind'), 'view'),
        }, depends=['kind'])
    deferrals = fields.One2Many('account.account.deferral', 'account',
            'Deferrals', readonly=True, states={
                'invisible': Equal(Eval('kind'), 'view'),
            }, depends=['kind'])
    template = fields.Many2One('account.account.template', 'Template')

    def __init__(self):
        super(Account, self).__init__()
        self._constraints += [
            ('check_recursion', 'recursive_accounts'),
        ]
        self._error_messages.update({
            'recursive_accounts': 'You can not create recursive accounts!',
            'delete_account_containing_move_lines': 'You can not delete ' \
                    'accounts containing move lines!',
        })
        self._sql_error_messages.update({
            'parent_fkey': 'You can not delete accounts ' \
                    'that have children!',
        })
        self._order.insert(0, ('code', 'ASC'))
        self._order.insert(1, ('name', 'ASC'))

    def default_left(self, cursor, user, context=None):
        return 0

    def default_right(self, cursor, user, context=None):
        return 0

    def default_active(self, cursor, user, context=None):
        return True

    def default_company(self, cursor, user, context=None):
        if context is None:
            context = {}
        if context.get('company'):
            return context['company']
        return False

    def default_reconcile(self, cursor, user, context=None):
        return False

    def default_deferral(self, cursor, user, context=None):
        return True

    def default_kind(self, cursor, user, context=None):
        return 'view'

    def get_currency(self, cursor, user, ids, name, context=None):
        currency_obj = self.pool.get('currency.currency')
        res = {}
        for account in self.browse(cursor, user, ids, context=context):
            res[account.id] = account.company.currency.id
        return res

    def get_currency_digits(self, cursor, user, ids, name, context=None):
        res = {}
        for account in self.browse(cursor, user, ids, context=context):
            res[account.id] = account.company.currency.digits
        return res

    def get_balance(self, cursor, user, ids, name, context=None):
        res = {}
        company_obj = self.pool.get('company.company')
        currency_obj = self.pool.get('currency.currency')
        move_line_obj = self.pool.get('account.move.line')
        fiscalyear_obj = self.pool.get('account.fiscalyear')
        deferral_obj = self.pool.get('account.account.deferral')

        if context is None:
            context = {}

        query_ids, args_ids = self.search(cursor, user, [
            ('parent', 'child_of', ids),
            ], context=context, query_string=True)
        line_query, fiscalyear_ids = move_line_obj.query_get(cursor, user,
                context=context)
        cursor.execute('SELECT a.id, ' \
                    'SUM((COALESCE(l.debit, 0) - COALESCE(l.credit, 0))) ' \
                'FROM account_account a ' \
                    'LEFT JOIN account_move_line l ' \
                    'ON (a.id = l.account) ' \
                'WHERE a.kind != \'view\' ' \
                    'AND a.id IN (' + query_ids + ') ' \
                    'AND ' + line_query + ' ' \
                    'AND a.active ' \
                'GROUP BY a.id', args_ids)
        account_sum = {}
        for account_id, sum in cursor.fetchall():
            # SQLite uses float for SUM
            if not isinstance(sum, Decimal):
                sum = Decimal(str(sum))
            account_sum[account_id] = sum

        account2company = {}
        id2company = {}
        id2account = {}
        all_ids = self.search(cursor, user, [('parent', 'child_of', ids)],
                context=context)
        accounts = self.browse(cursor, user, all_ids, context=context)
        for account in accounts:
            account2company[account.id] = account.company.id
            id2company[account.company.id] = account.company
            id2account[account.id] = account

        for account_id in ids:
            res.setdefault(account_id, Decimal('0.0'))
            child_ids = self.search(cursor, user, [
                ('parent', 'child_of', [account_id]),
                ], context=context)
            company_id = account2company[account_id]
            to_currency = id2company[company_id].currency
            for child_id in child_ids:
                child_company_id = account2company[child_id]
                from_currency = id2company[child_company_id].currency
                res[account_id] += currency_obj.compute(cursor, user,
                        from_currency, account_sum.get(child_id, Decimal('0.0')),
                        to_currency, round=True, context=context)

        youngest_fiscalyear = None
        for fiscalyear in fiscalyear_obj.browse(cursor, user, fiscalyear_ids,
                context=context):
            if not youngest_fiscalyear \
                    or youngest_fiscalyear.start_date > fiscalyear.start_date:
                youngest_fiscalyear = fiscalyear

        fiscalyear = None
        if youngest_fiscalyear:
            fiscalyear_ids = fiscalyear_obj.search(cursor, user, [
                ('end_date', '<=', youngest_fiscalyear.start_date),
                ('company', '=', youngest_fiscalyear.company),
                ], order=[('end_date', 'DESC')], limit=1, context=context)
            if fiscalyear_ids:
                fiscalyear = fiscalyear_obj.browse(cursor, user,
                        fiscalyear_ids[0], context=context)

        if fiscalyear:
            if fiscalyear.state == 'close':
                deferral_ids = deferral_obj.search(cursor, user, [
                    ('fiscalyear', '=', fiscalyear.id),
                    ('account', 'in', ids),
                    ], context=context)
                id2deferral = {}
                for deferral in deferral_obj.browse(cursor, user, deferral_ids,
                        context=context):
                    id2deferral[deferral.account.id] = deferral

                for account_id in ids:
                    if account_id in id2deferral:
                        deferral = id2deferral[account_id]
                        res[account_id] += deferral.debit - deferral.credit
            else:
                ctx = context.copy()
                ctx['fiscalyear'] = fiscalyear.id
                if 'date' in context:
                    del context['date']
                res2 = self.get_balance(cursor, user, ids, name, context=ctx)
                for account_id in ids:
                    res[account_id] += res2[account_id]

        for account_id in ids:
            company_id = account2company[account_id]
            to_currency = id2company[company_id].currency
            res[account_id] = currency_obj.round(cursor, user, to_currency,
                    res[account_id])
        return res

    def get_credit_debit(self, cursor, user, ids, names, context=None):
        '''
        Function to compute debit, credit for account ids.

        :param cursor: the database cursor
        :param user: the user id
        :param ids: the ids of the account
        :param names: the list of field name to compute
        :param context: the context
        :return: a dictionary with all field names as key and
            a dictionary as value with id as key
        '''
        res = {}
        move_line_obj = self.pool.get('account.move.line')
        company_obj = self.pool.get('company.company')
        currency_obj = self.pool.get('currency.currency')
        fiscalyear_obj = self.pool.get('account.fiscalyear')
        deferral_obj = self.pool.get('account.account.deferral')

        if context is None:
            context = {}

        for name in names:
            if name not in ('credit', 'debit'):
                raise Exception('Bad argument')
            res[name] = {}

        line_query, fiscalyear_ids = move_line_obj.query_get(cursor, user,
                context=context)
        for i in range(0, len(ids), cursor.IN_MAX):
            sub_ids = ids[i:i + cursor.IN_MAX]
            red_sql, red_ids = reduce_ids('a.id', sub_ids)
            cursor.execute('SELECT a.id, ' + \
                        ','.join('SUM(COALESCE(l.' + name + ', 0))'
                            for name in names) + ' ' \
                    'FROM account_account a ' \
                        'LEFT JOIN account_move_line l ' \
                        'ON (a.id = l.account) ' \
                    'WHERE a.kind != \'view\' ' \
                        'AND ' + red_sql + ' ' \
                        'AND ' + line_query + ' ' \
                        'AND a.active ' \
                    'GROUP BY a.id', red_ids)
            for row in cursor.fetchall():
                account_id = row[0]
                for i in range(len(names)):
                    # SQLite uses float for SUM
                    if not isinstance(row[i + 1], Decimal):
                        res[names[i]][account_id] = Decimal(str(row[i + 1]))
                    else:
                        res[names[i]][account_id] = row[i + 1]

        account2company = {}
        id2company = {}
        accounts = self.browse(cursor, user, ids, context=context)
        for account in accounts:
            account2company[account.id] = account.company.id
            id2company[account.company.id] = account.company

        for account_id in ids:
            for name in names:
                res[name].setdefault(account_id, Decimal('0.0'))

        youngest_fiscalyear = None
        for fiscalyear in fiscalyear_obj.browse(cursor, user, fiscalyear_ids,
                context=context):
            if not youngest_fiscalyear \
                    or youngest_fiscalyear.start_date > fiscalyear.start_date:
                youngest_fiscalyear = fiscalyear

        fiscalyear = None
        if youngest_fiscalyear:
            fiscalyear_ids = fiscalyear_obj.search(cursor, user, [
                ('end_date', '<=', youngest_fiscalyear.start_date),
                ('company', '=', youngest_fiscalyear.company),
                ], order=[('end_date', 'DESC')], limit=1, context=context)
            if fiscalyear_ids:
                fiscalyear = fiscalyear_obj.browse(cursor, user,
                        fiscalyear_ids[0], context=context)

        if fiscalyear:
            if fiscalyear.state == 'close':
                deferral_ids = deferral_obj.search(cursor, user, [
                    ('fiscalyear', '=', fiscalyear.id),
                    ('account', 'in', ids),
                    ], context=context)
                id2deferral = {}
                for deferral in deferral_obj.browse(cursor, user, deferral_ids,
                        context=context):
                    id2deferral[deferral.account.id] = deferral

                for account_id in ids:
                    if account_id in id2deferral:
                        deferral = id2deferral[account_id]
                        for name in names:
                            res[name][account_id] += deferral[name]
            else:
                ctx = context.copy()
                ctx['fiscalyear'] = fiscalyear.id
                if 'date' in ctx:
                    del ctx['date']
                res2 = self.get_credit_debit(cursor, user, ids, names,
                        context=ctx)
                for account_id in ids:
                    for name in names:
                        res[name][account_id] += res2[name][account_id]

        for account_id in ids:
            company_id = account2company[account_id]
            currency = id2company[company_id].currency
            for name in names:
                res[name][account_id] = currency_obj.round(cursor, user,
                        currency, res[name][account_id])
        return res

    def get_rec_name(self, cursor, user, ids, name, context=None):
        if not ids:
            return {}
        res = {}
        for account in self.browse(cursor, user, ids, context=context):
            if account.code:
                res[account.id] = account.code + ' - ' + account.name
            else:
                res[account.id] = account.name
        return res

    def search_rec_name(self, cursor, user, name, clause, context=None):
        ids = self.search(cursor, user, [
            ('code',) + clause[1:],
            ], limit=1, context=context)
        if ids:
            return [('code',) + clause[1:]]
        return [(self._rec_name,) + clause[1:]]

    def copy(self, cursor, user, ids, default=None, context=None):
        if default is None:
            default = {}
        default['left'] = 0
        default['right'] = 0
        res = super(Account, self).copy(cursor, user, ids, default=default,
                context=context)
        self._rebuild_tree(cursor, user, 'parent', False, 0)
        return res

    def write(self, cursor, user, ids, vals, context=None):
        if not vals.get('active', True):
            move_line_obj = self.pool.get('account.move.line')
            account_ids = self.search(cursor, user, [
                ('parent', 'child_of', ids),
                ], context=context)
            if move_line_obj.search(cursor, user, [
                ('account', 'in', account_ids),
                ], context=context):
                vals = vals.copy()
                del vals['active']
        return super(Account, self).write(cursor, user, ids, vals,
                context=context)

    def delete(self, cursor, user, ids, context=None):
        move_line_obj = self.pool.get('account.move.line')
        if isinstance(ids, (int, long)):
            ids = [ids]
        account_ids = self.search(cursor, user, [
            ('parent', 'child_of', ids),
            ], context=context)
        if move_line_obj.search(cursor, user, [
            ('account', 'in', account_ids),
            ], context=context):
            self.raise_user_error(cursor,
                    'delete_account_containing_move_lines', context=context)
        return super(Account, self).delete(cursor, user, account_ids,
                context=context)

    def update_account(self, cursor, user, account, context=None,
            template2account=None, template2type=None):
        '''
        Update recursively accounts based on template.

        :param cursor: the database cursor
        :param user: the user id
        :param account: an account id or the BrowseRecord of the account
        :param context: the context
        :param template2account: a dictionary with template id as key
                and account id as value, used to convert template id
                into account. The dictionary is filled with new accounts
        :param template2type: a dictionary with type template id as key
                and type id as value, used to convert type template id
                into type.
        '''
        template_obj = self.pool.get('account.account.template')
        lang_obj = self.pool.get('ir.lang')

        if template2account is None:
            template2account = {}

        if template2type is None:
            template2type = {}

        if isinstance(account, (int, long)):
            account = self.browse(cursor, user, account, context=context)

        if account.template:
            vals = template_obj._get_account_value(cursor, user,
                    account.template, context=context, account=account)
            if account.type.id != template2type.get(account.template.type.id,
                    False):
                vals['type'] = template2type.get(account.template.type.id,
                        False)
            if vals:
                self.write(cursor, user, account.id, vals, context=context)

            prev_lang = account._context.get('language') or 'en_US'
            prev_data = {}
            for field_name, field in account.template._columns.iteritems():
                if getattr(field, 'translate', False):
                    prev_data[field_name] = account.template[field_name]
            ctx = context.copy()
            for lang in lang_obj.get_translatable_languages(cursor, user,
                    context=context):
                if lang == prev_lang:
                    continue
                ctx['language'] = lang
                account.setLang(lang)
                data = {}
                for field_name, field in account.template._columns.iteritems():
                    if (getattr(field, 'translate', False)
                            and account.template[field_name] !=
                            prev_data[field_name]):
                        data[field_name] = account.template[field_name]
                if data:
                    self.write(cursor, user, account.id, data, context=ctx)
            account.setLang(prev_lang)
            template2account[account.template.id] = account.id

        for child in account.childs:
            self.update_account(cursor, user, child, context=context,
                    template2account=template2account,
                    template2type=template2type)

    def update_account_taxes(self, cursor, user, account, template2account,
            template2tax, context=None):
        '''
        Update recursively account taxes base on template.

        :param cursor: the database cursor
        :param user: the user id
        :param account: the account id or the BrowseRecord of account
        :param template2account: a dictionary with template id as key
            and account id as value, used to convert template id into
            account.
        :param template2tax: a dictionary with tax template id as key
            and tax id as value, used to convert tax template id into
            tax.
        :param context: the context
        '''
        if template2account is None:
            template2account = {}

        if template2tax is None:
            template2tax = {}

        if isinstance(account, (int, long)):
            account = self.browse(cursor, user, account, context=context)

        if account.template:
            if account.template.taxes:
                tax_ids = [template2tax[x.id] for x in account.template.taxes
                        if x.id in template2tax]
                old_tax_ids = [x.id for x in account.taxes]
                for tax_id in tax_ids:
                    if tax_id not in old_tax_ids:
                        self.write(cursor, user, account.id, {
                            'taxes': [
                                ('add', template2tax[x.id]) \
                                        for x in account.template.taxes \
                                        if x.id in template2tax],
                            }, context=context)
                        break

        for child in account.childs:
            self.update_account_taxes(cursor, user, child, template2account,
                    template2tax, context=context)

Account()


class AccountDeferral(ModelSQL, ModelView):
    '''
    Account Deferral

    It is used to deferral the debit/credit of account by fiscal year.
    '''
    _name = 'account.account.deferral'
    _description = 'Account Deferral'

    account = fields.Many2One('account.account', 'Account', required=True,
            select=1)
    fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
            required=True, select=1)
    debit = fields.Numeric('Debit', digits=(16, Eval('currency_digits', 2)),
            depends=['currency_digits'])
    credit = fields.Numeric('Credit', digits=(16, Eval('currency_digits', 2)),
            depends=['currency_digits'])
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
            'get_currency_digits')

    def __init__(self):
        super(AccountDeferral, self).__init__()
        self._sql_constraints += [
            ('deferral_uniq', 'UNIQUE(account, fiscalyear)',
                'Deferral must be unique by account and fiscal year'),
        ]
        self._error_messages.update({
            'write_deferral': 'You can not modify Account Deferral records',
            })

    def get_currency_digits(self, cursor, user, ids, name, context=None):
        res = {}
        for deferral in self.browse(cursor, user, ids, context=context):
            res[deferral.id] = deferral.account.currency_digits
        return res

    def get_rec_name(self, cursor, user, ids, name, context=None):
        if not ids:
            return {}
        res = {}
        for deferral in self.browse(cursor, user, ids, context=context):
            res[deferral.id] = deferral.account.rec_name + ' - ' + \
                    deferral.fiscalyear.rec_name
        return res

    def search_rec_name(self, cursor, user, name, clause, context=None):
        ids = self.search(cursor, user, ['OR',
            ('account.rec_name',) + clause[1:],
            ('fiscalyear.rec_name',) + clause[1:],
            ], context=context)
        return [('id', 'in', ids)]

    def write(self, cursor, user, ids, vals, context=None):
        self.raise_user_error(cursor, 'write_deferral', context=context)

AccountDeferral()


class OpenChartAccountInit(ModelView):
    'Open Chart Account Init'
    _name = 'account.account.open_chart_account.init'
    _description = __doc__
    fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
            help='Leave empty for all open fiscal year')
    posted = fields.Boolean('Posted Move', help='Show only posted move')

    def default_posted(self, cursor, user, context=None):
        return False

OpenChartAccountInit()


class OpenChartAccount(Wizard):
    'Open Chart Of Account'
    _name = 'account.account.open_chart_account'
    states = {
        'init': {
            'result': {
                'type': 'form',
                'object': 'account.account.open_chart_account.init',
                'state': [
                    ('end', 'Cancel', 'tryton-cancel'),
                    ('open', 'Open', 'tryton-ok', True),
                ],
            },
        },
        'open': {
            'result': {
                'type': 'action',
                'action': '_action_open_chart',
                'state': 'end',
            },
        },
    }

    def _action_open_chart(self, cursor, user, data, context=None):
        model_data_obj = self.pool.get('ir.model.data')
        act_window_obj = self.pool.get('ir.action.act_window')
        act_window_id = model_data_obj.get_id(cursor, user, 'account',
                'act_account_tree2', context=context)
        res = act_window_obj.read(cursor, user, act_window_id, context=context)
        res['pyson_context'] = PYSONEncoder().encode({
            'fiscalyear': data['form']['fiscalyear'],
            'posted': data['form']['posted'],
            })
        return res

OpenChartAccount()


class PrintGeneralLegderInit(ModelView):
    'Print General Ledger'
    _name = 'account.account.print_general_ledger.init'
    _description = __doc__
    fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
            required=True, on_change=['fiscalyear'])
    start_period = fields.Many2One('account.period', 'Start Period',
            domain=[
                ('fiscalyear', '=', Eval('fiscalyear')),
                ('start_date', '<=', (Eval('end_period'), 'start_date')),
            ])
    end_period = fields.Many2One('account.period', 'End Period',
            domain=[
                ('fiscalyear', '=', Eval('fiscalyear')),
                ('start_date', '>=', (Eval('start_period'), 'start_date'))
            ])
    company = fields.Many2One('company.company', 'Company', required=True)
    posted = fields.Boolean('Posted Move', help='Show only posted move')
    empty_account = fields.Boolean('Empty Account',
            help='With account without 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_id
        return False

    def default_company(self, cursor, user, context=None):
        if context is None:
            context = {}
        if context.get('company'):
            return context['company']
        return False

    def default_posted(self, cursor, user, context=None):
        return False

    def default_empty_account(self, cursor, user, context=None):
        return False

    def on_change_fiscalyear(self, cursor, user, vals, context=None):
        return {
            'start_period': False,
            'end_period': False,
        }

PrintGeneralLegderInit()


class PrintGeneralLegder(Wizard):
    'Print General Legder'
    _name = 'account.account.print_general_ledger'
    states = {
        'init': {
            'result': {
                'type': 'form',
                'object': 'account.account.print_general_ledger.init',
                'state': [
                    ('end', 'Cancel', 'tryton-cancel'),
                    ('print', 'Print', 'tryton-print', True),
                ],
            },
        },
        'print': {
            'result': {
                'type': 'print',
                'report': 'account.account.general_ledger',
                'state': 'end',
            },
        },
    }

PrintGeneralLegder()


class GeneralLegder(Report):
    _name = 'account.account.general_ledger'

    def parse(self, cursor, user, report, 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']),
            ('kind', '!=', 'view'),
            ], order=[('code', 'ASC'), ('id', 'ASC')], context=context)

        start_period_ids = [0]
        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)

        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)
            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_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]

        if not datas['form']['empty_account']:
            account_id2lines = self.get_lines(cursor, user, account_ids,
                    end_period_ids, datas['form']['posted'], context)
            account_ids = account_id2lines.keys()
            account_id2lines = None

        account_id2lines = self.lines(cursor, user, account_ids,
                list(set(end_period_ids).difference(
                set(start_period_ids))), datas['form']['posted'], context)

        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: account_id2lines[account_id]
        context['company'] = company

        return super(GeneralLegder, self).parse(cursor, user, report, objects,
                datas, context)

    def get_lines(self, cursor, user, account_ids, period_ids, posted,
            context):
        move_line_obj = self.pool.get('account.move.line')
        clause = [
            ('account', 'in', account_ids),
            ('period', 'in', period_ids),
            ('state', '!=', 'draft'),
            ]
        if posted:
            clause.append(('move.state', '=', 'posted'))
        move_ids = move_line_obj.search(cursor, user, clause,
                order=[], context=context)
        res = {}
        for move in move_line_obj.browse(cursor, user, move_ids,
                context=context):
            res.setdefault(move.account.id, []).append(move)
        return res

    def lines(self, cursor, user, account_ids, period_ids, posted, context):
        move_line_obj = self.pool.get('account.move.line')
        move_obj = self.pool.get('account.move')
        res = dict((account_id, []) for account_id in account_ids)
        account_id2lines = self.get_lines(cursor, user, account_ids,
                period_ids, posted, context)

        state_selections = dict(move_obj.fields_get(cursor, user,
                fields_names=['state'], context=context)['state']['selection'])

        for account_id, lines in account_id2lines.iteritems():
            lines.sort(lambda x, y: cmp(x.date, y.date))
            balance = Decimal('0.0')
            for line in lines:
                balance += line.debit - line.credit
                res[account_id].append({
                    'date': line.date,
                    'debit': line.debit,
                    'credit': line.credit,
                    'balance': balance,
                    'name': line.name,
                    'state': state_selections.get(line.move.state, line.move.state),
                    })
        return res

GeneralLegder()


class PrintTrialBalanceInit(ModelView):
    'Print Trial Balance Init'
    _name = 'account.account.print_trial_balance.init'
    _description = __doc__
    fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
            required=True, on_change=['fiscalyear'],
            depends=['start_period', 'end_period'])
    start_period = fields.Many2One('account.period', 'Start Period',
            domain=[
                ('fiscalyear', '=', Eval('fiscalyear')),
                ('start_date', '<=', (Eval('end_period'), 'start_date'))
            ],
            depends=['end_period'])
    end_period = fields.Many2One('account.period', 'End Period',
            domain=[
                ('fiscalyear', '=', Eval('fiscalyear')),
                ('start_date', '>=', (Eval('start_period'), 'start_date'))
            ],
            depends=['start_period'])
    company = fields.Many2One('company.company', 'Company', required=True)
    posted = fields.Boolean('Posted Move', help='Show only posted move')
    empty_account = fields.Boolean('Empty Account',
            help='With account without 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_id
        return False

    def default_company(self, cursor, user, context=None):
        if context is None:
            context = {}
        if context.get('company'):
            return context['company']
        return False

    def default_posted(self, cursor, user, context=None):
        return False

    def default_empty_account(self, cursor, user, context=None):
        return False

    def on_change_fiscalyear(self, cursor, user, vals, context=None):
        return {
            'start_period': False,
            'end_period': False,
        }

PrintTrialBalanceInit()


class PrintTrialBalance(Wizard):
    'Print Trial Balance'
    _name = 'account.account.print_trial_balance'
    states = {
        'init': {
            'result': {
                'type': 'form',
                'object': 'account.account.print_trial_balance.init',
                'state': [
                    ('end', 'Cancel', 'tryton-cancel'),
                    ('print', 'Print', 'tryton-print', True),
                ],
            },
        },
        'print': {
            'result': {
                'type': 'print',
                'report': 'account.account.trial_balance',
                'state': 'end',
            },
        },
    }

PrintTrialBalance()


class TrialBalance(Report):
    _name = 'account.account.trial_balance'

    def parse(self, cursor, user, report, 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']),
            ('kind', '!=', 'view'),
            ], context=context)

        start_period_ids = [0]
        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)

        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 = list(set(end_period_ids).difference(
                set(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 = list(set(end_period_ids).difference(
                set(start_period_ids)))

        ctx = context.copy()
        ctx['fiscalyear'] = datas['form']['fiscalyear']
        ctx['periods'] = end_period_ids
        ctx['posted'] = datas['form']['posted']
        accounts = account_obj.browse(cursor, user, account_ids,
                context=ctx)

        if not datas['form']['empty_account']:
            to_remove = []
            for account in accounts:
                if account.debit == Decimal('0.0') \
                        and account.credit == Decimal('0.0'):
                    to_remove.append(account)
            for account in to_remove:
                accounts.remove(account)

        periods = period_obj.browse(cursor, user, end_period_ids,
                context=context)

        context['accounts'] = accounts
        context['start_period'] = periods[0]
        context['end_period'] = periods[-1]
        context['company'] = company
        context['digits'] = company.currency.digits
        context['sum'] = lambda accounts, field: self.sum(cursor, user,
                accounts, field, context)

        return super(TrialBalance, self).parse(cursor, user, report, objects,
                datas, context)

    def sum(self, cursor, user, accounts, field, context):
        amount = Decimal('0.0')
        for account in accounts:
            amount += account[field]
        return amount

TrialBalance()


class OpenBalanceSheetInit(ModelView):
    'Open Balance Sheet Init'
    _name = 'account.account.open_balance_sheet.init'
    _description = __doc__
    date = fields.Date('Date', required=True)
    company = fields.Many2One('company.company', 'Company', required=True)
    posted = fields.Boolean('Posted Move', help='Show only posted move')

    def default_date(self, cursor, user, context=None):
        date_obj = self.pool.get('ir.date')
        return date_obj.today(cursor, user, context=context)

    def default_company(self, cursor, user, context=None):
        if context is None:
            context = {}
        if context.get('company'):
            return context['company']
        return False

    def default_posted(self, cursor, user, context=None):
        return False

OpenBalanceSheetInit()


class OpenBalanceSheet(Wizard):
    'Open Balance Sheet'
    _name = 'account.account.open_balance_sheet'
    states = {
        'init': {
            'result': {
                'type': 'form',
                'object': 'account.account.open_balance_sheet.init',
                'state': [
                    ('end', 'Cancel', 'tryton-cancel'),
                    ('open', 'Open', 'tryton-ok', True),
                ],
            },
        },
        'open': {
            'result': {
                'type': 'action',
                'action': '_action_open',
                'state': 'end',
            },
        },
    }

    def _action_open(self, cursor, user, datas, context=None):
        if context is None:
            context = {}
        model_data_obj = self.pool.get('ir.model.data')
        act_window_obj = self.pool.get('ir.action.act_window')
        company_obj = self.pool.get('company.company')
        lang_obj = self.pool.get('ir.lang')

        company = company_obj.browse(cursor, user, datas['form']['company'],
                context=context)
        for code in [context.get('language', False) or 'en_US', 'en_US']:
            lang_ids = lang_obj.search(cursor, user, [
                ('code', '=', code),
                ], context=context)
            if lang_ids:
                break
        lang = lang_obj.browse(cursor, user, lang_ids[0], context=context)

        date = lang_obj.strftime(datas['form']['date'], lang.code, lang.date)

        act_window_id = model_data_obj.get_id(cursor, user, 'account',
                'act_account_balance_sheet_tree', context=context)
        res = act_window_obj.read(cursor, user, act_window_id, context=context)
        res['pyson_context'] = PYSONEncoder().encode({
            'date': Date(datas['form']['date'].year,
                datas['form']['date'].month,
                datas['form']['date'].day),
            'posted': datas['form']['posted'],
            'company': datas['form']['company'],
            })
        res['name'] = res['name'] + ' - '+ date + ' - ' + company.name
        return res

OpenBalanceSheet()


class OpenIncomeStatementInit(ModelView):
    'Open Income Statement Init'
    _name = 'account.account.open_income_statement.init'
    _description = __doc__
    fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
            required=True, on_change=['fiscalyear'],
            depends=['start_period', 'end_period'])
    start_period = fields.Many2One('account.period', 'Start Period',
            domain=[
                ('fiscalyear', '=', Eval('fiscalyear')),
                ('start_date', '<=', (Eval('end_period'), 'start_date'))
            ],
            depends=['end_period'])
    end_period = fields.Many2One('account.period', 'End Period',
            domain=[
                ('fiscalyear', '=', Eval('fiscalyear')),
                ('start_date', '>=', (Eval('start_period'), 'start_date')),
            ],
            depends=['start_period'])
    company = fields.Many2One('company.company', 'Company', required=True)
    posted = fields.Boolean('Posted Move', help='Show 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_id
        return False

    def default_company(self, cursor, user, context=None):
        if context is None:
            context = {}
        if context.get('company'):
            return context['company']
        return False

    def default_posted(self, cursor, user, context=None):
        return False

    def on_change_fiscalyear(self, cursor, user, vals, context=None):
        return {
            'start_period': False,
            'end_period': False,
        }

OpenIncomeStatementInit()


class OpenIncomeStatement(Wizard):
    'Open Income Statement'
    _name = 'account.account.open_income_statement'
    states = {
        'init': {
            'result': {
                'type': 'form',
                'object': 'account.account.open_income_statement.init',
                'state': [
                    ('end', 'Cancel', 'tryton-cancel'),
                    ('open', 'Open', 'tryton-ok', True),
                ],
            },
        },
        'open': {
            'result': {
                'type': 'action',
                'action': '_action_open',
                'state': 'end',
            },
        },
    }

    def _action_open(self, cursor, user, datas, context=None):
        if context is None:
            context = {}
        model_data_obj = self.pool.get('ir.model.data')
        act_window_obj = self.pool.get('ir.action.act_window')
        period_obj = self.pool.get('account.period')

        start_period_ids = [0]
        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)

        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 = list(set(end_period_ids).difference(
                set(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 = list(set(end_period_ids).difference(
                set(start_period_ids)))

        act_window_id = model_data_obj.get_id(cursor, user, 'account',
                'act_account_income_statement_tree', context=context)
        res = act_window_obj.read(cursor, user, act_window_id, context=context)
        res['pyson_context'] = PYSONEncoder().encode({
            'periods': end_period_ids,
            'posted': datas['form']['posted'],
            'company': datas['form']['company'],
            })
        return res

OpenIncomeStatement()


class CreateChartAccountInit(ModelView):
    'Create Chart Account Init'
    _name = 'account.account.create_chart_account.init'
    _description = __doc__

CreateChartAccountInit()


class CreateChartAccountAccount(ModelView):
    'Create Chart Account Account'
    _name = 'account.account.create_chart_account.account'
    _description = __doc__
    company = fields.Many2One('company.company', 'Company', required=True)
    account_template = fields.Many2One('account.account.template',
            'Account Template', required=True, domain=[('parent', '=', False)])

    def default_company(self, cursor, user, context=None):
        if context is None:
            context = {}
        if context.get('company'):
            return context['company']
        return False

CreateChartAccountAccount()


class CreateChartAccountPropertites(ModelView):
    'Create Chart Account Properties'
    _name = 'account.account.create_chart_account.properties'
    _description = __doc__
    company = fields.Many2One('company.company', 'Company')
    account_receivable = fields.Many2One('account.account',
            'Default Receivable Account',
            domain=[
                ('kind', '=', 'receivable'),
                ('company', '=', Eval('company')),
            ],
            depends=['company'])
    account_payable = fields.Many2One('account.account',
            'Default Payable Account',
            domain=[
                ('kind', '=', 'payable'),
                ('company', '=', Eval('company')),
            ],
            depends=['company'])

CreateChartAccountPropertites()


class CreateChartAccount(Wizard):
    'Create chart account from template'
    _name = 'account.account.create_chart_account'
    states = {
        'init': {
            'result': {
                'type': 'form',
                'object': 'account.account.create_chart_account.init',
                'state': [
                    ('end', 'Cancel', 'tryton-cancel'),
                    ('account', 'Ok', 'tryton-ok', True),
                ],
            },
        },
        'account': {
            'result': {
                'type': 'form',
                'object': 'account.account.create_chart_account.account',
                'state': [
                    ('end', 'Cancel', 'tryton-cancel'),
                    ('create_account', 'Create', 'tryton-ok', True),
                ],
            },
        },
        'create_account': {
            'actions': ['_action_create_account'],
            'result': {
                'type': 'form',
                'object': 'account.account.create_chart_account.properties',
                'state': [
                    ('end', 'Cancel', 'tryton-cancel'),
                    ('create_properties', 'Create', 'tryton-ok', True),
                ],
            },
        },
        'create_properties': {
            'result': {
                'type': 'action',
                'action': '_action_create_properties',
                'state': 'end',
            },
        },
    }

    def _action_create_account(self, cursor, user, datas, context=None):
        account_type_template_obj = \
                self.pool.get('account.account.type.template')
        account_template_obj = self.pool.get('account.account.template')
        tax_code_template_obj = self.pool.get('account.tax.code.template')
        tax_template_obj = self.pool.get('account.tax.template')
        tax_rule_template_obj = self.pool.get('account.tax.rule.template')
        tax_rule_line_template_obj = \
                self.pool.get('account.tax.rule.line.template')

        context=context.copy()
        context['language'] = 'en_US'

        account_template = account_template_obj.browse(cursor, user,
                datas['form']['account_template'], context=context)

        # Create account types
        template2type = {}
        account_type_template_obj.create_type(cursor, user,
                account_template.type, datas['form']['company'],
                context=context, template2type=template2type)

        # Create accounts
        template2account = {}
        account_template_obj.create_account(cursor, user,
                account_template, datas['form']['company'],
                context=context, template2account=template2account,
                template2type=template2type)

        # Create tax codes
        template2tax_code = {}
        tax_code_template_ids = tax_code_template_obj.search(cursor, user, [
            ('account', '=', datas['form']['account_template']),
            ('parent', '=', False),
            ], context=context)
        for tax_code_template in tax_code_template_obj.browse(cursor, user,
                tax_code_template_ids, context=context):
            tax_code_template_obj.create_tax_code(cursor, user,
                    tax_code_template, datas['form']['company'],
                    context=context, template2tax_code=template2tax_code)

        # Create taxes
        template2tax = {}
        tax_template_ids = tax_template_obj.search(cursor, user, [
            ('account', '=', datas['form']['account_template']),
            ('parent', '=', False),
            ], context=context)
        for tax_template in tax_template_obj.browse(cursor, user,
                tax_template_ids, context=context):
            tax_template_obj.create_tax(cursor, user, tax_template,
                    datas['form']['company'],
                    template2tax_code=template2tax_code,
                    template2account=template2account,
                    context=context, template2tax=template2tax)

        # Update taxes on accounts
        account_template_obj.update_account_taxes(cursor, user,
                account_template, template2account, template2tax, context=context)

        # Create tax rules
        template2rule = {}
        tax_rule_template_ids = tax_rule_template_obj.search(cursor, user, [
            ('account', '=', datas['form']['account_template']),
            ], context=context)
        for tax_rule_template in tax_rule_template_obj.browse(cursor, user,
                tax_rule_template_ids, context=context):
            tax_rule_template_obj.create_rule(cursor, user, tax_rule_template,
                    datas['form']['company'], context=context,
                    template2rule=template2rule)

        # Create tax rule lines
        template2rule_line = {}
        tax_rule_line_template_ids = tax_rule_line_template_obj.search(cursor,
                user, [
                    ('rule.account', '=', datas['form']['account_template']),
                    ], context=context)
        for tax_rule_line_template in tax_rule_line_template_obj.browse(cursor,
                user, tax_rule_line_template_ids, context=context):
            tax_rule_line_template_obj.create_rule_line(cursor, user,
                    tax_rule_line_template, template2tax, template2rule,
                    context=context, template2rule_line=template2rule_line)

        return {'company': datas['form']['company']}

    def _action_create_properties(self, cursor, user, datas, context=None):
        property_obj = self.pool.get('ir.property')
        model_field_obj = self.pool.get('ir.model.field')

        account_receivable_field_id = model_field_obj.search(cursor, user, [
            ('model.model', '=', 'party.party'),
            ('name', '=', 'account_receivable'),
            ], limit=1, context=context)[0]
        property_ids = property_obj.search(cursor, user, [
            ('field', '=', account_receivable_field_id),
            ('res', '=', False),
            ('company', '=', datas['form']['company']),
            ], context=context)
        property_obj.delete(cursor, 0, property_ids, context=context)
        property_obj.create(cursor, 0, {
            'name': 'account_receivable',
            'field': account_receivable_field_id,
            'value': 'account.account,' + \
                    str(datas['form']['account_receivable']),
            'company': datas['form']['company'],
            }, context=context)

        account_payable_field_id = model_field_obj.search(cursor, user, [
            ('model.model', '=', 'party.party'),
            ('name', '=', 'account_payable'),
            ], limit=1, context=context)[0]
        property_ids = property_obj.search(cursor, user, [
            ('field', '=', account_payable_field_id),
            ('res', '=', False),
            ('company', '=', datas['form']['company']),
            ], context=context)
        property_obj.delete(cursor, 0, property_ids, context=context)
        property_obj.create(cursor, 0, {
            'name': 'account_payable',
            'field': account_payable_field_id,
            'value': 'account.account,' + \
                    str(datas['form']['account_payable']),
            'company': datas['form']['company'],
            }, context=context)
        return {}

CreateChartAccount()


class UpdateChartAccountInit(ModelView):
    'Update Chart Account from Template Init'
    _name = 'account.account.update_chart_account.init'
    _description = __doc__
    account = fields.Many2One('account.account', 'Root Account',
            required=True, domain=[('parent', '=', False)])

UpdateChartAccountInit()


class UpdateChartAccountStart(ModelView):
    'Update Chart Account from Template Start'
    _name = 'account.account.update_chart_account.start'
    _description = __doc__

UpdateChartAccountStart()


class UpdateChartAccount(Wizard):
    'Update Chart Account from Template'
    _name = 'account.account.update_chart_account'
    states = {
        'init': {
            'result': {
                'type': 'form',
                'object': 'account.account.update_chart_account.init',
                'state': [
                    ('end', 'Cancel', 'tryton-cancel'),
                    ('start', 'Ok', 'tryton-ok', True),
                ],
            },
        },
        'start': {
            'actions': ['_action_update_account'],
            'result': {
                'type': 'form',
                'object': 'account.account.update_chart_account.start',
                'state': [
                    ('end', 'Ok', 'tryton-ok', True),
                ],
            },
        },
    }

    def _action_update_account(self, cursor, user, datas, context=None):
        account_type_obj = self.pool.get('account.account.type')
        account_type_template_obj = \
                self.pool.get('account.account.type.template')
        account_obj = self.pool.get('account.account')
        account_template_obj = self.pool.get('account.account.template')
        tax_code_obj = self.pool.get('account.tax.code')
        tax_code_template_obj = self.pool.get('account.tax.code.template')
        tax_obj = self.pool.get('account.tax')
        tax_template_obj = self.pool.get('account.tax.template')
        tax_rule_obj = self.pool.get('account.tax.rule')
        tax_rule_template_obj = self.pool.get('account.tax.rule.template')
        tax_rule_line_obj = self.pool.get('account.tax.rule.line')
        tax_rule_line_template_obj = \
                self.pool.get('account.tax.rule.line.template')

        account = account_obj.browse(cursor, user, datas['form']['account'],
                context=context)

        # Update account types
        template2type = {}
        account_type_obj.update_type(cursor, user, account.type,
                context=context, template2type=template2type)
        # Create missing account types
        if account.type.template:
            account_type_template_obj.create_type(cursor, user,
                    account.type.template, account.company.id, context=context,
                    template2type=template2type)

        # Update accounts
        template2account = {}
        account_obj.update_account(cursor, user, account, context=context,
                template2account=template2account, template2type=template2type)
        # Create missing accounts
        if account.template:
            account_template_obj.create_account(cursor, user, account.template,
                    account.company.id, context=context,
                    template2account=template2account,
                    template2type=template2type)

        # Update tax codes
        template2tax_code = {}
        tax_code_ids = tax_code_obj.search(cursor, user, [
            ('company', '=', account.company.id),
            ('parent', '=', False),
            ], context=context)
        for tax_code in tax_code_obj.browse(cursor, user, tax_code_ids,
                context=context):
            tax_code_obj.update_tax_code(cursor, user, tax_code, context=context,
                    template2tax_code=template2tax_code)
        # Create missing tax codes
        if account.template:
            tax_code_template_ids = tax_code_template_obj.search(cursor, user, [
                ('account', '=', account.template.id),
                ('parent', '=', False),
                ], context=context)
            for tax_code_template in tax_code_template_obj.browse(cursor, user,
                    tax_code_template_ids, context=context):
                tax_code_template_obj.create_tax_code(cursor, user,
                        tax_code_template, account.company.id, context=context,
                        template2tax_code=template2tax_code)

        # Update taxes
        template2tax = {}
        tax_ids = tax_obj.search(cursor, user, [
            ('company', '=', account.company.id),
            ('parent', '=', False),
            ], context=context)
        for tax in tax_obj.browse(cursor, user, tax_ids, context=context):
            tax_obj.update_tax(cursor, user, tax,
                    template2tax_code=template2tax_code,
                    template2account=template2account,
                    context=context,
                    template2tax=template2tax)
        # Create missing taxes
        if account.template:
            tax_template_ids = tax_template_obj.search(cursor, user, [
                ('account', '=', account.template.id),
                ('parent', '=', False),
                ], context=context)
            for tax_template in tax_template_obj.browse(cursor, user,
                    tax_template_ids, context=context):
                tax_template_obj.create_tax(cursor, user, tax_template,
                        account.company.id, template2tax_code=template2tax_code,
                        template2account=template2account, context=context,
                        template2tax=template2tax)

        # Update taxes on accounts
        account_obj.update_account_taxes(cursor, user, account,
                template2account, template2tax, context=context)

        # Update tax rules
        template2rule = {}
        tax_rule_ids = tax_rule_obj.search(cursor, user, [
            ('company', '=', account.company.id),
            ], context=context)
        for tax_rule in tax_rule_obj.browse(cursor, user, tax_rule_ids,
                context=context):
            tax_rule_obj.update_rule(cursor, user, tax_rule, context=context,
                    template2rule=template2rule)
        # Create missing tax rules
        if account.template:
            tax_rule_template_ids = tax_rule_template_obj.search(cursor, user, [
                ('account', '=', account.template.id),
                ], context=context)
            for tax_rule_template in tax_rule_template_obj.browse(cursor, user,
                    tax_rule_template_ids, context=context):
                tax_rule_template_obj.create_rule(cursor, user,
                        tax_rule_template, account.company.id, context=context,
                        template2rule=template2rule)

        # Update tax rule lines
        template2rule_line = {}
        tax_rule_line_ids = tax_rule_line_obj.search(cursor, user, [
            ('rule.company', '=', account.company.id),
            ], context=context)
        for tax_rule_line in tax_rule_line_obj.browse(cursor, user,
                tax_rule_line_ids, context=context):
            tax_rule_line_obj.update_rule_line(cursor, user, tax_rule_line,
                    template2tax, template2rule, context=context,
                    template2rule_line=template2rule_line)
        # Create missing tax rule lines
        if account.template:
            tax_rule_line_template_ids = tax_rule_line_template_obj.search(
                    cursor, user, [
                        ('rule.account', '=', account.template.id),
                        ], context=context)
            for tax_rule_line_template in tax_rule_line_template_obj.browse(
                    cursor, user, tax_rule_line_template_ids, context=context):
                tax_rule_line_template_obj.create_rule_line(cursor, user,
                        tax_rule_line_template, template2tax, template2rule,
                        context=context, template2rule_line=template2rule_line)
        return {}

UpdateChartAccount()


class OpenThirdPartyBalanceInit(ModelView):
    'Open Third Party Balance Init'
    _name = 'account.account.open_third_party_balance.init'
    _description = __doc__
    company = fields.Many2One('company.company', 'Company', required=True)
    fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
            required=True)
    posted = fields.Boolean('Posted Move', help='Show 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_id
        return False

    def default_posted(self, cursor, user, context=None):
        return False

    def default_company(self, cursor, user, context=None):
        if context is None:
            context = {}
        if context.get('company'):
            return context['company']
        return False

OpenThirdPartyBalanceInit()


class OpenThirdPartyBalance(Wizard):
    'Open Third Party Balance'
    _name = 'account.account.open_third_party_balance'
    states = {
        'init': {
            'result': {
                'type': 'form',
                'object': 'account.account.open_third_party_balance.init',
                'state': [
                    ('end', 'Cancel', 'tryton-cancel'),
                    ('print', 'Print', 'tryton-ok', True),
                ],
            },
        },
        'print': {
            'result': {
                'type': 'print',
                'report': 'account.account.third_party_balance',
                'state': 'end',
            },
        },
    }

OpenThirdPartyBalance()


class ThirdPartyBalance(Report):
    _name = 'account.account.third_party_balance'

    def parse(self, cursor, user, report, objects, datas, context):
        party_obj = self.pool.get('party.party')
        move_line_obj = self.pool.get('account.move.line')
        company_obj = self.pool.get('company.company')
        date_obj = self.pool.get('ir.date')
        context = context.copy()

        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 = ""

        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 (\'payable\',\'receivable\') ' \
                    'AND l.reconciliation IS NULL ' \
                    'AND a.company = %s ' \
                    'AND (l.maturity_date <= %s ' \
                        'OR l.maturity_date IS NULL) '\
                    'AND ' + line_query + ' ' \
                    + posted_clause + \
                'GROUP BY l.party ' \
                'HAVING (SUM(l.debit) != 0 OR SUM(l.credit) != 0)',
                (datas['form']['company'], date_obj.today(cursor, user,
                           context=context)))

        res = cursor.fetchall()
        id2party = {}
        for party in party_obj.browse(cursor, user, [x[0] for x in res],
                context=context):
            id2party[party.id] = party
        objects = [{
            'name': id2party[x[0]].rec_name,
            'debit': x[1],
            'credit': x[2],
            'solde': x[1] - x[2],
            } for x in res]
        objects.sort(lambda x, y: cmp(x['name'], y['name']))
        context['total_debit'] = sum((x['debit'] for x in objects))
        context['total_credit'] = sum((x['credit'] for x in objects))
        context['total_solde'] = sum((x['solde'] for x in objects))

        return super(ThirdPartyBalance, self).parse(cursor, user, report,
                objects, datas, context)

ThirdPartyBalance()


class OpenAgedBalanceInit(ModelView):
    'Open Aged Balance Init'
    _name = 'account.account.open_aged_balance.init'
    _description = __doc__
    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='Show 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_id
        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 = {}
        if context.get('company'):
            return context['company']
        return False

OpenAgedBalanceInit()


class OpenAgedBalance(Wizard):
    'Open Aged Party Balance'
    _name = 'account.account.open_aged_balance'

    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 __init__(self):
        super(OpenAgedBalance, self).__init__()
        self._error_messages.update({
                'warning': 'Warning',
                'term_overlap_desc': 'You cannot define overlapping terms'})

    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 datas['form']

OpenAgedBalance()


class AgedBalance(Report):
    _name = 'account.account.aged_balance'

    def parse(self, cursor, user, report, objects, datas, context):
        party_obj = self.pool.get('party.party')
        move_line_obj = self.pool.get('account.move.line')
        company_obj = self.pool.get('company.company')
        date_obj = self.pool.get('ir.date')

        company = company_obj.browse(cursor, user,
                datas['form']['company'], context=context)
        context['digits'] = company.currency.digits
        context['fiscalyear'] = datas['form']['fiscalyear']
        context['posted'] = datas['form']['posted']
        line_query, _ = move_line_obj.query_get(cursor, user, context=context)

        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,term in enumerate(terms):
            if position == 0:
                term_query = '(l.maturity_date <= %s '\
                    'OR l.maturity_date IS NULL) '
                term_args = (date_obj.today(cursor, user, context=context) + \
                    datetime.timedelta(days=term*coef),)
            else:
                term_query = '(l.maturity_date <= %s '\
                    'AND l.maturity_date > %s) '
                term_args = (
                    date_obj.today(cursor, user, context=context) + \
                        datetime.timedelta(days=term*coef),
                    date_obj.today(cursor, user, context=context) + \
                        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',) * len(kind)) + ") "\
                        'AND l.reconciliation IS NULL ' \
                        'AND a.company = %s ' \
                        'AND '+ term_query+\
                        'AND ' + line_query + ' ' \
                    'GROUP BY l.party ' \
                    'HAVING (SUM(l.debit) - SUM(l.credit) != 0)',
                    kind + (datas['form']['company'],) + term_args)
            for party, solde in cursor.fetchall():
                if party in res:
                    res[party][position] = solde
                else:
                    res[party] = [(i[0] == position) and solde \
                            or Decimal("0.0") for i in enumerate(terms)]
        party_ids = party_obj.search(cursor, user, [
            ('id', 'in', [k for k in res.iterkeys()]),
            ], context=context)
        parties = party_obj.browse(cursor, user, party_ids, context=context)

        context['main_title'] = datas['form']['balance_type']
        context['unit'] = datas['form']['unit']
        for i in range(3):
            context['total' + str(i)] = sum((v[i] for v in res.itervalues()))
            context['term' + str(i)] = terms[i]

        context['company'] = company
        context['parties']= [{
            'name': p.rec_name,
            'amount0': res[p.id][0],
            'amount1': res[p.id][1],
            'amount2': res[p.id][2],
            } for p in parties]

        return super(AgedBalance, self).parse(cursor, user, report, objects,
                datas, context)

AgedBalance()
