modifs
[odoo/odoo.git] / addons / account_analytic_plans / account_analytic_plans.py
index 80fffb3..7f1e385 100644 (file)
@@ -1,33 +1,24 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
 ##############################################################################
 #
-# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
 #
-# $Id: account.py 1005 2005-07-25 08:41:42Z nicoe $
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
 #
-# WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsability of assessing all potential
-# consequences resulting from its eventual inadequacies and bugs
-# End users who are looking for a ready-to-use solution with commercial
-# garantees and support are strongly adviced to contract a Free Software
-# Service Company
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
 #
-# This program is Free Software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
-from xml import dom
+from lxml import etree
 
 from mx import DateTime
 from mx.DateTime import now
@@ -38,6 +29,7 @@ from osv import fields, osv,orm
 import ir
 
 import tools
+from tools.translate import _
 
 class one2many_mod2(fields.one2many):
     def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
@@ -89,11 +81,11 @@ account_analytic_plan_line()
 
 class account_analytic_plan_instance(osv.osv):
     _name='account.analytic.plan.instance'
-    _description = 'Object for create analytic entries from invoice lines'
+    _description = 'Analytic Plan Instance'
     _columns={
         'name':fields.char('Analytic Distribution',size=64),
         'code':fields.char('Distribution Code',size=16),
-        'journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal', required=True),
+        'journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal' ),
         'account_ids':fields.one2many('account.analytic.plan.instance.line','plan_id','Account Id'),
         'account1_ids':one2many_mod2('account.analytic.plan.instance.line','plan_id','Account1 Id'),
         'account2_ids':one2many_mod2('account.analytic.plan.instance.line','plan_id','Account2 Id'),
@@ -103,6 +95,18 @@ class account_analytic_plan_instance(osv.osv):
         'account6_ids':one2many_mod2('account.analytic.plan.instance.line','plan_id','Account6 Id'),
         'plan_id':fields.many2one('account.analytic.plan', "Model's Plan"),
     }
+
+    def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
+        if context.get('journal_id', False):
+            journal = self.pool.get('account.journal').browse(cr, user, [context['journal_id']], context=context)[0]
+            analytic_journal = journal.analytic_journal_id and journal.analytic_journal_id.id or False
+            args.append('|')
+            args.append(('journal_id', '=', analytic_journal))
+            args.append(('journal_id', '=', False))
+        res = super(account_analytic_plan_instance, self).search(cr, user, args, offset=offset, limit=limit, order=order,
+                                                                 context=context, count=count)
+        return res
+
     def copy(self, cr, uid, id, default=None, context=None):
         if not default:
             default = {}
@@ -110,8 +114,16 @@ class account_analytic_plan_instance(osv.osv):
                 'account4_ids':False, 'account5_ids':False, 'account6_ids':False})
         return super(account_analytic_plan_instance, self).copy(cr, uid, id, default, context)
 
+    def _default_journal(self, cr, uid, context={}):
+        if context.has_key('journal_id') and context['journal_id']:
+            journal = self.pool.get('account.journal').browse(cr, uid, context['journal_id'])
+            if journal.analytic_journal_id:
+                return journal.analytic_journal_id.id
+        return False
+
     _defaults = {
         'plan_id': lambda *args: False,
+        'journal_id': _default_journal,
     }
     def name_get(self, cr, uid, ids, context={}):
         res = []
@@ -122,7 +134,7 @@ class account_analytic_plan_instance(osv.osv):
             res.append((inst.id, name))
         return res
 
-    def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=80):
+    def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
         args= args or []
         if name:
             ids = self.search(cr, uid, [('code', '=', name)] + args, limit=limit, context=context or {})
@@ -132,15 +144,17 @@ class account_analytic_plan_instance(osv.osv):
             ids = self.search(cr, uid, args, limit=limit, context=context or {})
         return self.name_get(cr, uid, ids, context or {})
 
-    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False):
+    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
         wiz_id = self.pool.get('ir.actions.wizard').search(cr, uid, [("wiz_name","=","create.model")])
