[IMP] modified invoices in order to use fiscal position direclty on account.invoice...
[odoo/odoo.git] / addons / account / account.py
index 78a53b0..6564def 100644 (file)
@@ -34,7 +34,7 @@ class account_payment_term(osv.osv):
     _name = "account.payment.term"
     _description = "Payment Term"
     _columns = {
-        'name': fields.char('Payment Term', size=32, translate=True, required=True),
+        'name': fields.char('Payment Term', size=64, translate=True, required=True),
         'active': fields.boolean('Active'),
         'note': fields.text('Description', translate=True),
         'line_ids': fields.one2many('account.payment.term.line', 'payment_id', 'Terms'),
@@ -77,7 +77,8 @@ class account_payment_term_line(osv.osv):
         'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the payment term lines from the lowest sequences to the higher ones"),
         'value': fields.selection([('procent','Percent'),('balance','Balance'),('fixed','Fixed Amount')], 'Value',required=True),
         'value_amount': fields.float('Value Amount'),
-        'days': fields.integer('Number of Days',required=True, help="Number of days to add before computation of the day of month."),
+        'days': fields.integer('Number of Days',required=True, help="Number of days to add before computation of the day of month." \
+            "If Date=15/01, Number of Days=22, Day of Month=-1, then the due date is 28/02."),
         'days2': fields.integer('Day of the Month',required=True, help="Day of the month, set -1 for the last day of the current month. If it's positive, it gives the day of the next month. Set 0 for net days (otherwise it's based on the beginning of the month)."),
         'payment_id': fields.many2one('account.payment.term','Payment Term', required=True, select=True),
     }
@@ -129,6 +130,7 @@ class account_account(osv.osv):
     _name = "account.account"
     _description = "Account"
     _parent_store = True
+    _parent_order = 'length(code),code'
 
     def search(self, cr, uid, args, offset=0, limit=None, order=None,
             context=None, count=False):
@@ -457,6 +459,7 @@ class account_journal(osv.osv):
         'currency': fields.many2one('res.currency', 'Currency', help='The currency used to enter statement'),
         'entry_posted': fields.boolean('Skip \'Draft\' State for Created Entries', help='Check this box if you don\'t want that new account moves pass through the \'draft\' state and goes direclty to the \'posted state\' without any manual validation.'),
         'company_id': fields.related('default_credit_account_id','company_id',type='many2one', relation="res.company", string="Company"),
