Partial Reconciliation
authorFabien Pinckaers <fp@tinyerp.com>
Wed, 9 Jan 2008 17:45:52 +0000 (17:45 +0000)
committerFabien Pinckaers <fp@tinyerp.com>
Wed, 9 Jan 2008 17:45:52 +0000 (17:45 +0000)
bzr revid: fp@tinyerp.com-8b22103181a265e1ebfed6882ddc7a8a94d7483a

addons/account/account.py
addons/account/account_bank_statement.py
addons/account/account_move_line.py
addons/account/account_view.xml
addons/account/invoice.py
addons/account/wizard/wizard_pay_invoice.py
addons/account/wizard/wizard_reconcile.py

index dfd9e1d..bcabf37 100644 (file)
@@ -891,11 +891,31 @@ class account_move_reconcile(osv.osv):
                'name': fields.char('Name', size=64, required=True),
                'type': fields.char('Type', size=16, required=True),
                'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry lines'),
+               'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'),
                'create_date': fields.date('Creation date', readonly=True),
        }
        _defaults = {
                'name': lambda self,cr,uid,ctx={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile') or '/',
        }
+       def reconcile_partial_check(self, cr, uid, ids, type='auto', context={}):
+               for rec in self.pool.get('account.move.reconcile').browse(cr, uid, ids):
+                       total = 0.0
+                       for line in rec.line_partial_ids:
+                               total += (line.debit or 0.0) - (line.credit or 0.0)
+                       if not total:
+                               self.write(cr,uid, map(lambda x: x.id, rec.line_partial_ids), {'reconcile_id': rec.id })
+                               for line in rec.line_partial_ids:
+                                       total += (line.debit or 0.0) - (line.credit or 0.0)
+               return True
+       def name_get(self, cr, uid, ids, context=None):
+               result = {}
+               for r in self.browse(cr, uid, ids, context):
+                       total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0)
+                       if total:
+                               result[r.id] = '%s (%.2f)' % (r.name, total)
+                       else:
+                               result[r.id] = r.name
+               return result
 account_move_reconcile()
 
 #----------------------------------------------------------
index 742e49f..db793de 100644 (file)
@@ -268,7 +268,10 @@ class account_bank_statement(osv.osv):
                                if move.reconcile_id and move.reconcile_id.line_ids:
                                        torec += map(lambda x: x.id, move.reconcile_id.line_ids)
                                        try:
-                                               account_move_line_obj.reconcile(cr, uid, torec, 'statement', context)
+                                               if abs(move.reconcile_amount-move.amount<0.0001):
+                                                       account_move_line_obj.reconcile(cr, uid, torec, 'statement', context)
+                                               else:
+                                                       account_move_line_obj.reconcile_partial(cr, uid, torec, 'statement', context)
                                        except:
                                                raise osv.except_osv('Error !', 'Unable to reconcile entry "%s": %.2f'%(move.name, move.amount))
 
index 1a2a661..fff29c6 100644 (file)
@@ -292,6 +292,7 @@ class account_move_line(osv.osv):
                'ref': fields.char('Ref.', size=32),
                'statement_id': fields.many2one('account.bank.statement', 'Statement', help="The bank statement used for bank reconciliation", select=1),
                'reconcile_id': fields.many2one('account.move.reconcile', 'Reconcile', readonly=True, ondelete='set null', select=2),
+               'reconcile_partial_id': fields.many2one('account.move.reconcile', 'Partial Reconcile', readonly=True, ondelete='set null', select=2),
                'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency if it is a multi-currency entry."),
                'currency_id': fields.many2one('res.currency', 'Currency', help="The optionnal other currency if it is a multi-currency entry."),
 
@@ -408,6 +409,32 @@ class account_move_line(osv.osv):
        # writeoff; entry generated for the difference between the lines
        #
 
+       def reconcile_partial(self, cr, uid, ids, type='auto', context={}):
+               merges = []
+               unmerge = []
+               total = 0.0
+               merges_rec = []
+               for line in self.browse(cr, uid, ids, context):
+                       if line.reconcile_id:
+                               raise 'Already Reconciled'
+                       if line.reconcile_partial_id:
+                               for line2 in line.reconcile_partial_id.line_partial_ids:
+                                       if not line2.reconcile_id:
+                                               merges.append(line2.id)
+                                               total += (line2.debit or 0.0) - (line2.credit or 0.0)
+                               merges_rec.append(line.reconcile_partial_id.id)
+                       else:
+                               unmerge.append(line.id)
+                               total += (line.debit or 0.0) - (line.credit or 0.0)
+               if not total:
+                       return self.reconcile(cr, uid, merges+unmerge, context=context)
+               r_id = self.pool.get('account.move.reconcile').create(cr, uid, {
+                       'type': type, 
+                       'line_partial_ids': map(lambda x: (4,x,False), merges+unmerge)
+               })
+               self.pool.get('account.move.reconcile').reconcile_partial_check(cr, uid, [r_id] + merges_rec, context=context)
+               return True
+
        def reconcile(self, cr, uid, ids, type='auto', writeoff_acc_id=False, writeoff_period_id=False, writeoff_journal_id=False, context={}):
                id_set = ','.join(map(str, ids))
                lines = self.browse(cr, uid, ids, context=context)
@@ -480,7 +507,8 @@ class account_move_line(osv.osv):
                r_id = self.pool.get('account.move.reconcile').create(cr, uid, {
                        #'name': date, 
                        'type': type, 
-                       'line_id': map(lambda x: (4,x,False), ids)
+                       'line_id': map(lambda x: (4,x,False), ids),
+                       'line_partial_ids': map(lambda x: (3,x,False), ids)
                })
                # the id of the move.reconcile is written in the move.line (self) by the create method above 
                # because of the way the line_id are defined: (4, x, False)
