← Back to team overview

openerp-expert-accounting team mailing list archive

Re: Closing of fiscal year, draft and valid move_lines

 

So I finally decided to patch 5.0 to get 3 additional fields for 
account_account
* debit_fy (Debit FY)
* credit_fy (Credit FY)
* balance_init

additionally the balance of the "old" fields
* debit (now "Debit Tot" to avoid confusion, sorry translators)
* credit (now Credit Tot) 
* balance
always adds up all open fiscal years of balance accounts until the selected 
one 

if no fiscal year is preselected, the current fy is used (especially for "List 
of Accounts")

you may want to remove debit, credit from account.tree.view

This way all existing reports and views will return the same (sometimes quite 
useless) figures, whereas it is  very easy to adapt reports.

IMHO the construct is compatible with the current behaviour of selecting 
valid/draft moves, although I strongly disagree to allow to select draft moves 
in balance and general ledger and similar because these may not be balanced 
and will cause support/bug reports. probably we need states draft, balanced, 
valid and allow to select valid , balanced or both.

@Borja
I didn't implement the period stuff yet, but it will be similar to fy stuff.
I also believe that calculating some more fields on request in ONE query will 
not harm performance - the most expensive is reading the database.
-- 
regards
Ferdinand Gassauer
ChriCar Beteiligungs- und Beratungs- GmbH
Official OpenERP Partner

Attachment: account_balance_init.png
Description: PNG image

--- /home/terp/OpenERP/official/addons/account/account.py	2010-04-10 22:53:52.847192301 +0200
+++ account.py	2010-04-23 21:04:37.543085457 +0200
@@ -195,21 +204,54 @@ class account_account(osv.osv):
             ids3 = self._get_children_and_consol(cr, uid, ids3, context)
         return ids2 + ids3
 
+    def _get_children_and_consol_balance(self, cr, uid, ids, context={}):
+        #this function search for all the children and all consolidated children (recursively) of the given account ids
+        ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids),('user_type.close_method', '<>', 'none')], context=context)
+        ids3 = []
+        for rec in self.browse(cr, uid, ids2, context=context):
+            for child in rec.child_consol_ids:
+                ids3.append(child.id)
+        if ids3:
+            ids3 = self._get_children_and_consol(cr, uid, ids3, context)
+        return ids2 + ids3
+
+
     def __compute(self, cr, uid, ids, field_names, arg, context={}, query=''):
         #compute the balance/debit/credit accordingly to the value of field_name for the given account ids
+        
+        periods_fy = self.pool.get('account.move.line')._periods_fy_get(cr, uid, context=context)
+
+        case_debit = 'COALESCE(SUM(case when l.period_id in ('+periods_fy+') then l.debit else 0 end),0) as debit_fy '
+        case_credit = 'COALESCE(SUM(case when l.period_id in ('+periods_fy+') then l.credit else 0 end),0 ) as credit_fy '
+        case_balance_init = 'COALESCE(SUM(case when l.period_id not in ('+periods_fy+') then l.debit - l.credit else 0 end),0) as balance_init '
+        
+#        mapping = {
+#            'balance': "COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance ",
+#            'debit': "COALESCE(SUM(l.debit), 0) as debit ",
+#            'credit': "COALESCE(SUM(l.credit), 0) as credit "
+#        }
         mapping = {
             'balance': "COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance ",
             'debit': "COALESCE(SUM(l.debit), 0) as debit ",
-            'credit': "COALESCE(SUM(l.credit), 0) as credit "
+            'credit': "COALESCE(SUM(l.credit), 0) as credit ",
+            'debit_fy': case_debit,
+            'credit_fy': case_credit,
+            'balance_init': case_balance_init,
         }
+        
         #get all the necessary accounts
         ids2 = self._get_children_and_consol(cr, uid, ids, context)
         acc_set = ",".join(map(str, ids2))
+
+        ids2_balance = self._get_children_and_consol_balance(cr, uid, ids, context)
+        acc_set_previous = ",".join(map(str, ids2_balance)) or '0'
         #compute for each account the balance/debit/credit from the move lines
         accounts = {}
         if ids2:
             aml_query = self.pool.get('account.move.line')._query_get(cr, uid, context=context)
+            aml_query_previous = self.pool.get('account.move.line')._query_get_previous(cr, uid, context=context)
 
+            query2 = query
             wheres = [""]
             if query.strip():
                 wheres.append(query.strip())
@@ -217,20 +259,59 @@ class account_account(osv.osv):
                 wheres.append(aml_query.strip())
             query = " AND ".join(wheres)
 
