1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
6 # $Id: account.py 1005 2005-07-25 08:41:42Z nicoe $
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 ##############################################################################
32 from mx import DateTime
33 from mx.DateTime import now
37 from osv import fields, osv,orm
42 class one2many_mod2(fields.one2many):
43 def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
50 if 'journal_id' in context:
51 journal = obj.pool.get('account.journal').browse(cr, user, context['journal_id'], context)
52 pnum = int(name[7]) -1
53 plan = journal.plan_id
54 if plan and len(plan.plan_ids)>pnum:
55 acc_id = plan.plan_ids[pnum].root_analytic_id.id
56 ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id,'in',ids),('analytic_account_id','child_of',[acc_id])], limit=self._limit)
58 ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id,'in',ids)], limit=self._limit)
59 for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
60 res[r[self._fields_id]].append( r['id'] )
63 class account_analytic_plan(osv.osv):
64 _name = "account.analytic.plan"
65 _description = "Analytic Plans"
67 'name': fields.char('Analytic Plan', size=64, required=True, select=True,),
68 'plan_ids': fields.one2many('account.analytic.plan.line','plan_id','Analytic Plans'),
70 account_analytic_plan()
72 class account_analytic_plan_line(osv.osv):
73 _name = "account.analytic.plan.line"
74 _description = "Analytic Plan Lines"
76 'plan_id':fields.many2one('account.analytic.plan','Analytic Plan'),
77 'name': fields.char('Plan Name', size=64, required=True, select=True),
78 'sequence':fields.integer('Sequence'),
79 'root_analytic_id': fields.many2one('account.analytic.account','Root Account',help="Root account of this plan.",required=True),
80 'min_required': fields.float('Minimum Allowed (%)'),
81 'max_required': fields.float('Maximum Allowed (%)'),
84 'min_required': lambda *args: 100.0,
85 'max_required': lambda *args: 100.0,
87 _order = "sequence,id"
88 account_analytic_plan_line()
90 class account_analytic_plan_instance(osv.osv):
91 _name='account.analytic.plan.instance'
92 _description = 'Analytic Plan Instance'
94 'name':fields.char('Analytic Distribution',size=64),
95 'code':fields.char('Distribution Code',size=16),
96 'journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal', required=True),
97 'account_ids':fields.one2many('account.analytic.plan.instance.line','plan_id','Account Id'),
98 'account1_ids':one2many_mod2('account.analytic.plan.instance.line','plan_id','Account1 Id'),
99 'account2_ids':one2many_mod2('account.analytic.plan.instance.line','plan_id','Account2 Id'),
100 'account3_ids':one2many_mod2('account.analytic.plan.instance.line','plan_id','Account3 Id'),
101 'account4_ids':one2many_mod2('account.analytic.plan.instance.line','plan_id','Account4 Id'),
102 'account5_ids':one2many_mod2('account.analytic.plan.instance.line','plan_id','Account5 Id'),
103 'account6_ids':one2many_mod2('account.analytic.plan.instance.line','plan_id','Account6 Id'),
104 'plan_id':fields.many2one('account.analytic.plan', "Model's Plan"),
106 def copy(self, cr, uid, id, default=None, context=None):
109 default.update({'account1_ids':False, 'account2_ids':False, 'account3_ids':False,
110 'account4_ids':False, 'account5_ids':False, 'account6_ids':False})
111 return super(account_analytic_plan_instance, self).copy(cr, uid, id, default, context)
114 'plan_id': lambda *args: False,
116 def name_get(self, cr, uid, ids, context={}):
118 for inst in self.browse(cr, uid, ids, context):
119 name = inst.name or '/'
120 if name and inst.code:
121 name=name+' ('+inst.code+')'
122 res.append((inst.id, name))
125 def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=80):
128 ids = self.search(cr, uid, [('code', '=', name)] + args, limit=limit, context=context or {})
130 ids = self.search(cr, uid, [('name', operator, name)] + args, limit=limit, context=context or {})
132 ids = self.search(cr, uid, args, limit=limit, context=context or {})
133 return self.name_get(cr, uid, ids, context or {})
135 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False):
136 wiz_id = self.pool.get('ir.actions.wizard').search(cr, uid, [("wiz_name","=","create.model")])
137 res = super(account_analytic_plan_instance,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar)
138 if (res['type']=='form'):
140 if context.get('journal_id',False):
141 plan_id = self.pool.get('account.journal').browse(cr, uid, int(context['journal_id']), context).plan_id
142 elif context.get('plan_id',False):
143 plan_id = self.pool.get('account.analytic.plan').browse(cr, uid, int(context['plan_id']), context).plan_id
146 res['arch'] = """<form string="%s">
149 <field name="journal_id"/>
150 <button name="%d" string="Save This Distribution as a Model" type="action" colspan="2"/>
151 """% (tools.to_xml(plan_id.name), wiz_id[0])
152 for line in plan_id.plan_ids:
154 <field name="account%d_ids" string="%s" colspan="4">
155 <tree string="%s" editable="bottom">
157 <field name="analytic_account_id" domain="[('parent_id','child_of',[%d])]"/>
160 <newline/>"""%(i,tools.to_xml(line.name),tools.to_xml(line.name),line.root_analytic_id and line.root_analytic_id.id or 0)
162 res['arch'] += "</form>"
163 doc = dom.minidom.parseString(res['arch'])
164 xarch, xfields = self._view_look_dom_arch(cr, uid, doc, context=context)
166 res['fields'] = xfields
171 def create(self, cr, uid, vals, context=None):
172 if context and 'journal_id' in context:
173 journal= self.pool.get('account.journal').browse(cr,uid,context['journal_id'])
175 pids = self.pool.get('account.analytic.plan.instance').search(cr, uid, [('name','=',vals['name']),('code','=',vals['code']),('plan_id','<>',False)])
177 raise osv.except_osv('Error', 'A model having this name and code already exists !')
179 res = self.pool.get('account.analytic.plan.line').search(cr,uid,[('plan_id','=',journal.plan_id.id)])
182 item = self.pool.get('account.analytic.plan.line').browse(cr,uid,i)
183 temp_list=['account1_ids','account2_ids','account3_ids','account4_ids','account5_ids','account6_ids']
186 for tempo in vals[l]:
187 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'])]):
188 total_per_plan += tempo[2]['rate']
189 if total_per_plan < item.min_required or total_per_plan > item.max_required:
190 raise osv.except_osv("Value Error" ,"The Total Should be Between " + str(item.min_required) + " and " + str(item.max_required))
192 return super(account_analytic_plan_instance, self).create(cr, uid, vals, context)
194 def write(self, cr, uid, ids, vals, context={}, check=True, update_check=True):
195 this = self.browse(cr,uid,ids[0])
196 if this.plan_id and not vals.has_key('plan_id'):
197 #this instance is a model, so we have to create a new plan instance instead of modifying it
198 #copy the existing model
199 temp_id = self.copy(cr, uid, this.id, None, context)
200 #get the list of the invoice line that were linked to the model
201 list = self.pool.get('account.invoice.line').search(cr,uid,[('analytics_id','=',this.id)])
202 #make them link to the copy
203 self.pool.get('account.invoice.line').write(cr, uid, list, {'analytics_id':temp_id}, context)
205 #and finally modify the old model to be not a model anymore
206 vals['plan_id'] = False
207 if not vals.has_key['name']:
208 vals['name'] = this.name+'*'
209 if not vals.has_key['code']:
210 vals['code'] = this.code+'*'
211 return self.write(cr, uid, [this.id],vals, context)
213 #this plan instance isn't a model, so a simple write is fine
214 return super(account_analytic_plan_instance, self).write(cr, uid, ids, vals, context)
216 account_analytic_plan_instance()
218 class account_analytic_plan_instance_line(osv.osv):
219 _name='account.analytic.plan.instance.line'
220 _description = 'Analytic Instance Line'
222 'plan_id':fields.many2one('account.analytic.plan.instance','Plan Id'),
223 'analytic_account_id':fields.many2one('account.analytic.account','Analytic Account', required=True),
224 'rate':fields.float('Rate (%)', required=True),
226 def name_get(self, cr, uid, ids, context={}):
229 reads = self.read(cr, uid, ids, ['analytic_account_id'], context)
232 res.append((record['id'], record['analytic_account_id']))
235 account_analytic_plan_instance_line()
237 class account_journal(osv.osv):
238 _inherit='account.journal'
239 _name='account.journal'
241 'plan_id':fields.many2one('account.analytic.plan','Analytic Plans'),
245 class account_invoice_line(osv.osv):
246 _inherit='account.invoice.line'
247 _name='account.invoice.line'
249 'analytics_id':fields.many2one('account.analytic.plan.instance','Analytic Distribution'),
252 def create(self, cr, uid, vals, context=None):
253 if 'analytics_id' in vals and isinstance(vals['analytics_id'],tuple):
254 vals['analytics_id'] = vals['analytics_id'][0]
255 return super(account_invoice_line, self).create(cr, uid, vals, context)
257 def move_line_get_item(self, cr, uid, line, context={}):
258 res= super(account_invoice_line,self).move_line_get_item(cr, uid, line, context={})
259 res ['analytics_id']=line.analytics_id and line.analytics_id.id or False
262 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={}):
263 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)
264 rec = self.pool.get('account.analytic.default').account_get(cr, uid, product, partner_id, uid, time.strftime('%Y-%m-%d'), context)
265 if rec and rec.analytics_id:
266 res_prod['value'].update({'analytics_id':rec.analytics_id.id})
268 account_invoice_line()
270 class account_move_line(osv.osv):
271 _inherit='account.move.line'
272 _name='account.move.line'
274 'analytics_id':fields.many2one('account.analytic.plan.instance','Analytic Distribution'),
276 # def _analytic_update(self, cr, uid, ids, context):
277 # for line in self.browse(cr, uid, ids, context):
278 # if line.analytics_id:
279 # print "line.analytics_id",line,"now",line.analytics_id
280 # toremove = self.pool.get('account.analytic.line').search(cr, uid, [('move_id','=',line.id)], context=context)
281 # print "toremove",toremove
283 # obj_line=self.pool.get('account.analytic.line')
284 # self.pool.get('account.analytic.line').unlink(cr, uid, toremove, context=context)
285 # for line2 in line.analytics_id.account_ids:
286 # val = (line.debit or 0.0) - (line.credit or 0.0)
287 # amt=val * (line2.rate/100)
293 # 'account_id': line2.analytic_account_id.id,
295 # 'general_account_id': line.account_id.id,
296 # 'move_id': line.id,
297 # 'journal_id': line.analytics_id.journal_id.id,
300 # ali_id=self.pool.get('account.analytic.line').create(cr,uid,al_vals)
303 # def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True):
304 # result = super(account_move_line, self).write(cr, uid, ids, vals, context, check, update_check)
305 # self._analytic_update(cr, uid, ids, context)
308 # def create(self, cr, uid, vals, context=None, check=True):
309 # result = super(account_move_line, self).create(cr, uid, vals, context, check)
310 # self._analytic_update(cr, uid, [result], context)
314 class account_invoice(osv.osv):
315 _name = "account.invoice"
316 _inherit="account.invoice"
320 def line_get_convert(self, cr, uid, x, part, date, context={}):
321 res=super(account_invoice,self).line_get_convert(cr, uid, x, part, date, context)
322 res['analytics_id']=x.get('analytics_id',False)
325 def _get_analityc_lines(self, cr, uid, id):
326 inv = self.browse(cr, uid, [id])[0]
327 cur_obj = self.pool.get('res.currency')
329 company_currency = inv.company_id.currency_id.id
330 if inv.type in ('out_invoice', 'in_refund'):
335 iml = self.pool.get('account.invoice.line').move_line_get(cr, uid, inv.id)
338 if il['analytics_id']:
340 if inv.type in ('in_invoice', 'in_refund'):
343 ref = self._convert_ref(cr, uid, inv.number)
344 obj_move_line=self.pool.get('account.analytic.plan.instance').browse(cr,uid,il['analytics_id'])
345 amount_calc=cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, il['price'], context={'date': inv.date_invoice}) * sign
347 il['analytic_lines']=[]
348 for line2 in obj_move_line.account_ids:
349 amt=amount_calc * (line2.rate/100)
350 qtty=qty* (line2.rate/100)
353 'date': inv['date_invoice'],
355 'product_id':il['product_id'],
356 'account_id': line2.analytic_account_id.id,
358 'product_uom_id': il['uos_id'],
359 'general_account_id': il['account_id'],
360 'journal_id': self._get_journal_analytic(cr, uid, inv.type),
363 il['analytic_lines'].append((0,0,al_vals))
368 class account_analytic_plan(osv.osv):
369 _inherit = "account.analytic.plan"
371 'default_instance_id': fields.many2one('account.analytic.plan.instance', 'Default Entries'),
373 account_analytic_plan()
375 class analytic_default(osv.osv):
376 _inherit = 'account.analytic.default'
378 'analytics_id': fields.many2one('account.analytic.plan.instance', 'Analytic Distribution'),