index f0c4fb3..ed6efd2 100644 (file)
                                                <field name="journal_id" select="2"/>
                                                <field name="period_id"/>
                                                <field name="reconcile_id"/>
+                                               <field name="reconcile_partial_id"/>
                                                <field name="active" select="2"/>
                                                <field name="state" select="2"/>
                                        </page>
                                                <field name="blocked" select="3"/>
 
                                                <separator string="State" colspan="4"/>
-                                               <field name="state" select="2"/>
+                                               <newline/>
                                                <field name="reconcile_id"/>
+                                               <field name="reconcile_partial_id"/>
+                                               <field name="state" select="2"/>
                                        </page>
                                        <page string="Analytic Lines">
                                                <field name="analytic_lines" colspan="4" nolabel="1"/>
 
                                                <separator string="State" colspan="4"/>
                                                <field name="reconcile_id"/>
+                                               <field name="reconcile_partial_id"/>
                                                <field name="statement_id"/>
                                                <field name="state"/>
                                        </form>
index e36aa3b..a0e9d85 100644 (file)
@@ -707,14 +707,21 @@ class account_invoice(osv.osv):
                move_id = self.pool.get('account.move').create(cr, uid, move)
 
                line_ids = []
+               total = 0.0
                line = self.pool.get('account.move.line')
                cr.execute('select id from account_move_line where move_id in ('+str(move_id)+','+str(invoice.move_id.id)+')')
                lines = line.browse(cr, uid, map(lambda x: x[0], cr.fetchall()) )
                for l in lines:
                        if l.account_id.id==src_account_id:
                                line_ids.append(l.id)
-
-               self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context)
+                               total += (l.debit or 0.0) - (l.credit or 0.0)
+               print total, writeoff_period_id, writeoff_acc_id
+               if (not total) or writeoff_acc_id:
+                       print 'Total'
+                       self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context)
+               else:
+                       print 'Partial'
+                       self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context)
                return True
 account_invoice()
 
index 9f19f36..7562cec 100644 (file)
@@ -95,7 +95,7 @@ class wizard_pay_invoice(wizard.interface):
        states = {
                'init': {
                        'actions': [_get_period],
-                       'result': {'type':'form', 'arch':pay_form, 'fields':pay_fields, 'state':[('end','Cancel'),('writeoff_check','Pay Invoice')]}
+                       'result': {'type':'form', 'arch':pay_form, 'fields':pay_fields, 'state':[('end','Cancel'),('reconcile','Partial Payment'),('writeoff_check','Full Payment')]}
                },
                'writeoff_check': {
                        'actions': [],
index bee28cb..77f8653 100644 (file)
@@ -64,6 +64,12 @@ def _trans_rec_get(self, cr, uid, data, context=None):
                        account_id = line.account_id.id
        return {'trans_nbr': count, 'account_id': account_id, 'credit': credit, 'debit': debit, 'writeoff': debit - credit}
 
+def _trans_rec_reconcile_partial(self, cr, uid, data, context=None):
+       pool = pooler.get_pool(cr.dbname)
+       account_move_line_obj = pool.get('account.move.line')
+       account_move_line_obj.reconcile_partial(cr, uid, data['ids'], 'manual', context=context)
+       return {}
+
 def _trans_rec_reconcile(self, cr, uid, data, context=None):
        pool = pooler.get_pool(cr.dbname)
        account_move_line_obj = pool.get('account.move.line')
@@ -76,10 +82,10 @@ def _trans_rec_reconcile(self, cr, uid, data, context=None):
                        period_id, journal_id, context=context)
        return {}
 
-def _wo_check(self, cr, uid, data, context):
-       if data['form']['writeoff'] == 0:
-               return 'reconcile'
-       return 'addendum'
+def _partial_check(self, cr, uid, data, context):
+       if _trans_rec_get(self,cr,uid, data, context)['writeoff'] == 0:
+               return 'init_full'
+       return 'init_partial'
 
 _transaction_add_form = '''<?xml version="1.0"?>
 <form string="Information addendum">
@@ -107,12 +113,16 @@ def _trans_rec_addendum(self, cr, uid, data, context={}):
 class wiz_reconcile(wizard.interface):
        states = {
                'init': {
+                       'actions': [],
+                       'result': {'type': 'choice', 'next_state': _partial_check}
+               },
+               'init_full': {
                        'actions': [_trans_rec_get],
-                       'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel'),('writeoff_check','Reconcile')]}
+                       'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel'),('reconcile','Reconcile')]}
                },
-               'writeoff_check': {
-                       'actions': [],
-                       'result' : {'type' : 'choice', 'next_state': _wo_check }
+               'init_partial': {
+                       'actions': [_trans_rec_get],
+                       'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel'),('addendum','Reconcile With Write-Off'),('partial','Partial Reconcile')]}
                },
                'addendum': {
                        'actions': [_trans_rec_addendum],
@@ -121,6 +131,10 @@ class wiz_reconcile(wizard.interface):
                'reconcile': {
                        'actions': [_trans_rec_reconcile],
                        'result': {'type': 'state', 'state':'end'}
+               },
+               'partial': {
+                       'actions': [_trans_rec_reconcile_partial],
+                       'result': {'type': 'state', 'state':'end'}
                }
        }
 wiz_reconcile('account.move.line.reconcile')