-        res = super(account_analytic_plan_instance,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar)
+        res = super(account_analytic_plan_instance,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
+
         if (res['type']=='form'):
             plan_id = False
             if context.get('journal_id',False):
                 plan_id = self.pool.get('account.journal').browse(cr, uid, int(context['journal_id']), context).plan_id
             elif context.get('plan_id',False):
-                plan_id = self.pool.get('account.analytic.plan').browse(cr, uid, int(context['plan_id']), context).plan_id
+                plan_id = self.pool.get('account.analytic.plan').browse(cr, uid, int(context['plan_id']), context)
+
             if plan_id:
                 i=1
                 res['arch'] = """<form string="%s">
@@ -160,8 +174,8 @@ class account_analytic_plan_instance(osv.osv):
                 <newline/>"""%(i,tools.to_xml(line.name),tools.to_xml(line.name),line.root_analytic_id and line.root_analytic_id.id or 0)
                     i+=1
                 res['arch'] += "</form>"
-                doc = dom.minidom.parseString(res['arch'])
-                xarch, xfields = self._view_look_dom_arch(cr, uid, doc, context=context)
+                doc = etree.fromstring(res['arch'].encode('utf8'))
+                xarch, xfields = self._view_look_dom_arch(cr, uid, doc, view_id, context=context)
                 res['arch'] = xarch
                 res['fields'] = xfields
             return res
@@ -171,11 +185,10 @@ class account_analytic_plan_instance(osv.osv):
     def create(self, cr, uid, vals, context=None):
         if context and 'journal_id' in context:
             journal= self.pool.get('account.journal').browse(cr,uid,context['journal_id'])
-            vals.update({'plan_id': journal.plan_id.id})
 
             pids = self.pool.get('account.analytic.plan.instance').search(cr, uid, [('name','=',vals['name']),('code','=',vals['code']),('plan_id','<>',False)])
             if pids:
-                raise osv.except_osv('Error', 'A model having this name and code already exists !')
+                raise osv.except_osv(_('Error'), _('A model having this name and code already exists !'))
 
             res = self.pool.get('account.analytic.plan.line').search(cr,uid,[('plan_id','=',journal.plan_id.id)])
             for i in res:
@@ -188,43 +201,34 @@ class account_analytic_plan_instance(osv.osv):
                             if self.pool.get('account.analytic.account').search(cr,uid,[('parent_id','child_of',[item.root_analytic_id.id]),('id','=',tempo[2]['analytic_account_id'])]):
                                 total_per_plan += tempo[2]['rate']
                 if total_per_plan < item.min_required or total_per_plan > item.max_required:
-                    raise osv.except_osv("Value Error" ,"The Total Should be Between " + str(item.min_required) + " and " + str(item.max_required))
+                    raise osv.except_osv(_('Value Error') ,_('The Total Should be Between %s and %s') % (str(item.min_required), str(item.max_required)))
 
         return super(account_analytic_plan_instance, self).create(cr, uid, vals, context)
 
     def write(self, cr, uid, ids, vals, context={}, check=True, update_check=True):
         this = self.browse(cr,uid,ids[0])
-        res = self.pool.get('account.analytic.plan.line').search(cr,uid,[('plan_id','=',this.plan_id.id)])
-        for i in res:
-            item = self.pool.get('account.analytic.plan.line').browse(cr,uid,i)
-            total_per_plan = 0
-            for j in self.pool.get('account.analytic.plan.instance.line').search(cr,uid,[('plan_id','=',this.id),('analytic_account_id','child_of',[item.root_analytic_id.id])]):
-                plan_line = self.pool.get('account.analytic.plan.instance.line').browse(cr,uid,j)
-                unchanged = True
-                temp_list=['account1_ids','account2_ids','account3_ids','account4_ids','account5_ids','account6_ids']
-                for l in temp_list:
-                    if vals.has_key(l):
-                        for tempo in vals[l]:
-                            if tempo[1] == plan_line.id:
-                                unchanged = False
-                                if tempo[2] != False:
-                                    total_per_plan += tempo[2]['rate']
-                            if tempo[1] == 0:
-                                if self.pool.get('account.analytic.account').search(cr,uid,[('parent_id','child_of',[item.root_analytic_id.id]),('id','=',tempo[2]['analytic_account_id'])]):
-                                    total_per_plan += tempo[2]['rate']
-                if unchanged:
-                    total_per_plan += plan_line.rate
-            if total_per_plan < item.min_required or total_per_plan > item.max_required:
-                raise osv.except_osv("Value Error" ,"The Total Should be Between " + str(item.min_required) + " and " + str(item.max_required))
-        if context.get('journal_id',False):
-            new_copy=self.copy(cr, uid, ids[0], context=context)
-            vals['plan_id']=this.plan_id.id
+        if this.plan_id and not vals.has_key('plan_id'):
+            #this instance is a model, so we have to create a new plan instance instead of modifying it
+            #copy the existing model
+            temp_id = self.copy(cr, uid, this.id, None, context)
+            #get the list of the invoice line that were linked to the model
+            list = self.pool.get('account.invoice.line').search(cr,uid,[('analytics_id','=',this.id)])
+            #make them link to the copy
+            self.pool.get('account.invoice.line').write(cr, uid, list, {'analytics_id':temp_id}, context)
+
+            #and finally modify the old model to be not a model anymore
+            vals['plan_id'] = False
+            if not vals.has_key('name'):
+                vals['name'] = this.name and (str(this.name)+'*') or "*"
+            if not vals.has_key('code'):
+                vals['code'] = this.code and (str(this.code)+'*') or "*"
         return super(account_analytic_plan_instance, self).write(cr, uid, ids, vals, context)
+
 account_analytic_plan_instance()
 
 class account_analytic_plan_instance_line(osv.osv):
     _name='account.analytic.plan.instance.line'
-    _description = 'Object for create analytic entries from invoice lines'
+    _description = 'Analytic Instance Line'
     _columns={
         'plan_id':fields.many2one('account.analytic.plan.instance','Plan Id'),
         'analytic_account_id':fields.many2one('account.analytic.account','Analytic Account', required=True),
@@ -261,77 +265,74 @@ class account_invoice_line(osv.osv):
             vals['analytics_id'] = vals['analytics_id'][0]
         return super(account_invoice_line, self).create(cr, uid, vals, context)
 
-    def move_line_get_item(self, cr, uid, line, context={}):
+    def move_line_get_item(self, cr, uid, line, context=None):
         res= super(account_invoice_line,self).move_line_get_item(cr, uid, line, context={})
         res ['analytics_id']=line.analytics_id and line.analytics_id.id or False
         return res
 
-    def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, price_unit=False, address_invoice_id=False, context={}):
-        res_prod = super(account_invoice_line,self).product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, price_unit, address_invoice_id, context)
-        if product:
-            res = self.pool.get('product.product').browse(cr, uid, product, context=context)
-            res_prod['value'].update({'analytics_id':res.property_account_distribution.id})
+    def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, address_invoice_id=False, currency_id=False, context=None):
+        res_prod = super(account_invoice_line,self).product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, fposition_id, price_unit, address_invoice_id, currency_id, context=context)
+        rec = self.pool.get('account.analytic.default').account_get(cr, uid, product, partner_id, uid, time.strftime('%Y-%m-%d'), context)
+        if rec and rec.analytics_id:
+            res_prod['value'].update({'analytics_id':rec.analytics_id.id})
         return res_prod