+        'fy_seq_id': fields.one2many('fiscalyear.seq', 'journal_id', 'Sequences'),
     }
 
     _defaults = {
@@ -494,6 +497,8 @@ class account_fiscalyear(osv.osv):
     _columns = {
         'name': fields.char('Fiscal Year', size=64, required=True),
         'code': fields.char('Code', size=6, required=True),
+        'company_id': fields.many2one('res.company', 'Company',
+            help="Keep empty if the fiscal year belongs to several companies."),
         'date_start': fields.date('Start date', required=True),
         'date_stop': fields.date('End date', required=True),
         'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'),
@@ -556,6 +561,8 @@ class account_period(osv.osv):
     _columns = {
         'name': fields.char('Period Name', size=64, required=True),
         'code': fields.char('Code', size=12),
+        'special': fields.boolean('Opening/Closing Period', size=12,
+            help="These periods can overlap."),
         'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}),
         'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}),
         'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True),
@@ -566,21 +573,28 @@ class account_period(osv.osv):
     }
     _order = "date_start"
 
-    def _check_duration(self,cr,uid,ids):
+    def _check_duration(self,cr,uid,ids,context={}):
         obj_period=self.browse(cr,uid,ids[0])
         if obj_period.date_stop < obj_period.date_start:
             return False
         return True
 
-    def _check_year_limit(self,cr,uid,ids):
-        obj_period=self.browse(cr,uid,ids[0])
-        if obj_period.fiscalyear_id.date_stop < obj_period.date_stop or obj_period.fiscalyear_id.date_stop < obj_period.date_start or obj_period.fiscalyear_id.date_start > obj_period.date_start or obj_period.fiscalyear_id.date_start > obj_period.date_stop:
-            return False
+    def _check_year_limit(self,cr,uid,ids,context={}):
+        for obj_period in self.browse(cr,uid,ids):
+            if obj_period.special:
+                continue
+            if obj_period.fiscalyear_id.date_stop < obj_period.date_stop or obj_period.fiscalyear_id.date_stop < obj_period.date_start or obj_period.fiscalyear_id.date_start > obj_period.date_start or obj_period.fiscalyear_id.date_start > obj_period.date_stop:
+                return False
+
+            pids = self.search(cr, uid, [('date_stop','>=',obj_period.date_start),('date_start','<=',obj_period.date_stop),('special','=',False),('id','<>',obj_period.id)])
+            for period in self.browse(cr, uid, pids):
+                if period.fiscalyear_id.company_id.id==obj_period.fiscalyear_id.company_id.id:
+                    return False
         return True
 
     _constraints = [
         (_check_duration, 'Error ! The date duration of the Period(s) is invalid. ', ['date_stop']),
-        (_check_year_limit, 'Error ! The date duration of the Period(s) should be within the limit of the Fiscal year. ', ['date_stop'])
+        (_check_year_limit, 'Invalid period ! Some periods overlap or the date duration is not in the limit of the fiscal year. ', ['date_stop'])
     ]
 
     def next(self, cr, uid, period, step, context={}):
@@ -668,7 +682,6 @@ class account_fiscalyear(osv.osv):
     _inherit = "account.fiscalyear"
     _description = "Fiscal Year"
     _columns = {
-        'start_journal_period_id':fields.many2one('account.journal.period','New Entries Journal'),
         'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True),
     }
 
@@ -923,7 +936,7 @@ class account_move(osv.osv):
         for move in self.browse(cr, uid, ids, context):
             #unlink analytic lines on move_lines
             for obj_line in move.line_id:
-                for obj in obj_line.analytic_lines: 
+                for obj in obj_line.analytic_lines:
                     self.pool.get('account.analytic.line').unlink(cr,uid,obj.id)
 
             journal = move.journal_id
@@ -1124,6 +1137,19 @@ class account_tax_code(osv.osv):
             res[record.id] = round(_rec_get(record), 2)
         return res
 
+    def _sum_year(self, cr, uid, ids, name, args, context):
+        if 'fiscalyear_id' in context and context['fiscalyear_id']:
+            fiscalyear_id = context['fiscalyear_id']
+        else:
+            fiscalyear_id = self.pool.get('account.fiscalyear').find(cr, uid, exception=False)
+        where = ''
+        if fiscalyear_id:
+             pids = map(lambda x: str(x.id), self.pool.get('account.fiscalyear').browse(cr, uid, fiscalyear_id).period_ids)
+             if pids:
+                 where = ' and period_id in (' + (','.join(pids))+')'
+        return self._sum(cr, uid, ids, name, args, context,
+                where=where)
+
     def _sum_period(self, cr, uid, ids, name, args, context):
         if 'period_id' in context and context['period_id']:
             period_id = context['period_id']
@@ -1142,7 +1168,7 @@ class account_tax_code(osv.osv):
         'name': fields.char('Tax Case Name', size=64, required=True),
         'code': fields.char('Case Code', size=64),
         'info': fields.text('Description'),
-        'sum': fields.function(_sum, method=True, string="Year Sum"),
+        'sum': fields.function(_sum_year, method=True, string="Year Sum"),
         'sum_period': fields.function(_sum_period, method=True, string="Period Sum"),
         'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True),
         'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'),
@@ -1204,8 +1230,10 @@ class account_tax(osv.osv):
         'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the taxes lines from the lowest sequences to the higher ones. The order is important if you have a tax that have several tax childs. In this case, the evaluation order is important."),
         'amount': fields.float('Amount', required=True, digits=(14,4)),
         'active': fields.boolean('Active'),
-        'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
-        'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
+        'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code'),('balance','Balance')], 'Tax Type', required=True,
+            help="The computation method for the tax amount."),
+        'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True,
+            help="If not applicable (computed through a Python code), the tax do not appears on the invoice."),
         'domain':fields.char('Domain', size=32, help="This field is only used if you develop your own module allowing developpers to create specific taxes in a custom domain."),
         'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'),
         'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'),
@@ -1318,13 +1346,31 @@ class account_tax(osv.osv):
                 exec tax.python_compute in localdict
                 amount = localdict['result']
                 data['amount'] = amount
+            elif tax.type=='balance':
+                data['amount'] = cur_price_unit - reduce(lambda x,y: y.get('amount',0.0)+x, res, 0.0)
+                data['balance'] = cur_price_unit
+
             amount2 = data['amount']
             if len(tax.child_ids):
                 if tax.child_depend:
-                    del res[-1]
+                    latest = res.pop()
                 amount = amount2
                 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner)
                 res.extend(child_tax)