+            # for open previous years
+            wheres2 = [""]
+            if query2.strip():
+                wheres2.append(query2.strip())
+            if aml_query_previous.strip():
+                wheres2.append(aml_query_previous.strip())
+            query2 = " AND ".join(wheres2)
+
+#            cr.execute(("SELECT l.account_id as id, " +\
+#                    ' , '.join(map(lambda x: mapping[x], field_names)) +
+#                    "FROM " \
+#                        "account_move_line l " \
+#                    "WHERE " \
+#                        "l.account_id IN (%s) " \
+#                        + query +
+#                    " GROUP BY l.account_id " ) % (acc_set,))
+#
+            # FIXME
+            # the "not in" fetches meoves for all balance accounts of prevoious open fiscal years
+            # Do not know if this can be achieved already in acc_set_previous 
+            # - because of "child_of" the condition 'none' might cause problems for views.
+#            cr.execute(("SELECT l.account_id as id, " +\
+#                    ' , '.join(map(lambda x: mapping[x], field_names)) +
+#                    "FROM " \
+#                        "account_move_line l " \
+#                    "WHERE " \
+#                        "(l.account_id IN (%s) " \
+#                        + query + ") or " 
+#                        "(l.account_id not in (select a.id from account_account a, " \
+#                                                             "account_account_type t "\
+#                                                       "where t.id = a.user_type " \
+#                                                         "and t.close_method = 'none') AND " \
+#                        "l.account_id IN (%s) " \
+#                        + query2 + ") "  \
+#                    " GROUP BY l.account_id " ) % (acc_set,acc_set_previous,))
+
             cr.execute(("SELECT l.account_id as id, " +\
                     ' , '.join(map(lambda x: mapping[x], field_names)) +
                     "FROM " \
                         "account_move_line l " \
                     "WHERE " \
-                        "l.account_id IN (%s) " \
-                        + query +
-                    " GROUP BY l.account_id") % (acc_set, ))
+                        "(l.account_id IN (%s) " \
+                        + query + ") or " 
+                        "(l.account_id IN (%s) " \
+                        + query2 + ") "  \
+                    " GROUP BY l.account_id " ) % (acc_set,acc_set_previous,))
 
             for res in cr.dictfetchall():
                 accounts[res['id']] = res
 
 
         # consolidate accounts with direct children
+        ids2.reverse()
         brs = list(self.browse(cr, uid, ids2, context=context))
         sums = {}
         while brs:
@@ -295,8 +376,11 @@ class account_account(osv.osv):
         'child_consol_ids': fields.many2many('account.account', 'account_account_consol_rel', 'child_id', 'parent_id', 'Consolidated Children'),
         'child_id': fields.function(_get_child_ids, method=True, type='many2many', relation="account.account", string="Child Accounts"),
         'balance': fields.function(__compute, digits=(16, int(config['price_accuracy'])), method=True, string='Balance', multi='balance'),
-        'credit': fields.function(__compute, digits=(16, int(config['price_accuracy'])), method=True, string='Credit', multi='balance'),
-        'debit': fields.function(__compute, digits=(16, int(config['price_accuracy'])), method=True, string='Debit', multi='balance'),
+        'balance_init': fields.function(__compute, digits=(16, int(config['price_accuracy'])), method=True, string='Initial Balance', multi='balance_init'),
+        'credit': fields.function(__compute, digits=(16, int(config['price_accuracy'])), method=True, string='Credit Tot', multi='balance'),
+        'credit_fy': fields.function(__compute, digits=(16, int(config['price_accuracy'])), method=True, string='Credit FY', multi='balance'),
+        'debit': fields.function(__compute, digits=(16, int(config['price_accuracy'])), method=True, string='Debit Tot', multi='balance'),
+        'debit_fy': fields.function(__compute, digits=(16, int(config['price_accuracy'])), method=True, string='Debit FY', multi='balance'),
         'reconcile': fields.boolean('Reconcile', help="Check this if the user is allowed to reconcile entries in this account."),
         'shortcut': fields.char('Shortcut', size=12),
         'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel',
--- /home/terp/OpenERP/official/addons/account/account_move_line.py	2010-03-14 06:25:32.972557583 +0100
+++ account_move_line.py	2010-04-23 21:21:10.631275386 +0200
@@ -29,11 +29,57 @@ import mx.DateTime
 from mx.DateTime import RelativeDateTime, now, DateTime, localtime
 
 import tools
+import sys
 
+#----------------------------------------------------------------------------
+# Account Move Line
+#----------------------------------------------------------------------------
 class account_move_line(osv.osv):
     _name = "account.move.line"
     _description = "Entry lines"
 