-
-
 account_invoice_line()
 
 class account_move_line(osv.osv):
+
     _inherit='account.move.line'
     _name='account.move.line'
     _columns = {
         'analytics_id':fields.many2one('account.analytic.plan.instance','Analytic Distribution'),
     }
-#   def _analytic_update(self, cr, uid, ids, context):
-#       for line in self.browse(cr, uid, ids, context):
-#           if line.analytics_id:
-#               print "line.analytics_id",line,"now",line.analytics_id
-#               toremove = self.pool.get('account.analytic.line').search(cr, uid, [('move_id','=',line.id)], context=context)
-#               print "toremove",toremove
-#               if toremove:
-#                   obj_line=self.pool.get('account.analytic.line')
-#                   self.pool.get('account.analytic.line').unlink(cr, uid, toremove, context=context)
-#               for line2 in line.analytics_id.account_ids:
-#                   val = (line.debit or 0.0) - (line.credit or  0.0)
-#                   amt=val * (line2.rate/100)
-#                   al_vals={
-#                       'name': line.name,
-#                       'date': line.date,
-#                       'unit_amount':1,
-#                       'product_id':12,
-#                       'account_id': line2.analytic_account_id.id,
-#                       'amount': amt,
-#                       'general_account_id': line.account_id.id,
-#                       'move_id': line.id,
-#                       'journal_id': line.analytics_id.journal_id.id,
-#                       'ref': line.ref,
-#                   }
-#                   ali_id=self.pool.get('account.analytic.line').create(cr,uid,al_vals)
-#       return True
-#
-#   def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True):
-#       result = super(account_move_line, self).write(cr, uid, ids, vals, context, check, update_check)
-#       self._analytic_update(cr, uid, ids, context)
-#       return result
-#
-#   def create(self, cr, uid, vals, context=None, check=True):
-#       result = super(account_move_line, self).create(cr, uid, vals, context, check)
-#       self._analytic_update(cr, uid, [result], context)
-#       return result
+
+    def _default_get_move_form_hook(self, cursor, user, data):
+        data = super(account_move_line, self)._default_get_move_form_hook(cursor, user, data)
+        if data.has_key('analytics_id'):
+            del(data['analytics_id'])
+        return data
+
+    def create_analytic_lines(self, cr, uid, ids, context={}):
+        super(account_move_line, self).create_analytic_lines(cr, uid, ids, context)
+        for line in self.browse(cr, uid, ids, context):
+           if line.analytics_id:
+               if not line.journal_id.analytic_journal_id:
+                   raise osv.except_osv(_('No Analytic Journal !'),_("You have to define an analytic journal on the '%s' journal!") % (line.journal_id.name,))
+
+               toremove = self.pool.get('account.analytic.line').search(cr, uid, [('move_id','=',line.id)], context=context)
+               if toremove:
+                    line.unlink(cr, uid, toremove, context=context)
+               for line2 in line.analytics_id.account_ids:
+                   val = (line.credit or  0.0) - (line.debit or 0.0)
+                   amt=val * (line2.rate/100)
+                   al_vals={
+                       'name': line.name,
+                       'date': line.date,
+                       'account_id': line2.analytic_account_id.id,
+                       'unit_amount': line.quantity,
+                       'product_id': line.product_id and line.product_id.id or False,
+                       'product_uom_id': line.product_uom_id and line.product_uom_id.id or False,
+                       'amount': amt,
+                       'general_account_id': line.account_id.id,
+                       'move_id': line.id,
+                       'journal_id': line.journal_id.analytic_journal_id.id,
+                       'ref': line.ref,
+                   }
+                   ali_id=self.pool.get('account.analytic.line').create(cr,uid,al_vals)
+        return True
+
 account_move_line()
 
 class account_invoice(osv.osv):
     _name = "account.invoice"
     _inherit="account.invoice"
 