+                if tax.child_depend:
+                    for r in res:
+                        for name in ('base','ref_base'):
+                            if latest[name+'_code_id'] and latest[name+'_sign'] and not r[name+'_code_id']:
+                                r[name+'_code_id'] = latest[name+'_code_id']
+                                r[name+'_sign'] = latest[name+'_sign']
+                                r['price_unit'] = latest['price_unit']
+                                latest[name+'_code_id'] = False
+                        for name in ('tax','ref_tax'):
+                            if latest[name+'_code_id'] and latest[name+'_sign'] and not r[name+'_code_id']:
+                                r[name+'_code_id'] = latest[name+'_code_id']
+                                r[name+'_sign'] = latest[name+'_sign']
+                                r['amount'] = data['amount']
+                                latest[name+'_code_id'] = False
             if tax.include_base_amount:
                 cur_price_unit+=amount2
         return res
@@ -1340,8 +1386,14 @@ class account_tax(osv.osv):
             one tax for each tax id in IDS and their childs
         """
         res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner)
+        total = 0.0
         for r in res:
-            r['amount'] *= quantity
+            if r.get('balance',False):
+                r['amount'] = round(r['balance'] * quantity, 2) - total
+            else:
+                r['amount'] = round(r['amount'] * quantity, 2)
+                total += r['amount']
+
         return res
 
     def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
@@ -1371,6 +1423,10 @@ class account_tax(osv.osv):
                 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
                 exec tax.python_compute_inv in localdict
                 amount = localdict['result']
+            elif tax.type=='balance':
+                data['amount'] = cur_price_unit - reduce(lambda x,y: y.get('amount',0.0)+x, res, 0.0)
+                data['balance'] = cur_price_unit
+
 
             if tax.include_base_amount:
                 cur_price_unit -= amount
@@ -1423,8 +1479,13 @@ class account_tax(osv.osv):
             one tax for each tax id in IDS and their childs
         """
         res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
+        total = 0.0
         for r in res:
-            r['amount'] *= quantity
+            if r.get('balance',False):
+                r['amount'] = round(r['balance'] * quantity, 2) - total
+            else:
+                r['amount'] = round(r['amount'] * quantity, 2)
+                total += r['amount']
         return res
 account_tax()
 
@@ -1872,14 +1933,14 @@ account_tax_template()
 class account_fiscal_position_template(osv.osv):
     _name = 'account.fiscal.position.template'
     _description = 'Template for Fiscal Position'
-    
+
     _columns = {
         'name': fields.char('Fiscal Position Template', size=64, translate=True, required=True),
         'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
         'account_ids': fields.one2many('account.fiscal.position.account.template', 'position_id', 'Accounts Mapping'),
         'tax_ids': fields.one2many('account.fiscal.position.tax.template', 'position_id', 'Taxes Mapping')
     }
-    
+
 account_fiscal_position_template()
 
 class account_fiscal_position_tax_template(osv.osv):
@@ -1905,7 +1966,7 @@ class account_fiscal_position_account_template(osv.osv):
         'account_dest_id': fields.many2one('account.account.template', 'Account Destination', domain=[('type','<>','view')], required=True)
     }
 
-account_fiscal_position_account_template() 
+account_fiscal_position_account_template()
 
     # Multi charts of Accounts wizard
 
@@ -2156,19 +2217,19 @@ class wizard_multi_charts_accounts(osv.osv_memory):
                 property_obj.create(cr, uid, vals)
 
         fp_ids = obj_fiscal_position_template.search(cr, uid,[('chart_template_id', '=', obj_multi.chart_template_id.id)])
-        
+
         if fp_ids:
             for position in obj_fiscal_position_template.browse(cr, uid, fp_ids):
-                
+
                 vals_fp = {
                            'company_id' : company_id,
                            'name' : position.name,
                            }
                 new_fp = obj_fiscal_position.create(cr, uid, vals_fp)
-                
+
                 obj_tax_fp = self.pool.get('account.fiscal.position.tax')
                 obj_ac_fp = self.pool.get('account.fiscal.position.account')
-                
+
                 for tax in position.tax_ids:
                     vals_tax = {
                                 'tax_src_id' : tax_template_ref[tax.tax_src_id.id],
@@ -2176,7 +2237,7 @@ class wizard_multi_charts_accounts(osv.osv_memory):
                                 'position_id' : new_fp,
                                 }
                     obj_tax_fp.create(cr, uid, vals_tax)
-                
+
                 for acc in position.account_ids:
                     vals_acc = {
                                 'account_src_id' : acc_template_ref[acc.account_src_id.id],
@@ -2184,7 +2245,7 @@ class wizard_multi_charts_accounts(osv.osv_memory):
                                 'position_id' : new_fp,
                                 }
                     obj_ac_fp.create(cr, uid, vals_acc)
-        
+
         return {
                 'view_type': 'form',
                 "view_mode": 'form',