+    def _query_get_previous(self, cr, uid, obj='l', context={}):
+        fiscalyear_obj = self.pool.get('account.fiscalyear')
+        if not context.get('fiscalyear', False):
+            fiscalyear_ids = fiscalyear_obj.search(cr, uid, [('state', '=', 'draftq')])
+            fiscalyear_clause = (','.join([str(x) for x in fiscalyear_ids])) or '0'
+        else:
+            fy_id = context.get('fiscalyear')
+            fys = fiscalyear_obj.search(cr, uid, [('id', '=', fy_id )])
+            for fy_ids in self.pool.get('account.fiscalyear').browse(cr, uid,fys):
+                fy_end_date = fy_ids.date_stop
+            fiscalyear_ids = fiscalyear_obj.search(cr, uid, [('date_stop', '<', fy_end_date),('state', '=', 'draft')])
+            fiscalyear_clause = (','.join([str(x) for x in fiscalyear_ids])) or '0'
+        state=context.get('state',False)
+        where_move_state = ''
+        where_move_lines_by_date = ''
+
+        if context.get('date_from', False) and context.get('date_to', False):
+            where_move_lines_by_date = " AND " +obj+".move_id in ( select id from account_move  where date >= '" +context['date_from']+"' AND date <= '"+context['date_to']+"')"
+            
+        if state:
+            if state.lower() not in ['all']:
+                where_move_state= " AND "+obj+".move_id in (select id from account_move where account_move.state = '"+state+"')"
+
+        if context.get('periods', False):
+            ids = ','.join([str(x) for x in context['periods']])
+            return obj+".state<>'draft' AND "+obj+".period_id in (SELECT id from account_period WHERE fiscalyear_id in (%s) AND id in (%s)) %s %s" % (fiscalyear_clause, ids,where_move_state,where_move_lines_by_date)
+        else:
+            return obj+".state<>'draft' AND "+obj+".period_id in (SELECT id from account_period WHERE fiscalyear_id in (%s) %s %s)" % (fiscalyear_clause,where_move_state,where_move_lines_by_date)
+        
+
+    def _periods_fy_get(self, cr, uid, obj='l', context={}):
+        period_obj = self.pool.get('account.period')
+        if not context.get('fiscalyear', False):
+            today = now()
+            period_ids = period_obj.search(cr, uid, [('fiscalyear_id.date_start', '<=', today), ('fiscalyear_id.date_stop', '>=', today) ])
+        else:
+            fy_id = context.get('fiscalyear')
+            period_ids = period_obj.search(cr, uid, [('fiscalyear_id.id', '=', fy_id )])
+        periods_fy_selected = (','.join([str(x) for x in period_ids])) or '0'
+                
+        return periods_fy_selected
+
     def _query_get(self, cr, uid, obj='l', context={}):
         fiscalyear_obj = self.pool.get('account.fiscalyear')
         if not context.get('fiscalyear', False):
@@ -245,12 +291,20 @@ class account_move_line(osv.osv):
         res={}
         # TODO group the foreach in sql
         for id in ids:
-            cr.execute('SELECT date,account_id FROM account_move_line WHERE id=%s', (id,))
+            # FIX ME we just need to fetch ONE record 
+            cr.execute('SELECT date,account_id FROM account_move_line WHERE id=%s limit 1', (id,))
             dt, acc = cr.fetchone()
             cr.execute('SELECT SUM(debit-credit) FROM account_move_line WHERE account_id=%s AND (date<%s OR (date=%s AND id<=%s))', (acc,dt,dt,id))
             res[id] = cr.fetchone()[0]
         return res
 
+    def _balance_move_line(self, cr, uid, ids, name, arg, context={}):
+        res = {} 
+        for line in self.browse(cr, uid, ids,context):
+            balance = line.debit -  line.credit
+            res[line.id] = balance
+        return res
+
     def _invoice(self, cursor, user, ids, name, arg, context=None):
         invoice_obj = self.pool.get('account.invoice')
         res = {}
@@ -344,6 +398,7 @@ class account_move_line(osv.osv):
         'product_id': fields.many2one('product.product', 'Product'),
         'debit': fields.float('Debit', digits=(16,2)),
         'credit': fields.float('Credit', digits=(16,2)),
+        'balance_move_line': fields.function(_balance_move_line, method=True, string='Balance Line'),
         'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade", domain=[('type','<>','view'), ('type', '<>', 'closed')], select=2),
         'move_id': fields.many2one('account.move', 'Move', ondelete="cascade", states={'valid':[('readonly',True)]}, help="The move of this entry line.", select=2),
 
@@ -578,7 +633,7 @@ class account_move_line(osv.osv):
             raise osv.except_osv(_('Error'), _('Entry is already reconciled'))
         account = self.pool.get('account.account').browse(cr, uid, account_id, context=context)
         if not context.get('fy_closing', False) and not account.reconcile:
-            raise osv.except_osv(_('Error'), _('The account is not defined to be reconciled !'))
+            raise osv.except_osv(_('Error'), _('The account %s is not defined to be reconcile !' % (account.name)))
         if r[0][1] != None:
             raise osv.except_osv(_('Error'), _('Some entries are already reconciled !'))
 
--- /home/terp/OpenERP/official/addons/account/account_view.xml	2010-04-06 08:29:45.091463943 +0200
+++ account_view.xml	2010-04-23 21:26:13.075095034 +0200
@@ -163,11 +167,16 @@
                 <tree string="Chart of accounts" toolbar="1" colors="blue:type=='view'">
                     <field name="code"/>
                     <field name="name"/>
+                    <field name="balance_init"/>
+                    <field name="debit_fy"/>
+                    <field name="credit_fy"/>
+                    <field name="balance"/>
                     <field name="debit"/>
                     <field name="credit"/>
-                    <field name="balance"/>
                     <field name="company_currency_id"/>
-                    <field name="type" invisible="1"/>
+                    <field name="type" />
+                    <field name="user_type" />
+                    <field name="parent_id" />
                 </tree>
             </field>
         </record>

References