-
-
     def line_get_convert(self, cr, uid, x, part, date, context={}):
         res=super(account_invoice,self).line_get_convert(cr, uid, x, part, date, context)
         res['analytics_id']=x.get('analytics_id',False)
         return res
 
-    def _get_analityc_lines(self, cr, uid, id):
+    def _get_analytic_lines(self, cr, uid, id):
         inv = self.browse(cr, uid, [id])[0]
         cur_obj = self.pool.get('res.currency')
 
@@ -381,24 +382,31 @@ class account_analytic_plan(osv.osv):
     }
 account_analytic_plan()
 
-class product_product(osv.osv):
-    _name = 'product.product'
-    _inherit = 'product.product'
-    _description = 'Product'
-
+class analytic_default(osv.osv):
+    _inherit = 'account.analytic.default'
     _columns = {
-        'property_account_distribution': fields.property(
-            'account.analytic.plan.instance',
-            type='many2one',
-            relation='account.analytic.plan.instance',
-            string="Analytic Distribution",
-            method=True,
-            view_load=True,
-            group_name="Accounting Properties",
-            help="This Analytic Distribution will be use in sale order line and invoice lines",
-            ),
-                }
-
-product_product()
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+        'analytics_id': fields.many2one('account.analytic.plan.instance', 'Analytic Distribution'),
+    }
+analytic_default()
+
+class sale_order_line(osv.osv):
+    _inherit = 'sale.order.line'
+
+    # Method overridden to set the analytic account by default on criterion match
+    def invoice_line_create(self, cr, uid, ids, context={}):
+        create_ids = super(sale_order_line,self).invoice_line_create(cr, uid, ids, context)
+        if ids:
+            sale_line_obj = self.browse(cr, uid, ids[0], context)
+            pool_inv_line = self.pool.get('account.invoice.line')
+
+            for line in pool_inv_line.browse(cr, uid, create_ids, context):
+                rec = self.pool.get('account.analytic.default').account_get(cr, uid, line.product_id.id, sale_line_obj.order_id.partner_id.id, uid, time.strftime('%Y-%m-%d'), context)
 
+                if rec:
+                    pool_inv_line.write(cr, uid, [line.id], {'analytics_id':rec.analytics_id.id}, context=context)
+        return create_ids
+
+sale_order_line()
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: