merged two similar features to centralize the invoice lines if possible. Kept the...
[odoo/odoo.git] / addons / account / account.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
6 #    $Id$
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation, either version 3 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22 import time
23 import netsvc
24 from osv import fields, osv
25
26 from tools.misc import currency
27 from tools.translate import _
28 import pooler
29 import mx.DateTime
30 from mx.DateTime import RelativeDateTime, now, DateTime, localtime
31
32
33 class account_payment_term(osv.osv):
34     _name = "account.payment.term"
35     _description = "Payment Term"
36     _columns = {
37         'name': fields.char('Payment Term', size=32, translate=True, required=True),
38         'active': fields.boolean('Active'),
39         'note': fields.text('Description', translate=True),
40         'line_ids': fields.one2many('account.payment.term.line', 'payment_id', 'Terms'),
41     }
42     _defaults = {
43         'active': lambda *a: 1,
44     }
45     _order = "name"
46
47     def compute(self, cr, uid, id, value, date_ref=False, context={}):
48         if not date_ref:
49             date_ref = now().strftime('%Y-%m-%d')
50         pt = self.browse(cr, uid, id, context)
51         amount = value
52         result = []
53         for line in pt.line_ids:
54             if line.value=='fixed':
55                 amt = round(line.value_amount, 2)
56             elif line.value=='procent':
57                 amt = round(value * line.value_amount, 2)
58             elif line.value=='balance':
59                 amt = round(amount, 2)
60             if amt:
61                 next_date = mx.DateTime.strptime(date_ref, '%Y-%m-%d') + RelativeDateTime(days=line.days)
62                 if line.days2<0:
63                     next_date += RelativeDateTime(day=line.days2)
64                 if line.days2>0:
65                     next_date += RelativeDateTime(day=line.days2, months=1)
66                 result.append( (next_date.strftime('%Y-%m-%d'), amt) )
67                 amount -= amt
68         return result
69
70 account_payment_term()
71
72 class account_payment_term_line(osv.osv):
73     _name = "account.payment.term.line"
74     _description = "Payment Term Line"
75     _columns = {
76         'name': fields.char('Line Name', size=32,required=True),
77         '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"),
78         'value': fields.selection([('procent','Percent'),('balance','Balance'),('fixed','Fixed Amount')], 'Value',required=True),
79         'value_amount': fields.float('Value Amount'),
80         'days': fields.integer('Number of Days',required=True, help="Number of days to add before computation of the day of month."),
81         '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)."),
82         'payment_id': fields.many2one('account.payment.term','Payment Term', required=True, select=True),
83     }
84     _defaults = {
85         'value': lambda *a: 'balance',
86         'sequence': lambda *a: 5,
87         'days2': lambda *a: 0,
88     }
89     _order = "sequence"
90 account_payment_term_line()
91
92
93 class account_account_type(osv.osv):
94     _name = "account.account.type"
95     _description = "Account Type"
96     _columns = {
97         'name': fields.char('Acc. Type Name', size=64, required=True, translate=True),
98         'code': fields.char('Code', size=32, required=True),
99         'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of account types."),
100         'partner_account': fields.boolean('Partner account'),
101         'close_method': fields.selection([('none','None'), ('balance','Balance'), ('detail','Detail'),('unreconciled','Unreconciled')], 'Deferral Method', required=True),
102         'sign': fields.selection([(-1, 'Negative'), (1, 'Positive')], 'Sign on Reports', required=True, help='Allows to change the displayed amount of the balance in the reports, in order to see positive results instead of negative ones in expenses accounts.'),
103     }
104     _defaults = {
105         'close_method': lambda *a: 'none',
106         'sequence': lambda *a: 5,
107         'sign': lambda *a: 1,
108     }
109     _order = "sequence"
110 account_account_type()
111
112 def _code_get(self, cr, uid, context={}):
113     acc_type_obj = self.pool.get('account.account.type')
114     ids = acc_type_obj.search(cr, uid, [])
115     res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context)
116     return [(r['code'], r['name']) for r in res]
117
118 #----------------------------------------------------------
119 # Accounts
120 #----------------------------------------------------------
121
122 class account_tax(osv.osv):
123     _name = 'account.tax'
124 account_tax()
125
126 class account_account(osv.osv):
127     _order = "parent_left"
128     _parent_order = "code"
129     _name = "account.account"
130     _description = "Account"
131     _parent_store = True
132
133     def search(self, cr, uid, args, offset=0, limit=None, order=None,
134             context=None, count=False):
135         if context is None:
136             context = {}
137         pos = 0
138
139         while pos<len(args):
140
141             if args[pos][0]=='code' and args[pos][1] in ('like','ilike') and args[pos][2]:
142                 args[pos] = ('code', '=like', str(args[pos][2].replace('%',''))+'%')
143             if args[pos][0]=='journal_id':
144                 if not args[pos][2]:
145                     del args[pos]
146                     continue
147                 jour = self.pool.get('account.journal').browse(cr, uid, args[pos][2])
148                 if (not (jour.account_control_ids or jour.type_control_ids)) or not args[pos][2]:
149                     del args[pos]
150                     continue
151                 ids3 = map(lambda x: x.code, jour.type_control_ids)
152                 ids1 = super(account_account,self).search(cr, uid, [('type','in',ids3)])
153                 ids1 += map(lambda x: x.id, jour.account_control_ids)
154                 args[pos] = ('id','in',ids1)
155             pos+=1
156
157         if context and context.has_key('consolidate_childs'): #add consolidated childs of accounts
158             ids = super(account_account,self).search(cr, uid, args, offset, limit,
159                 order, context=context, count=count)
160             for consolidate_child in self.browse(cr, uid, context['account_id']).child_consol_ids:
161                 ids.append(consolidate_child.id)
162             return ids
163
164         return super(account_account,self).search(cr, uid, args, offset, limit,
165                 order, context=context, count=count)
166
167     def _get_children_and_consol(self, cr, uid, ids, context={}):
168         #this function search for all the children and all consolidated children (recursively) of the given account ids
169         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)], context=context)
170         ids3 = []
171         for rec in self.browse(cr, uid, ids2, context=context):
172             for child in rec.child_consol_ids:
173                 ids3.append(child.id)
174         if ids3:
175             ids3 = self._get_children_and_consol(cr, uid, ids3, context)
176         return ids2+ids3
177
178     def __compute(self, cr, uid, ids, field_names, arg, context={}, query=''):
179         #compute the balance/debit/credit accordingly to the value of field_name for the given account ids
180         mapping = {
181             'balance': "COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance ",
182             'debit': "COALESCE(SUM(l.debit), 0) as debit ",
183             'credit': "COALESCE(SUM(l.credit), 0) as credit "
184         }
185         #get all the necessary accounts
186         ids2 = self._get_children_and_consol(cr, uid, ids, context)
187         acc_set = ",".join(map(str, ids2))
188         #compute for each account the balance/debit/credit from the move lines
189         accounts = {}
190         if ids2:
191             query = self.pool.get('account.move.line')._query_get(cr, uid,
192                     context=context)
193             cr.execute(("SELECT l.account_id as id, " +\
194                     ' , '.join(map(lambda x: mapping[x], field_names)) +
195                     "FROM " \
196                         "account_move_line l " \
197                     "WHERE " \
198                         "l.account_id IN (%s) " \
199                         "AND " + query + " " \
200                     "GROUP BY l.account_id") % (acc_set, ))
201
202             for res in cr.dictfetchall():
203                 accounts[res['id']] = res
204
205         #for the asked accounts, get from the dictionnary 'accounts' the value of it
206         res = {}
207         for id in ids:
208             res[id] = self._get_account_values(cr, uid, id, accounts, field_names, context)
209         return res
210
211     def _get_account_values(self, cr, uid, id, accounts, field_names, context={}):
212         res = {}.fromkeys(field_names, 0.0)
213         browse_rec = self.browse(cr, uid, id)
214         if browse_rec.type == 'consolidation':
215             ids2 = self.read(cr, uid, [browse_rec.id], ['child_consol_ids'], context)[0]['child_consol_ids']
216             for t in self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])]):
217                 if t not in ids2 and t != browse_rec.id:
218                     ids2.append(t)
219             for i in ids2:
220                 tmp = self._get_account_values(cr, uid, i, accounts, field_names, context)
221                 for a in field_names:
222                     res[a] += tmp[a]
223         else:
224             ids2 = self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])])
225             for i in ids2:
226                 for a in field_names:
227                     res[a] += accounts.get(i, {}).get(a, 0.0)
228         return res
229
230     def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}):
231         result = {}
232         for rec in self.browse(cr, uid, ids, context):
233             result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code)
234         return result
235
236     def _get_child_ids(self, cr, uid, ids, field_name, arg, context={}):
237         result={}
238         for record in self.browse(cr, uid, ids, context):
239             if record.child_parent_ids:
240                 result[record.id]=[x.id for x in record.child_parent_ids]
241             else:
242                 result[record.id]=[]
243
244             if record.child_consol_ids:
245                 for acc in record.child_consol_ids:
246                     result[record.id].append(acc.id)
247
248         return result
249
250     _columns = {
251         'name': fields.char('Name', size=128, required=True, select=True),
252         'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
253         'code': fields.char('Code', size=64, required=True),
254         'type': fields.selection([
255             ('receivable','Receivable'),
256             ('payable','Payable'),
257             ('view','View'),
258             ('consolidation','Consolidation'),
259             ('other','Others'),
260             ('closed','Closed'),
261         ], 'Internal Type', required=True,),
262
263         'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
264         'parent_id': fields.many2one('account.account','Parent', ondelete='cascade'),
265         'child_parent_ids':fields.one2many('account.account','parent_id','Children'),
266         'child_consol_ids':fields.many2many('account.account', 'account_account_consol_rel', 'child_id', 'parent_id', 'Consolidated Children'),
267         'child_id': fields.function(_get_child_ids, method=True, type='many2many',relation="account.account",string="Children Accounts"),
268         'balance': fields.function(__compute, digits=(16,2), method=True, string='Balance', multi='balance'),
269         'credit': fields.function(__compute, digits=(16,2), method=True, string='Credit', multi='balance'),
270         'debit': fields.function(__compute, digits=(16,2), method=True, string='Debit', multi='balance'),
271         'reconcile': fields.boolean('Reconcile', help="Check this account if the user can make a reconciliation of the entries in this account."),
272         'shortcut': fields.char('Shortcut', size=12),
273         'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel',
274             'account_id','tax_id', 'Default Taxes'),
275         'note': fields.text('Note'),
276         'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Company Currency'),
277         'company_id': fields.many2one('res.company', 'Company', required=True),
278         'active': fields.boolean('Active', select=2),
279
280         'parent_left': fields.integer('Parent Left', select=1),
281         'parent_right': fields.integer('Parent Right', select=1),
282         'currency_mode': fields.selection([('current','At Date'),('average','Average Rate')], 'Outgoing Currencies Rate',
283             help=
284             'This will select how is computed the current currency rate for outgoing transactions. '\
285             'In most countries the legal method is "average" but only a few softwares are able to '\
286             'manage this. So if you import from another software, you may have to use the rate at date. ' \
287             'Incoming transactions, always use the rate at date.', \
288             required=True),
289         'check_history': fields.boolean('Display History',
290             help="Check this box if you want to print all entries when printing the General Ledger, "\
291             "otherwise it will only print its balance."),
292     }
293
294     def _default_company(self, cr, uid, context={}):
295         user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
296         if user.company_id:
297             return user.company_id.id
298         return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
299
300     _defaults = {
301         'type' : lambda *a :'view',
302         'reconcile': lambda *a: False,
303         'company_id': _default_company,
304         'active': lambda *a: True,
305         'check_history': lambda *a: True,
306         'currency_mode': lambda *a: 'current'
307     }
308
309     def _check_recursion(self, cr, uid, ids):
310         obj_self=self.browse(cr,uid,ids[0])
311         p_id=obj_self.parent_id and obj_self.parent_id.id
312         if (obj_self in obj_self.child_consol_ids) or (p_id and (p_id is obj_self.id)):
313             return False
314         while(ids):
315             cr.execute('select distinct child_id from account_account_consol_rel where parent_id in ('+','.join(map(str,ids))+')')
316             child_ids = filter(None, map(lambda x:x[0], cr.fetchall()))
317             c_ids=child_ids
318             if (p_id and (p_id in c_ids)) or (obj_self.id in c_ids):
319                 return False
320             while len(c_ids):
321                 s_ids=self.search(cr,uid,[('parent_id','in',c_ids)])
322                 if p_id and (p_id in s_ids):
323                     return False
324                 c_ids=s_ids
325             ids=child_ids
326         return True
327
328     _constraints = [
329         (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
330     ]
331     def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
332         if not args:
333             args=[]
334         if not context:
335             context = {}
336         args = args[:]
337         ids = []
338         try:
339             if name and str(name).startswith('partner:'):
340                 part_id = int(name.split(':')[1])
341                 part = self.pool.get('res.partner').browse(cr, user, part_id, context)
342                 args += [('id','in', (part.property_account_payable.id, part.property_account_receivable.id))]
343                 name = False
344             if name and str(name).startswith('type:'):
345                 type = name.split(':')[1]
346                 args += [('type','=', type)]
347                 name = False
348         except:
349             pass
350         if name:
351             ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit)
352             if not ids:
353                 ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit)
354             if not ids:
355                 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
356         else:
357             ids = self.search(cr, user, args, context=context, limit=limit)
358         return self.name_get(cr, user, ids, context=context)
359
360     def name_get(self, cr, uid, ids, context={}):
361         if not len(ids):
362             return []
363         reads = self.read(cr, uid, ids, ['name','code'], context)
364         res = []
365         for record in reads:
366             name = record['name']
367             if record['code']:
368                 name = record['code']+' '+name
369             res.append((record['id'],name ))
370         return res
371
372     def copy(self, cr, uid, id, default={}, context={},done_list=[]):
373         account = self.browse(cr, uid, id, context=context)
374         new_child_ids = []
375         if not default:
376             default={}
377         default=default.copy()
378         default['parent_id'] = False
379         if account.id in done_list:
380             return False
381         done_list.append(account.id)
382         if account:
383             for child in account.child_id:
384                 child_ids=self.copy(cr, uid, child.id, default, context=context,done_list=done_list)
385                 if child_ids:
386                     new_child_ids.append(child_ids)
387             default['child_parent_ids'] = [(6, 0, new_child_ids)]
388         else:
389             default['child_parent_ids'] = False
390         return super(account_account, self).copy(cr, uid, id, default, context=context)
391
392     def write(self, cr, uid, ids, vals, context=None):
393         if not context:
394             context={}
395         if 'active' in vals and not vals['active']:
396             line_obj = self.pool.get('account.move.line')
397             account_ids = self.search(cr, uid, [('id', 'child_of', ids)])
398             if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]):
399                 raise osv.except_osv(_('Error !'), _('You can not deactivate an account that contains account moves.'))
400         return super(account_account, self).write(cr, uid, ids, vals, context=context)
401 account_account()
402
403 class account_journal_view(osv.osv):
404     _name = "account.journal.view"
405     _description = "Journal View"
406     _columns = {
407         'name': fields.char('Journal View', size=64, required=True),
408         'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
409     }
410     _order = "name"
411 account_journal_view()
412
413
414 class account_journal_column(osv.osv):
415     def _col_get(self, cr, user, context={}):
416         result = []
417         cols = self.pool.get('account.move.line')._columns
418         for col in cols:
419             result.append( (col, cols[col].string) )
420         result.sort()
421         return result
422     _name = "account.journal.column"
423     _description = "Journal Column"
424     _columns = {
425         'name': fields.char('Column Name', size=64, required=True),
426         'field': fields.selection(_col_get, 'Field Name', method=True, required=True, size=32),
427         'view_id': fields.many2one('account.journal.view', 'Journal View', select=True),
428         'sequence': fields.integer('Sequence'),
429         'required': fields.boolean('Required'),
430         'readonly': fields.boolean('Readonly'),
431     }
432     _order = "sequence"
433 account_journal_column()
434
435 class account_journal(osv.osv):
436     _name = "account.journal"
437     _description = "Journal"
438     _columns = {
439         'name': fields.char('Journal Name', size=64, required=True, translate=True),
440         'code': fields.char('Code', size=16),
441         'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True),
442         'refund_journal': fields.boolean('Refund Journal'),
443
444         'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]),
445         'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]),
446
447         'active': fields.boolean('Active'),
448         'view_id': fields.many2one('account.journal.view', 'View', required=True, help="Gives the view used when writing or browsing entries in this journal. The view tell Open ERP which fields should be visible, required or readonly and in which order. You can create your own view for a faster encoding in each journal."),
449         'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account', domain="[('type','!=','view')]"),
450         'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account', domain="[('type','!=','view')]"),
451         'centralisation': fields.boolean('Centralised counterpart', help="Check this box if you want that each entry doesn't create a counterpart but share the same counterpart for each entry of this journal. This is used in fiscal year closing."),
452         'update_posted': fields.boolean('Allow Cancelling Entries'),
453         'group_invoice_lines': fields.boolean('Group invoice lines', help="If this box is cheked, the system will try to group the accouting lines when generating them from invoices."),
454         'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="The sequence gives the display order for a list of journals", required=True),
455         'user_id': fields.many2one('res.users', 'User', help="The responsible user of this journal"),
456         'groups_id': fields.many2many('res.groups', 'account_journal_group_rel', 'journal_id', 'group_id', 'Groups'),
457         'currency': fields.many2one('res.currency', 'Currency', help='The currency used to enter statement'),
458         '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.'),
459         'company_id': fields.related('default_credit_account_id','company_id',type='many2one', relation="res.company", string="Company"),
460     }
461
462     _defaults = {
463         'active': lambda *a: 1,
464         'user_id': lambda self,cr,uid,context: uid,
465     }
466     def create(self, cr, uid, vals, context={}):
467         journal_id = super(osv.osv, self).create(cr, uid, vals, context)
468 #       journal_name = self.browse(cr, uid, [journal_id])[0].code
469 #       periods = self.pool.get('account.period')
470 #       ids = periods.search(cr, uid, [('date_stop','>=',time.strftime('%Y-%m-%d'))])
471 #       for period in periods.browse(cr, uid, ids):
472 #           self.pool.get('account.journal.period').create(cr, uid, {
473 #               'name': (journal_name or '')+':'+(period.code or ''),
474 #               'journal_id': journal_id,
475 #               'period_id': period.id
476 #           })
477         return journal_id
478     def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
479         if not args:
480             args=[]
481         if not context:
482             context={}
483         ids = []
484         if name:
485             ids = self.search(cr, user, [('code','ilike',name)]+ args, limit=limit)
486         if not ids:
487             ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
488         return self.name_get(cr, user, ids, context=context)
489 account_journal()
490
491 class account_fiscalyear(osv.osv):
492     _name = "account.fiscalyear"
493     _description = "Fiscal Year"
494     _columns = {
495         'name': fields.char('Fiscal Year', size=64, required=True),
496         'code': fields.char('Code', size=6, required=True),
497         'date_start': fields.date('Start date', required=True),
498         'date_stop': fields.date('End date', required=True),
499         'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'),
500         'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', redonly=True),
501     }
502
503     _defaults = {
504         'state': lambda *a: 'draft',
505     }
506     _order = "date_start"
507
508     def _check_duration(self,cr,uid,ids):
509         obj_fy=self.browse(cr,uid,ids[0])
510         if obj_fy.date_stop < obj_fy.date_start:
511             return False
512         return True
513
514     _constraints = [
515         (_check_duration, 'Error ! The date duration of the Fiscal Year is invalid. ', ['date_stop'])
516     ]
517
518     def create_period3(self,cr, uid, ids, context={}):
519         return self.create_period(cr, uid, ids, context, 3)
520
521     def create_period(self,cr, uid, ids, context={}, interval=1):
522         for fy in self.browse(cr, uid, ids, context):
523             dt = fy.date_start
524             ds = mx.DateTime.strptime(fy.date_start, '%Y-%m-%d')
525             while ds.strftime('%Y-%m-%d')<fy.date_stop:
526                 de = ds + RelativeDateTime(months=interval, days=-1)
527
528                 if de.strftime('%Y-%m-%d')>fy.date_stop:
529                     de=mx.DateTime.strptime(fy.date_stop, '%Y-%m-%d')
530
531                 self.pool.get('account.period').create(cr, uid, {
532                     'name': ds.strftime('%m/%Y'),
533                     'code': ds.strftime('%m/%Y'),
534                     'date_start': ds.strftime('%Y-%m-%d'),
535                     'date_stop': de.strftime('%Y-%m-%d'),
536                     'fiscalyear_id': fy.id,
537                 })
538                 ds = ds + RelativeDateTime(months=interval)
539         return True
540
541     def find(self, cr, uid, dt=None, exception=True, context={}):
542         if not dt:
543             dt = time.strftime('%Y-%m-%d')
544         ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)])
545         if not ids:
546             if exception:
547                 raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.'))
548             else:
549                 return False
550         return ids[0]
551 account_fiscalyear()
552
553 class account_period(osv.osv):
554     _name = "account.period"
555     _description = "Account period"
556     _columns = {
557         'name': fields.char('Period Name', size=64, required=True),
558         'code': fields.char('Code', size=12),
559         'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}),
560         'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}),
561         'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True),
562         'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', readonly=True)
563     }
564     _defaults = {
565         'state': lambda *a: 'draft',
566     }
567     _order = "date_start"
568
569     def _check_duration(self,cr,uid,ids):
570         obj_period=self.browse(cr,uid,ids[0])
571         if obj_period.date_stop < obj_period.date_start:
572             return False
573         return True
574
575     def _check_year_limit(self,cr,uid,ids):
576         obj_period=self.browse(cr,uid,ids[0])
577         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:
578             return False
579         return True
580
581     _constraints = [
582         (_check_duration, 'Error ! The date duration of the Period(s) is invalid. ', ['date_stop']),
583         (_check_year_limit, 'Error ! The date duration of the Period(s) should be within the limit of the Fiscal year. ', ['date_stop'])
584     ]
585
586     def next(self, cr, uid, period, step, context={}):
587         ids = self.search(cr, uid, [('date_start','>',period.date_start)])
588         if len(ids)>=step:
589             return ids[step-1]
590         return False
591
592     def find(self, cr, uid, dt=None, context={}):
593         if not dt:
594             dt = time.strftime('%Y-%m-%d')
595 #CHECKME: shouldn't we check the state of the period?
596         ids = self.search(cr, uid, [('date_start','<=',dt),('date_stop','>=',dt)])
597         if not ids:
598             raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.'))
599         return ids
600
601     def action_draft(self, cr, uid, ids, *args):
602         users_roles = self.pool.get('res.users').browse(cr, uid, uid).roles_id
603         for role in users_roles:
604             if role.name=='Period':
605                 mode = 'draft'
606                 for id in ids:
607                     cr.execute('update account_journal_period set state=%s where period_id=%s', (mode, id))
608                     cr.execute('update account_period set state=%s where id=%s', (mode, id))
609         return True
610
611 account_period()
612
613 class account_journal_period(osv.osv):
614     _name = "account.journal.period"
615     _description = "Journal - Period"
616
617     def _icon_get(self, cr, uid, ids, field_name, arg=None, context={}):
618         result = {}.fromkeys(ids, 'STOCK_NEW')
619         for r in self.read(cr, uid, ids, ['state']):
620             result[r['id']] = {
621                 'draft': 'STOCK_NEW',
622                 'printed': 'STOCK_PRINT_PREVIEW',
623                 'done': 'STOCK_DIALOG_AUTHENTICATION',
624             }.get(r['state'], 'STOCK_NEW')
625         return result
626
627     _columns = {
628         'name': fields.char('Journal-Period Name', size=64, required=True),
629         'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"),
630         'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"),
631         'icon': fields.function(_icon_get, method=True, string='Icon', type='string'),
632         'active': fields.boolean('Active', required=True),
633         'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'Status', required=True, readonly=True)
634     }
635
636     def _check(self, cr, uid, ids, context={}):
637         for obj in self.browse(cr, uid, ids, context):
638             cr.execute('select * from account_move_line where journal_id=%s and period_id=%s limit 1', (obj.journal_id.id, obj.period_id.id))
639             res = cr.fetchall()
640             if res:
641                 raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !'))
642         return True
643
644     def write(self, cr, uid, ids, vals, context={}):
645         self._check(cr, uid, ids, context)
646         return super(account_journal_period, self).write(cr, uid, ids, vals, context)
647
648     def create(self, cr, uid, vals, context={}):
649         period_id=vals.get('period_id',False)
650         if period_id:
651             period = self.pool.get('account.period').browse(cr, uid,period_id)
652             vals['state']=period.state
653         return super(account_journal_period, self).create(cr, uid, vals, context)
654
655     def unlink(self, cr, uid, ids, context={}):
656         self._check(cr, uid, ids, context)
657         return super(account_journal_period, self).unlink(cr, uid, ids, context)
658
659     _defaults = {
660         'state': lambda *a: 'draft',
661         'active': lambda *a: True,
662     }
663     _order = "period_id"
664
665 account_journal_period()
666
667 class account_fiscalyear(osv.osv):
668     _inherit = "account.fiscalyear"
669     _description = "Fiscal Year"
670     _columns = {
671         'start_journal_period_id':fields.many2one('account.journal.period','New Entries Journal'),
672         'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True),
673     }
674
675 account_fiscalyear()
676 #----------------------------------------------------------
677 # Entries
678 #----------------------------------------------------------
679 class account_move(osv.osv):
680     _name = "account.move"
681     _description = "Account Entry"
682     _order = 'id desc'
683
684     def name_get(self, cursor, user, ids, context=None):
685         if not len(ids):
686             return []
687         res=[]
688         data_move = self.pool.get('account.move').browse(cursor,user,ids)
689         for move in data_move:
690             if move.state=='draft':
691                 name = '*' + str(move.id)
692             else:
693                 name = move.name
694             res.append((move.id, name))
695         return res
696
697
698     def _get_period(self, cr, uid, context):
699         periods = self.pool.get('account.period').find(cr, uid)
700         if periods:
701             return periods[0]
702         else:
703             return False
704
705     def _amount_compute(self, cr, uid, ids, name, args, context, where =''):
706         if not ids: return {}
707         cr.execute('select move_id,sum(debit) from account_move_line where move_id in ('+','.join(map(str,ids))+') group by move_id')
708         result = dict(cr.fetchall())
709         for id in ids:
710             result.setdefault(id, 0.0)
711         return result
712
713     _columns = {
714         'name': fields.char('Number', size=64, required=True),
715         'ref': fields.char('Ref', size=64),
716         'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}),
717         'journal_id': fields.many2one('account.journal', 'Journal', required=True, states={'posted':[('readonly',True)]}),
718         'state': fields.selection([('draft','Draft'), ('posted','Posted')], 'Status', required=True, readonly=True),
719         'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}),
720         'to_check': fields.boolean('To Be Verified'),
721         'partner_id': fields.related('line_id', 'partner_id', type="many2one", relation="res.partner", string="Partner"),
722         'amount': fields.function(_amount_compute, method=True, string='Amount', digits=(16,2)),
723         'date': fields.date('Date', required=True),
724         'type': fields.selection([
725             ('pay_voucher','Cash Payment'),
726             ('bank_pay_voucher','Bank Payment'),
727             ('rec_voucher','Cash Receipt'),
728             ('bank_rec_voucher','Bank Receipt'),
729             ('cont_voucher','Contra'),
730             ('journal_sale_vou','Journal Sale'),
731             ('journal_pur_voucher','Journal Purchase'),
732             ('journal_voucher','Journal Voucher'),
733         ],'Type', readonly=True, select=True, states={'draft':[('readonly',False)]}),
734     }
735     _defaults = {
736         'name': lambda *a: '/',
737         'state': lambda *a: 'draft',
738         'period_id': _get_period,
739         'type' : lambda *a : 'journal_voucher',
740         'date': lambda *a:time.strftime('%Y-%m-%d'),
741     }
742
743     def _check_centralisation(self, cursor, user, ids):
744         for move in self.browse(cursor, user, ids):
745             if move.journal_id.centralisation:
746                 move_ids = self.search(cursor, user, [
747                     ('period_id', '=', move.period_id.id),
748                     ('journal_id', '=', move.journal_id.id),
749                     ])
750                 if len(move_ids) > 1:
751                     return False
752         return True
753
754     def _check_period_journal(self, cursor, user, ids):
755         for move in self.browse(cursor, user, ids):
756             for line in move.line_id:
757                 if line.period_id.id != move.period_id.id:
758                     return False
759                 if line.journal_id.id != move.journal_id.id:
760                     return False
761         return True
762
763     _constraints = [
764         (_check_centralisation,
765             'You can not create more than one move per period on centralized journal',
766             ['journal_id']),
767         (_check_period_journal,
768             'You can not create entries on different period/journal in the same move',
769             ['line_id']),
770     ]
771     def post(self, cr, uid, ids, context=None):
772         if self.validate(cr, uid, ids, context) and len(ids):
773             for move in self.browse(cr, uid, ids):
774                 if move.name =='/':
775                     new_name = False
776                     journal = move.journal_id
777                     if journal.sequence_id:
778                         new_name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id)
779                     else:
780                         raise osv.except_osv(_('Error'), _('No sequence defined in the journal !'))
781                     if new_name:
782                         self.write(cr, uid, [move.id], {'name':new_name})
783
784             cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('posted',))
785         else:
786             raise osv.except_osv(_('Integrity Error !'), _('You can not validate a non balanced entry !'))
787         return True
788
789     def button_validate(self, cursor, user, ids, context=None):
790         return self.post(cursor, user, ids, context=context)
791
792     def button_cancel(self, cr, uid, ids, context={}):
793         for line in self.browse(cr, uid, ids, context):
794             if not line.journal_id.update_posted:
795                 raise osv.except_osv(_('Error !'), _('You can not modify a posted entry of this journal !\nYou should mark the journal to allow canceling entries.'))
796         if len(ids):
797             cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('draft',))
798         return True
799
800     def write(self, cr, uid, ids, vals, context={}):
801         c = context.copy()
802         c['novalidate'] = True
803         result = super(osv.osv, self).write(cr, uid, ids, vals, c)
804         self.validate(cr, uid, ids, context)
805         return result
806
807     #
808     # TODO: Check if period is closed !
809     #
810     def create(self, cr, uid, vals, context={}):
811         if 'line_id' in vals:
812             if 'journal_id' in vals:
813                 for l in vals['line_id']:
814                     if not l[0]:
815                         l[2]['journal_id'] = vals['journal_id']
816                 context['journal_id'] = vals['journal_id']
817             if 'period_id' in vals:
818                 for l in vals['line_id']:
819                     if not l[0]:
820                         l[2]['period_id'] = vals['period_id']
821                 context['period_id'] = vals['period_id']
822             else:
823                 default_period = self._get_period(cr, uid, context)
824                 for l in vals['line_id']:
825                     if not l[0]:
826                         l[2]['period_id'] = default_period
827                 context['period_id'] = default_period
828
829         accnt_journal = self.pool.get('account.journal').browse(cr, uid, vals['journal_id'])
830         if 'line_id' in vals:
831             c = context.copy()
832             c['novalidate'] = True
833             result = super(account_move, self).create(cr, uid, vals, c)
834             self.validate(cr, uid, [result], context)
835         else:
836             result = super(account_move, self).create(cr, uid, vals, context)
837         return result
838
839     def copy(self, cr, uid, id, default=None, context=None):
840         if default is None:
841             default = {}
842         default = default.copy()
843         default.update({'state':'draft', 'name':'/',})
844         return super(account_move, self).copy(cr, uid, id, default, context)
845
846     def unlink(self, cr, uid, ids, context={}, check=True):
847         toremove = []
848         for move in self.browse(cr, uid, ids, context):
849             if move['state'] <> 'draft':
850                 raise osv.except_osv(_('UserError'),
851                         _('You can not delete posted movement: "%s"!') % \
852                                 move['name'])
853             line_ids = map(lambda x: x.id, move.line_id)
854             context['journal_id'] = move.journal_id.id
855             context['period_id'] = move.period_id.id
856             self.pool.get('account.move.line')._update_check(cr, uid, line_ids, context)
857             toremove.append(move.id)
858         result = super(account_move, self).unlink(cr, uid, toremove, context)
859         return result
860
861     def _compute_balance(self, cr, uid, id, context={}):
862         move = self.browse(cr, uid, [id])[0]
863         amount = 0
864         for line in move.line_id:
865             amount+= (line.debit - line.credit)
866         return amount
867
868     def _centralise(self, cr, uid, move, mode):
869         if mode=='credit':
870             account_id = move.journal_id.default_debit_account_id.id
871             mode2 = 'debit'
872             if not account_id:
873                 raise osv.except_osv(_('UserError'),
874                         _('There is no default default debit account defined \n' \
875                                 'on journal "%s"') % move.journal_id.name)
876         else:
877             account_id = move.journal_id.default_credit_account_id.id
878             mode2 = 'credit'
879             if not account_id:
880                 raise osv.except_osv(_('UserError'),
881                         _('There is no default default credit account defined \n' \
882                                 'on journal "%s"') % move.journal_id.name)
883
884         # find the first line of this move with the current mode
885         # or create it if it doesn't exist
886         cr.execute('select id from account_move_line where move_id=%s and centralisation=%s limit 1', (move.id, mode))
887         res = cr.fetchone()
888         if res:
889             line_id = res[0]
890         else:
891             line_id = self.pool.get('account.move.line').create(cr, uid, {
892                 'name': 'Centralisation '+mode,
893                 'centralisation': mode,
894                 'account_id': account_id,
895                 'move_id': move.id,
896                 'journal_id': move.journal_id.id,
897                 'period_id': move.period_id.id,
898                 'date': move.period_id.date_stop,
899                 'debit': 0.0,
900                 'credit': 0.0,
901             }, {'journal_id': move.journal_id.id, 'period_id': move.period_id.id})
902
903         # find the first line of this move with the other mode
904         # so that we can exclude it from our calculation
905         cr.execute('select id from account_move_line where move_id=%s and centralisation=%s limit 1', (move.id, mode2))
906         res = cr.fetchone()
907         if res:
908             line_id2 = res[0]
909         else:
910             line_id2 = 0
911
912         cr.execute('select sum('+mode+') from account_move_line where move_id=%s and id<>%s', (move.id, line_id2))
913         result = cr.fetchone()[0] or 0.0
914         cr.execute('update account_move_line set '+mode2+'=%s where id=%s', (result, line_id))
915         return True
916
917     #
918     # Validate a balanced move. If it is a centralised journal, create a move.
919     #
920     def validate(self, cr, uid, ids, context={}):
921         ok = True
922         for move in self.browse(cr, uid, ids, context):
923             #unlink analytic lines on move_lines
924             for obj_line in move.line_id:
925                 for obj in obj_line.analytic_lines: 
926                     self.pool.get('account.analytic.line').unlink(cr,uid,obj.id)
927
928             journal = move.journal_id
929             amount = 0
930             line_ids = []
931             line_draft_ids = []
932             company_id=None
933             for line in move.line_id:
934                 amount += line.debit - line.credit
935                 line_ids.append(line.id)
936                 if line.state=='draft':
937                     line_draft_ids.append(line.id)
938
939                 if not company_id:
940                     company_id = line.account_id.company_id.id
941                 if not company_id == line.account_id.company_id.id:
942                     raise osv.except_osv(_('Error'), _("Couldn't create move between different companies"))
943
944                 if line.account_id.currency_id:
945                     if line.account_id.currency_id.id != line.currency_id.id and (line.account_id.currency_id.id != line.account_id.company_id.currency_id.id or line.currency_id):
946                             raise osv.except_osv(_('Error'), _("""Couldn't create move with currency different than the secondary currency of the account "%s - %s". Clear the secondary currency field of the account definition if you want to accept all currencies.""" % (line.account_id.code, line.account_id.name)))
947
948             if abs(amount) < 0.0001:
949                 if not len(line_draft_ids):
950                     continue
951                 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
952                     'journal_id': move.journal_id.id,
953                     'period_id': move.period_id.id,
954                     'state': 'valid'
955                 }, context, check=False)
956                 todo = []
957                 account = {}
958                 account2 = {}
959                 if journal.type not in ('purchase','sale'):
960                     continue
961
962                 for line in move.line_id:
963                     if move.journal_id.type == 'sale':
964                         if line.debit:
965                             field_base = 'ref_'
966                             key = 'account_paid_id'
967                         else:
968                             field_base = ''
969                             key = 'account_collected_id'
970                     else:
971                         if line.debit:
972                             field_base = ''
973                             key = 'account_collected_id'
974                         else:
975                             field_base = 'ref_'
976                             key = 'account_paid_id'
977                     if line.account_id.tax_ids:
978                         code = amount = False
979                         for tax in line.account_id.tax_ids:
980                             if tax.tax_code_id:
981                                 acc = getattr(tax, key).id
982                                 account[acc] = (getattr(tax,
983                                     field_base + 'tax_code_id').id,
984                                     getattr(tax, field_base + 'tax_sign'))
985                                 account2[(acc,getattr(tax,
986                                     field_base + 'tax_code_id').id)] = (getattr(tax,
987                                         field_base + 'tax_code_id').id,
988                                         getattr(tax, field_base + 'tax_sign'))
989                                 code = getattr(tax, field_base + 'base_code_id').id
990                                 amount = getattr(tax, field_base+'base_sign') * \
991                                         (line.debit + line.credit)
992                                 break
993                         if code and not (line.tax_code_id or line.tax_amount):
994                             self.pool.get('account.move.line').write(cr, uid,
995                                     [line.id], {
996                                 'tax_code_id': code,
997                                 'tax_amount': amount
998                             }, context=context, check=False)
999                     else:
1000                         todo.append(line)
1001                 for line in todo:
1002                     code = amount = 0
1003                     key = (line.account_id.id, line.tax_code_id.id)
1004                     if key in account2:
1005                         code = account2[key][0]
1006                         amount = account2[key][1] * (line.debit + line.credit)
1007                     elif line.account_id.id in account:
1008                         code = account[line.account_id.id][0]
1009                         amount = account[line.account_id.id][1] * (line.debit + line.credit)
1010                     if (code or amount) and not (line.tax_code_id or line.tax_amount):
1011                         self.pool.get('account.move.line').write(cr, uid, [line.id], {
1012                             'tax_code_id': code,
1013                             'tax_amount': amount
1014                         }, context, check=False)
1015                 #
1016                 # Compute VAT
1017                 #
1018                 continue
1019             if journal.centralisation:
1020                 self._centralise(cr, uid, move, 'debit')
1021                 self._centralise(cr, uid, move, 'credit')
1022                 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
1023                     'state': 'valid'
1024                 }, context, check=False)
1025                 continue
1026             else:
1027                 self.pool.get('account.move.line').write(cr, uid, line_ids, {
1028                     'journal_id': move.journal_id.id,
1029                     'period_id': move.period_id.id,
1030                     #'tax_code_id': False,
1031                     #'tax_amount': False,
1032                     'state': 'draft'
1033                 }, context, check=False)
1034                 ok = False
1035         if ok:
1036             list_ids = []
1037             for tmp in move.line_id:
1038                 list_ids.append(tmp.id)
1039             self.pool.get('account.move.line').create_analytic_lines(cr, uid, list_ids, context)
1040         return ok
1041 account_move()
1042
1043 class account_move_reconcile(osv.osv):
1044     _name = "account.move.reconcile"
1045     _description = "Account Reconciliation"
1046     _columns = {
1047         'name': fields.char('Name', size=64, required=True),
1048         'type': fields.char('Type', size=16, required=True),
1049         'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry lines'),
1050         'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'),
1051         'create_date': fields.date('Creation date', readonly=True),
1052     }
1053     _defaults = {
1054         'name': lambda self,cr,uid,ctx={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile') or '/',
1055     }
1056     def reconcile_partial_check(self, cr, uid, ids, type='auto', context={}):
1057         for rec in self.browse(cr, uid, ids, context):
1058             total = 0.0
1059             for line in rec.line_partial_ids:
1060                 total += (line.debit or 0.0) - (line.credit or 0.0)
1061         if not total:
1062             self.pool.get('account.move.line').write(cr, uid,
1063                 map(lambda x: x.id, rec.line_partial_ids),
1064                 {'reconcile_id': rec.id }
1065             )
1066         return True
1067
1068     def name_get(self, cr, uid, ids, context=None):
1069         if not len(ids):
1070             return []
1071         result = []
1072         for r in self.browse(cr, uid, ids, context):
1073             total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0)
1074             if total:
1075                 name = '%s (%.2f)' % (r.name, total)
1076                 result.append((r.id,name))
1077             else:
1078                 result.append((r.id,r.name))
1079         return result
1080
1081
1082 account_move_reconcile()
1083
1084 #----------------------------------------------------------
1085 # Tax
1086 #----------------------------------------------------------
1087 """
1088 a documenter
1089 child_depend: la taxe depend des taxes filles
1090 """
1091 class account_tax_code(osv.osv):
1092     """
1093     A code for the tax object.
1094
1095     This code is used for some tax declarations.
1096     """
1097     def _sum(self, cr, uid, ids, name, args, context, where =''):
1098         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
1099         acc_set = ",".join(map(str, ids2))
1100         if context.get('based_on', 'invoices') == 'payments':
1101             cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1102                     FROM account_move_line AS line, \
1103                         account_move AS move \
1104                         LEFT JOIN account_invoice invoice ON \
1105                             (invoice.move_id = move.id) \
1106                     WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1107                         AND move.id = line.move_id \
1108                         AND ((invoice.state = \'paid\') \
1109                             OR (invoice.id IS NULL)) \
1110                     GROUP BY line.tax_code_id')
1111         else:
1112             cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1113                     FROM account_move_line AS line \
1114                     WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1115                     GROUP BY line.tax_code_id')
1116         res=dict(cr.fetchall())
1117         for record in self.browse(cr, uid, ids, context):
1118             def _rec_get(record):
1119                 amount = res.get(record.id, 0.0)
1120                 for rec in record.child_ids:
1121                     amount += _rec_get(rec) * rec.sign
1122                 return amount
1123             res[record.id] = round(_rec_get(record), 2)
1124         return res
1125
1126     def _sum_period(self, cr, uid, ids, name, args, context):
1127         if 'period_id' in context and context['period_id']:
1128             period_id = context['period_id']
1129         else:
1130             period_id = self.pool.get('account.period').find(cr, uid)
1131             if not len(period_id):
1132                 return dict.fromkeys(ids, 0.0)
1133             period_id = period_id[0]
1134         return self._sum(cr, uid, ids, name, args, context,
1135                 where=' and line.period_id='+str(period_id))
1136
1137     _name = 'account.tax.code'
1138     _description = 'Tax Code'
1139     _rec_name = 'code'
1140     _columns = {
1141         'name': fields.char('Tax Case Name', size=64, required=True),
1142         'code': fields.char('Case Code', size=64),
1143         'info': fields.text('Description'),
1144         'sum': fields.function(_sum, method=True, string="Year Sum"),
1145         'sum_period': fields.function(_sum_period, method=True, string="Period Sum"),
1146         'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True),
1147         'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'),
1148         'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'),
1149         'company_id': fields.many2one('res.company', 'Company', required=True),
1150         'sign': fields.float('Sign for parent', required=True),
1151     }
1152
1153     def name_get(self, cr, uid, ids, context=None):
1154         if not len(ids):
1155             return []
1156         if isinstance(ids, (int, long)):
1157             ids = [ids]
1158         reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1159         return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1160                 for x in reads]
1161
1162     def _default_company(self, cr, uid, context={}):
1163         user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1164         if user.company_id:
1165             return user.company_id.id
1166         return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1167     _defaults = {
1168         'company_id': _default_company,
1169         'sign': lambda *args: 1.0,
1170     }
1171     def _check_recursion(self, cr, uid, ids):
1172         level = 100
1173         while len(ids):
1174             cr.execute('select distinct parent_id from account_tax_code where id in ('+','.join(map(str,ids))+')')
1175             ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1176             if not level:
1177                 return False
1178             level -= 1
1179         return True
1180
1181     _constraints = [
1182         (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
1183     ]
1184     _order = 'code,name'
1185 account_tax_code()
1186
1187 class account_tax(osv.osv):
1188     """
1189     A tax object.
1190
1191     Type: percent, fixed, none, code
1192         PERCENT: tax = price * amount
1193         FIXED: tax = price + amount
1194         NONE: no tax line
1195         CODE: execute python code. localcontext = {'price_unit':pu, 'address':address_object}
1196             return result in the context
1197             Ex: result=round(price_unit*0.21,4)
1198     """
1199     _name = 'account.tax'
1200     _description = 'Tax'
1201     _columns = {
1202         'name': fields.char('Tax Name', size=64, required=True, help="This name will be used to be displayed on reports"),
1203         '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."),
1204         'amount': fields.float('Amount', required=True, digits=(14,4)),
1205         'active': fields.boolean('Active'),
1206         'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1207         'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1208         '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."),
1209         'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'),
1210         'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'),
1211         'parent_id':fields.many2one('account.tax', 'Parent Tax Account', select=True),
1212         'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'),
1213         'child_depend':fields.boolean('Tax on Childs', help="Indicate if the tax computation is based on the value computed for the computation of child taxes or based on the total amount."),
1214         'python_compute':fields.text('Python Code'),
1215         'python_compute_inv':fields.text('Python Code (reverse)'),
1216         'python_applicable':fields.text('Python Code'),
1217         'tax_group': fields.selection([('vat','VAT'),('other','Other')], 'Tax Group', help="If a default tax if given in the partner it only override taxes from account (or product) of the same group."),
1218
1219         #
1220         # Fields used for the VAT declaration
1221         #
1222         'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."),
1223         'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."),
1224         'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1225         'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1226
1227         # Same fields for refund invoices
1228
1229         'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the VAT declaration."),
1230         'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1231         'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1232         'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1233         'include_base_amount': fields.boolean('Include in base amount', help="Indicate if the amount of tax must be included in the base amount for the computation of the next taxes"),
1234         'company_id': fields.many2one('res.company', 'Company', required=True),
1235         'description': fields.char('Internal Name',size=32),
1236         'price_include': fields.boolean('Tax Included in Price', help="Check this is the price you use on the product and invoices is including this tax.")
1237     }
1238
1239     def name_get(self, cr, uid, ids, context={}):
1240         if not len(ids):
1241             return []
1242         res = []
1243         for record in self.read(cr, uid, ids, ['description','name'], context):
1244             name = record['description'] and record['description'] or record['name']
1245             res.append((record['id'],name ))
1246         return res
1247
1248     def _default_company(self, cr, uid, context={}):
1249         user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1250         if user.company_id:
1251             return user.company_id.id
1252         return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1253     _defaults = {
1254         'python_compute': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or None\n# partner : res.partner object or None\n\nresult = price_unit * 0.10''',
1255         'python_compute_inv': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or False\n\nresult = price_unit * 0.10''',
1256         'applicable_type': lambda *a: 'true',
1257         'type': lambda *a: 'percent',
1258         'amount': lambda *a: 0,
1259         'price_include': lambda *a: 0,
1260         'active': lambda *a: 1,
1261         'sequence': lambda *a: 1,
1262         'tax_group': lambda *a: 'vat',
1263         'ref_tax_sign': lambda *a: 1,
1264         'ref_base_sign': lambda *a: 1,
1265         'tax_sign': lambda *a: 1,
1266         'base_sign': lambda *a: 1,
1267         'include_base_amount': lambda *a: False,
1268         'company_id': _default_company,
1269     }
1270     _order = 'sequence'
1271
1272     def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1273         res = []
1274         for tax in taxes:
1275             if tax.applicable_type=='code':
1276                 localdict = {'price_unit':price_unit, 'address':self.pool.get('res.partner.address').browse(cr, uid, address_id), 'product':product, 'partner':partner}
1277                 exec tax.python_applicable in localdict
1278                 if localdict.get('result', False):
1279                     res.append(tax)
1280             else:
1281                 res.append(tax)
1282         return res
1283
1284     def _unit_compute(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1285         taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1286
1287         res = []
1288         cur_price_unit=price_unit
1289         for tax in taxes:
1290             # we compute the amount for the current tax object and append it to the result
1291
1292             data = {'id':tax.id,
1293                             'name':tax.name,
1294                             'account_collected_id':tax.account_collected_id.id,
1295                             'account_paid_id':tax.account_paid_id.id,
1296                             'base_code_id': tax.base_code_id.id,
1297                             'ref_base_code_id': tax.ref_base_code_id.id,
1298                             'sequence': tax.sequence,
1299                             'base_sign': tax.base_sign,
1300                             'tax_sign': tax.tax_sign,
1301                             'ref_base_sign': tax.ref_base_sign,
1302                             'ref_tax_sign': tax.ref_tax_sign,
1303                             'price_unit': cur_price_unit,
1304                             'tax_code_id': tax.tax_code_id.id,
1305                             'ref_tax_code_id': tax.ref_tax_code_id.id,
1306             }
1307             res.append(data)
1308             if tax.type=='percent':
1309                 amount = cur_price_unit * tax.amount
1310                 data['amount'] = amount
1311
1312             elif tax.type=='fixed':
1313                 data['amount'] = tax.amount
1314             elif tax.type=='code':
1315                 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1316                 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1317                 exec tax.python_compute in localdict
1318                 amount = localdict['result']
1319                 data['amount'] = amount
1320             amount2 = data['amount']
1321             if len(tax.child_ids):
1322                 if tax.child_depend:
1323                     del res[-1]
1324                 amount = amount2
1325                 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner)
1326                 res.extend(child_tax)
1327             if tax.include_base_amount:
1328                 cur_price_unit+=amount2
1329         return res
1330
1331     def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1332
1333         """
1334         Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1335
1336         RETURN:
1337             [ tax ]
1338             tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1339             one tax for each tax id in IDS and their childs
1340         """
1341         res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner)
1342         for r in res:
1343             r['amount'] *= quantity
1344         return res
1345
1346     def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1347         taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1348
1349         res = []
1350         taxes.reverse()
1351         cur_price_unit=price_unit
1352
1353         tax_parent_tot = 0.0
1354         for tax in taxes:
1355             if (tax.type=='percent') and not tax.include_base_amount:
1356                 tax_parent_tot+=tax.amount
1357
1358         for tax in taxes:
1359             if tax.type=='percent':
1360                 if tax.include_base_amount:
1361                     amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
1362                 else:
1363                     amount = (cur_price_unit / (1 + tax_parent_tot)) * tax.amount
1364
1365             elif tax.type=='fixed':
1366                 amount = tax.amount
1367
1368             elif tax.type=='code':
1369                 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1370                 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1371                 exec tax.python_compute_inv in localdict
1372                 amount = localdict['result']
1373
1374             if tax.include_base_amount:
1375                 cur_price_unit -= amount
1376                 todo = 0
1377             else:
1378                 todo = 1
1379             res.append({
1380                 'id': tax.id,
1381                 'todo': todo,
1382                 'name': tax.name,
1383                 'amount': amount,
1384                 'account_collected_id': tax.account_collected_id.id,
1385                 'account_paid_id': tax.account_paid_id.id,
1386                 'base_code_id': tax.base_code_id.id,
1387                 'ref_base_code_id': tax.ref_base_code_id.id,
1388                 'sequence': tax.sequence,
1389                 'base_sign': tax.base_sign,
1390                 'tax_sign': tax.tax_sign,
1391                 'ref_base_sign': tax.ref_base_sign,
1392                 'ref_tax_sign': tax.ref_tax_sign,
1393                 'price_unit': cur_price_unit,
1394                 'tax_code_id': tax.tax_code_id.id,
1395                 'ref_tax_code_id': tax.ref_tax_code_id.id,
1396             })
1397             if len(tax.child_ids):
1398                 if tax.child_depend:
1399                     del res[-1]
1400                     amount = price_unit
1401
1402             parent_tax = self._unit_compute_inv(cr, uid, tax.child_ids, amount, address_id, product, partner)
1403             res.extend(parent_tax)
1404
1405         total = 0.0
1406         for r in res:
1407             if r['todo']:
1408                 total += r['amount']
1409         for r in res:
1410             r['price_unit'] -= total
1411             r['todo'] = 0
1412         return res
1413
1414     def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1415         """
1416         Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1417         Price Unit is a VAT included price
1418
1419         RETURN:
1420             [ tax ]
1421             tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1422             one tax for each tax id in IDS and their childs
1423         """
1424         res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
1425         for r in res:
1426             r['amount'] *= quantity
1427         return res
1428 account_tax()
1429
1430 # ---------------------------------------------------------
1431 # Account Entries Models
1432 # ---------------------------------------------------------
1433
1434 class account_model(osv.osv):
1435     _name = "account.model"
1436     _description = "Account Model"
1437     _columns = {
1438         'name': fields.char('Model Name', size=64, required=True, help="This is a model for recurring accounting entries"),
1439         'ref': fields.char('Ref', size=64),
1440         'journal_id': fields.many2one('account.journal', 'Journal', required=True),
1441         'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'),
1442         'legend' :fields.text('Legend',readonly=True,size=100),
1443     }
1444
1445     _defaults = {
1446         'legend': lambda self, cr, uid, context:_('You can specify year, month and date in the name of the model using the following labels:\n\n%(year)s : To Specify Year \n%(month)s : To Specify Month \n%(date)s : Current Date\n\ne.g. My model on %(date)s'),
1447
1448     }
1449
1450 account_model()
1451
1452 class account_model_line(osv.osv):
1453     _name = "account.model.line"
1454     _description = "Account Model Entries"
1455     _columns = {
1456         'name': fields.char('Name', size=64, required=True),
1457         'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the resources from the lowest sequences to the higher ones"),
1458         'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity on entries"),
1459         'debit': fields.float('Debit', digits=(16,2)),
1460         'credit': fields.float('Credit', digits=(16,2)),
1461
1462         'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"),
1463
1464         'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True),
1465
1466         'ref': fields.char('Ref.', size=16),
1467
1468         'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."),
1469         'currency_id': fields.many2one('res.currency', 'Currency'),
1470
1471         'partner_id': fields.many2one('res.partner', 'Partner Ref.'),
1472         'date_maturity': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Maturity date', help="The maturity date of the generated entries for this model. You can chosse between the date of the creation action or the the date of the creation of the entries plus the partner payment terms."),
1473         'date': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Current Date', required=True, help="The date of the generated entries"),
1474     }
1475     _defaults = {
1476         'date': lambda *a: 'today'
1477     }
1478     _order = 'sequence'
1479     _sql_constraints = [
1480         ('credit_debit1', 'CHECK (credit*debit=0)',  'Wrong credit or debit value in model !'),
1481         ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model !'),
1482     ]
1483 account_model_line()
1484
1485 # ---------------------------------------------------------
1486 # Account Subscription
1487 # ---------------------------------------------------------
1488
1489
1490 class account_subscription(osv.osv):
1491     _name = "account.subscription"
1492     _description = "Account Subscription"
1493     _columns = {
1494         'name': fields.char('Name', size=64, required=True),
1495         'ref': fields.char('Ref.', size=16),
1496         'model_id': fields.many2one('account.model', 'Model', required=True),
1497
1498         'date_start': fields.date('Starting date', required=True),
1499         'period_total': fields.integer('Number of period', required=True),
1500         'period_nbr': fields.integer('Period', required=True),
1501         'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True),
1502         'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status', required=True, readonly=True),
1503
1504         'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines')
1505     }
1506     _defaults = {
1507         'date_start': lambda *a: time.strftime('%Y-%m-%d'),
1508         'period_type': lambda *a: 'month',
1509         'period_total': lambda *a: 12,
1510         'period_nbr': lambda *a: 1,
1511         'state': lambda *a: 'draft',
1512     }
1513     def state_draft(self, cr, uid, ids, context={}):
1514         self.write(cr, uid, ids, {'state':'draft'})
1515         return False
1516
1517     def check(self, cr, uid, ids, context={}):
1518         todone = []
1519         for sub in self.browse(cr, uid, ids, context):
1520             ok = True
1521             for line in sub.lines_id:
1522                 if not line.move_id.id:
1523                     ok = False
1524                     break
1525             if ok:
1526                 todone.append(sub.id)
1527         if len(todone):
1528             self.write(cr, uid, todone, {'state':'done'})
1529         return False
1530
1531     def remove_line(self, cr, uid, ids, context={}):
1532         toremove = []
1533         for sub in self.browse(cr, uid, ids, context):
1534             for line in sub.lines_id:
1535                 if not line.move_id.id:
1536                     toremove.append(line.id)
1537         if len(toremove):
1538             self.pool.get('account.subscription.line').unlink(cr, uid, toremove)
1539         self.write(cr, uid, ids, {'state':'draft'})
1540         return False
1541
1542     def compute(self, cr, uid, ids, context={}):
1543         for sub in self.browse(cr, uid, ids, context):
1544             ds = sub.date_start
1545             for i in range(sub.period_total):
1546                 self.pool.get('account.subscription.line').create(cr, uid, {
1547                     'date': ds,
1548                     'subscription_id': sub.id,
1549                 })
1550                 if sub.period_type=='day':
1551                     ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(days=sub.period_nbr)).strftime('%Y-%m-%d')
1552                 if sub.period_type=='month':
1553                     ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(months=sub.period_nbr)).strftime('%Y-%m-%d')
1554                 if sub.period_type=='year':
1555                     ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(years=sub.period_nbr)).strftime('%Y-%m-%d')
1556         self.write(cr, uid, ids, {'state':'running'})
1557         return True
1558 account_subscription()
1559
1560 class account_subscription_line(osv.osv):
1561     _name = "account.subscription.line"
1562     _description = "Account Subscription Line"
1563     _columns = {
1564         'subscription_id': fields.many2one('account.subscription', 'Subscription', required=True, select=True),
1565         'date': fields.date('Date', required=True),
1566         'move_id': fields.many2one('account.move', 'Entry'),
1567     }
1568     _defaults = {
1569     }
1570     def move_create(self, cr, uid, ids, context={}):
1571         tocheck = {}
1572         for line in self.browse(cr, uid, ids, context):
1573             datas = {
1574                 'date': line.date,
1575             }
1576             ids = self.pool.get('account.model').generate(cr, uid, [line.subscription_id.model_id.id], datas, context)
1577             tocheck[line.subscription_id.id] = True
1578             self.write(cr, uid, [line.id], {'move_id':ids[0]})
1579         if tocheck:
1580             self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context)
1581         return True
1582     _rec_name = 'date'
1583 account_subscription_line()
1584
1585
1586 class account_config_wizard(osv.osv_memory):
1587     _name = 'account.config.wizard'
1588
1589     def _get_charts(self, cr, uid, context):
1590         module_obj=self.pool.get('ir.module.module')
1591         ids=module_obj.search(cr, uid, [('category_id', '=', 'Account Charts'), ('state', '<>', 'installed')])
1592         res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)]
1593         res.append((-1, 'None'))
1594         res.sort(key=lambda x: x[1])
1595         return res
1596
1597     _columns = {
1598         'name':fields.char('Name', required=True, size=64, help="Name of the fiscal year as displayed on screens."),
1599         'code':fields.char('Code', required=True, size=64, help="Name of the fiscal year as displayed in reports."),
1600         'date1': fields.date('Starting Date', required=True),
1601         'date2': fields.date('Ending Date', required=True),
1602         'period':fields.selection([('month','Month'),('3months','3 Months')], 'Periods', required=True),
1603         'charts' : fields.selection(_get_charts, 'Charts of Account',required=True)
1604     }
1605     _defaults = {
1606         'code': lambda *a: time.strftime('%Y'),
1607         'name': lambda *a: time.strftime('%Y'),
1608         'date1': lambda *a: time.strftime('%Y-01-01'),
1609         'date2': lambda *a: time.strftime('%Y-12-31'),
1610         'period':lambda *a:'month',
1611     }
1612     def action_cancel(self,cr,uid,ids,conect=None):
1613         return {
1614                 'view_type': 'form',
1615                 "view_mode": 'form',
1616                 'res_model': 'ir.actions.configuration.wizard',
1617                 'type': 'ir.actions.act_window',
1618                 'target':'new',
1619         }
1620
1621     def install_account_chart(self, cr, uid, ids, context=None):
1622         for res in self.read(cr,uid,ids):
1623             chart_id = res['charts']
1624             if chart_id > 0:
1625                 mod_obj = self.pool.get('ir.module.module')
1626                 mod_obj.button_install(cr, uid, [chart_id], context=context)
1627         cr.commit()
1628         db, pool = pooler.restart_pool(cr.dbname, update_module=True)
1629
1630     def action_create(self, cr, uid,ids, context=None):
1631         for res in self.read(cr,uid,ids):
1632             if 'date1' in res and 'date2' in res:
1633                 res_obj = self.pool.get('account.fiscalyear')
1634                 start_date=res['date1']
1635                 end_date=res['date2']
1636                 name=res['name']#DateTime.strptime(start_date, '%Y-%m-%d').strftime('%m.%Y') + '-' + DateTime.strptime(end_date, '%Y-%m-%d').strftime('%m.%Y')
1637                 vals={
1638                     'name':name,
1639                     'code':name,
1640                     'date_start':start_date,
1641                     'date_stop':end_date,
1642                 }
1643                 new_id=res_obj.create(cr, uid, vals, context=context)
1644                 if res['period']=='month':
1645                     res_obj.create_period(cr,uid,[new_id])
1646                 elif res['period']=='3months':
1647                     res_obj.create_period3(cr,uid,[new_id])
1648         self.install_account_chart(cr,uid,ids)
1649         return {
1650                 'view_type': 'form',
1651                 "view_mode": 'form',
1652                 'res_model': 'ir.actions.configuration.wizard',
1653                 'type': 'ir.actions.act_window',
1654                 'target':'new',
1655         }
1656
1657
1658
1659 account_config_wizard()
1660
1661
1662 #  ---------------------------------------------------------------
1663 #   Account Templates : Account, Tax, Tax Code and chart. + Wizard
1664 #  ---------------------------------------------------------------
1665
1666 class account_tax_template(osv.osv):
1667     _name = 'account.tax.template'
1668 account_tax_template()
1669
1670 class account_account_template(osv.osv):
1671     _order = "code"
1672     _name = "account.account.template"
1673     _description ='Templates for Accounts'
1674
1675     _columns = {
1676         'name': fields.char('Name', size=128, required=True, select=True),
1677         'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
1678         'code': fields.char('Code', size=64),
1679         'type': fields.selection([
1680             ('receivable','Receivable'),
1681             ('payable','Payable'),
1682             ('view','View'),
1683             ('consolidation','Consolidation'),
1684             ('other','Others'),
1685             ('closed','Closed'),
1686             ], 'Internal Type', required=True,),
1687         'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
1688         'reconcile': fields.boolean('Allow Reconciliation', help="Check this option if the user can make a reconciliation of the entries in this account."),
1689         'shortcut': fields.char('Shortcut', size=12),
1690         'note': fields.text('Note'),
1691         'parent_id': fields.many2one('account.account.template','Parent Account Template', ondelete='cascade'),
1692         'child_parent_ids':fields.one2many('account.account.template','parent_id','Children'),
1693         'tax_ids': fields.many2many('account.tax.template', 'account_account_template_tax_rel','account_id','tax_id', 'Default Taxes'),
1694     }
1695
1696     _defaults = {
1697         'reconcile': lambda *a: False,
1698         'type' : lambda *a :'view',
1699     }
1700
1701     def _check_recursion(self, cr, uid, ids):
1702         level = 100
1703         while len(ids):
1704             cr.execute('select parent_id from account_account_template where id in ('+','.join(map(str,ids))+')')
1705             ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1706             if not level:
1707                 return False
1708             level -= 1
1709         return True
1710
1711     _constraints = [
1712         (_check_recursion, 'Error ! You can not create recursive account templates.', ['parent_id'])
1713     ]
1714
1715
1716     def name_get(self, cr, uid, ids, context={}):
1717         if not len(ids):
1718             return []
1719         reads = self.read(cr, uid, ids, ['name','code'], context)
1720         res = []
1721         for record in reads:
1722             name = record['name']
1723             if record['code']:
1724                 name = record['code']+' '+name
1725             res.append((record['id'],name ))
1726         return res
1727
1728 account_account_template()
1729
1730 class account_tax_code_template(osv.osv):
1731
1732     _name = 'account.tax.code.template'
1733     _description = 'Tax Code Template'
1734     _order = 'code'
1735     _rec_name = 'code'
1736     _columns = {
1737         'name': fields.char('Tax Case Name', size=64, required=True),
1738         'code': fields.char('Case Code', size=64),
1739         'info': fields.text('Description'),
1740         'parent_id': fields.many2one('account.tax.code.template', 'Parent Code', select=True),
1741         'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Childs Codes'),
1742         'sign': fields.float('Sign for parent', required=True),
1743     }
1744
1745     _defaults = {
1746         'sign': lambda *args: 1.0,
1747     }
1748
1749     def name_get(self, cr, uid, ids, context=None):
1750         if not len(ids):
1751             return []
1752         if isinstance(ids, (int, long)):
1753             ids = [ids]
1754         reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1755         return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1756                 for x in reads]
1757
1758     def _check_recursion(self, cr, uid, ids):
1759         level = 100
1760         while len(ids):
1761             cr.execute('select distinct parent_id from account_tax_code_template where id in ('+','.join(map(str,ids))+')')
1762             ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1763             if not level:
1764                 return False
1765             level -= 1
1766         return True
1767
1768     _constraints = [
1769         (_check_recursion, 'Error ! You can not create recursive Tax Codes.', ['parent_id'])
1770     ]
1771     _order = 'code,name'
1772 account_tax_code_template()
1773
1774
1775 class account_chart_template(osv.osv):
1776     _name="account.chart.template"
1777     _description= "Templates for Account Chart"
1778
1779     _columns={
1780         'name': fields.char('Name', size=64, required=True),
1781         'account_root_id': fields.many2one('account.account.template','Root Account',required=True,domain=[('parent_id','=',False)]),
1782         'tax_code_root_id': fields.many2one('account.tax.code.template','Root Tax Code',required=True,domain=[('parent_id','=',False)]),
1783         'tax_template_ids': fields.one2many('account.tax.template', 'chart_template_id', 'Tax Template List', help='List of all the taxes that have to be installed by the wizard'),
1784         'bank_account_view_id': fields.many2one('account.account.template','Bank Account',required=True),
1785         'property_account_receivable': fields.many2one('account.account.template','Receivable Account'),
1786         'property_account_payable': fields.many2one('account.account.template','Payable Account'),
1787         'property_account_expense_categ': fields.many2one('account.account.template','Expense Category Account'),
1788         'property_account_income_categ': fields.many2one('account.account.template','Income Category Account'),
1789         'property_account_expense': fields.many2one('account.account.template','Expense Account on Product Template'),
1790         'property_account_income': fields.many2one('account.account.template','Income Account on Product Template'),
1791     }
1792
1793 account_chart_template()
1794
1795 class account_tax_template(osv.osv):
1796
1797     _name = 'account.tax.template'
1798     _description = 'Templates for Taxes'
1799
1800     _columns = {
1801         'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
1802         'name': fields.char('Tax Name', size=64, required=True),
1803         '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 children. In this case, the evaluation order is important."),
1804         'amount': fields.float('Amount', required=True, digits=(14,4)),
1805         'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1806         'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1807         'domain':fields.char('Domain', size=32, help="This field is only used if you develop your own module allowing developers to create specific taxes in a custom domain."),
1808         'account_collected_id':fields.many2one('account.account.template', 'Invoice Tax Account'),
1809         'account_paid_id':fields.many2one('account.account.template', 'Refund Tax Account'),
1810         'parent_id':fields.many2one('account.tax.template', 'Parent Tax Account', select=True),
1811         'child_depend':fields.boolean('Tax on Childs', help="Indicate if the tax computation is based on the value computed for the computation of child taxes or based on the total amount."),
1812         'python_compute':fields.text('Python Code'),
1813         'python_compute_inv':fields.text('Python Code (reverse)'),
1814         'python_applicable':fields.text('Python Code'),
1815         'tax_group': fields.selection([('vat','VAT'),('other','Other')], 'Tax Group', help="If a default tax if given in the partner it only override taxes from account (or product) of the same group."),
1816
1817         #
1818         # Fields used for the VAT declaration
1819         #
1820         'base_code_id': fields.many2one('account.tax.code.template', 'Base Code', help="Use this code for the VAT declaration."),
1821         'tax_code_id': fields.many2one('account.tax.code.template', 'Tax Code', help="Use this code for the VAT declaration."),
1822         'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1823         'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1824
1825         # Same fields for refund invoices
1826
1827         'ref_base_code_id': fields.many2one('account.tax.code.template', 'Refund Base Code', help="Use this code for the VAT declaration."),
1828         'ref_tax_code_id': fields.many2one('account.tax.code.template', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1829         'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1830         'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1831         'include_base_amount': fields.boolean('Include in base amount', help="Indicate if the amount of tax must be included in the base amount for the computation of the next taxes."),
1832         'description': fields.char('Internal Name', size=32),
1833     }
1834
1835     def name_get(self, cr, uid, ids, context={}):
1836         if not len(ids):
1837             return []
1838         res = []
1839         for record in self.read(cr, uid, ids, ['description','name'], context):
1840             name = record['description'] and record['description'] or record['name']
1841             res.append((record['id'],name ))
1842         return res
1843
1844     def _default_company(self, cr, uid, context={}):
1845         user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1846         if user.company_id:
1847             return user.company_id.id
1848         return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1849
1850     _defaults = {
1851         'python_compute': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or None\n# partner : res.partner object or None\n\nresult = price_unit * 0.10''',
1852         'python_compute_inv': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or False\n\nresult = price_unit * 0.10''',
1853         'applicable_type': lambda *a: 'true',
1854         'type': lambda *a: 'percent',
1855         'amount': lambda *a: 0,
1856         'sequence': lambda *a: 1,
1857         'tax_group': lambda *a: 'vat',
1858         'ref_tax_sign': lambda *a: 1,
1859         'ref_base_sign': lambda *a: 1,
1860         'tax_sign': lambda *a: 1,
1861         'base_sign': lambda *a: 1,
1862         'include_base_amount': lambda *a: False,
1863     }
1864     _order = 'sequence'
1865
1866
1867 account_tax_template()
1868
1869 # Fiscal Position Templates
1870
1871 class account_fiscal_position_template(osv.osv):
1872     _name = 'account.fiscal.position.template'
1873     _description = 'Template for Fiscal Position'
1874     
1875     _columns = {
1876         'name': fields.char('Fiscal Position Template', size=64, translate=True, required=True),
1877         'account_ids': fields.one2many('account.fiscal.position.account.template', 'position_id', 'Accounts Mapping'),
1878         'tax_ids': fields.one2many('account.fiscal.position.tax.template', 'position_id', 'Taxes Mapping')
1879     }
1880     
1881 account_fiscal_position_template()
1882
1883 class account_fiscal_position_tax_template(osv.osv):
1884     _name = 'account.fiscal.position.tax.template'
1885     _description = 'Fiscal Position Template Taxes Mapping'
1886     _rec_name = 'position_id'
1887
1888     _columns = {
1889         'position_id': fields.many2one('account.fiscal.position.template', 'Fiscal Position', required=True, ondelete='cascade'),
1890         'tax_src_id': fields.many2one('account.tax.template', 'Tax Source', required=True),
1891         'tax_dest_id': fields.many2one('account.tax.template', 'Replacement Tax')
1892     }
1893
1894 account_fiscal_position_tax_template()
1895
1896 class account_fiscal_position_account_template(osv.osv):
1897     _name = 'account.fiscal.position.account.template'
1898     _description = 'Fiscal Position Template Accounts Mapping'
1899     _rec_name = 'position_id'
1900     _columns = {
1901         'position_id': fields.many2one('account.fiscal.position.template', 'Fiscal Position', required=True, ondelete='cascade'),
1902         'account_src_id': fields.many2one('account.account.template', 'Account Source', domain=[('type','<>','view')], required=True),
1903         'account_dest_id': fields.many2one('account.account.template', 'Account Destination', domain=[('type','<>','view')], required=True)
1904     }
1905
1906 account_fiscal_position_account_template() 
1907
1908     # Multi charts of Accounts wizard
1909
1910 class wizard_multi_charts_accounts(osv.osv_memory):
1911     """
1912     Create a new account chart for a company.
1913     Wizards ask:
1914         * a company
1915         * an account chart template
1916         * a number of digits for formatting code of non-view accounts
1917         * a list of bank account owned by the company
1918     Then, the wizard:
1919         * generates all accounts from the template and assign them to the right company
1920         * generates all taxes and tax codes, changing account assignations
1921         * generates all accounting properties and assign correctly
1922     """
1923     _name='wizard.multi.charts.accounts'
1924
1925     _columns = {
1926         'company_id':fields.many2one('res.company','Company',required=True),
1927         'chart_template_id': fields.many2one('account.chart.template','Chart Template',required=True),
1928         'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Bank Accounts',required=True),
1929         'code_digits':fields.integer('# of Digits',required=True,help="No. of Digits to use for account code"),
1930     }
1931
1932     def _get_chart(self, cr, uid, context={}):
1933         ids = self.pool.get('account.chart.template').search(cr, uid, [], context=context)
1934         if ids:
1935             return ids[0]
1936         return False
1937     _defaults = {
1938         'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr,uid,[uid],c)[0].company_id.id,
1939         'chart_template_id': _get_chart,
1940         'code_digits': lambda *a:6,
1941     }
1942
1943     def action_create(self, cr, uid, ids, context=None):
1944         obj_multi = self.browse(cr,uid,ids[0])
1945         obj_acc = self.pool.get('account.account')
1946         obj_acc_tax = self.pool.get('account.tax')
1947         obj_journal = self.pool.get('account.journal')
1948         obj_acc_template = self.pool.get('account.account.template')
1949         obj_fiscal_position_template = self.pool.get('account.fiscal.position.template')
1950         obj_fiscal_position = self.pool.get('account.fiscal.position')
1951
1952         # Creating Account
1953         obj_acc_root = obj_multi.chart_template_id.account_root_id
1954         tax_code_root_id = obj_multi.chart_template_id.tax_code_root_id.id
1955         company_id = obj_multi.company_id.id
1956
1957         #new code
1958         acc_template_ref = {}
1959         tax_template_ref = {}
1960         tax_code_template_ref = {}
1961         todo_dict = {}
1962
1963         #create all the tax code
1964         children_tax_code_template = self.pool.get('account.tax.code.template').search(cr, uid, [('parent_id','child_of',[tax_code_root_id])], order='id')
1965         for tax_code_template in self.pool.get('account.tax.code.template').browse(cr, uid, children_tax_code_template):
1966             vals={
1967                 'name': (tax_code_root_id == tax_code_template.id) and obj_multi.company_id.name or tax_code_template.name,
1968                 'code': tax_code_template.code,
1969                 'info': tax_code_template.info,
1970                 'parent_id': tax_code_template.parent_id and ((tax_code_template.parent_id.id in tax_code_template_ref) and tax_code_template_ref[tax_code_template.parent_id.id]) or False,
1971                 'company_id': company_id,
1972                 'sign': tax_code_template.sign,
1973             }
1974             new_tax_code = self.pool.get('account.tax.code').create(cr,uid,vals)
1975             #recording the new tax code to do the mapping
1976             tax_code_template_ref[tax_code_template.id] = new_tax_code
1977
1978         #create all the tax
1979         for tax in obj_multi.chart_template_id.tax_template_ids:
1980             #create it
1981             vals_tax = {
1982                 'name':tax.name,
1983                 'sequence': tax.sequence,
1984                 'amount':tax.amount,
1985                 'type':tax.type,
1986                 'applicable_type': tax.applicable_type,
1987                 'domain':tax.domain,
1988                 'parent_id': tax.parent_id and ((tax.parent_id.id in tax_template_ref) and tax_template_ref[tax.parent_id.id]) or False,
1989                 'child_depend': tax.child_depend,
1990                 'python_compute': tax.python_compute,
1991                 'python_compute_inv': tax.python_compute_inv,
1992                 'python_applicable': tax.python_applicable,
1993                 'tax_group':tax.tax_group,
1994                 'base_code_id': tax.base_code_id and ((tax.base_code_id.id in tax_code_template_ref) and tax_code_template_ref[tax.base_code_id.id]) or False,
1995                 'tax_code_id': tax.tax_code_id and ((tax.tax_code_id.id in tax_code_template_ref) and tax_code_template_ref[tax.tax_code_id.id]) or False,
1996                 'base_sign': tax.base_sign,
1997                 'tax_sign': tax.tax_sign,
1998                 'ref_base_code_id': tax.ref_base_code_id and ((tax.ref_base_code_id.id in tax_code_template_ref) and tax_code_template_ref[tax.ref_base_code_id.id]) or False,
1999                 'ref_tax_code_id': tax.ref_tax_code_id and ((tax.ref_tax_code_id.id in tax_code_template_ref) and tax_code_template_ref[tax.ref_tax_code_id.id]) or False,
2000                 'ref_base_sign': tax.ref_base_sign,
2001                 'ref_tax_sign': tax.ref_tax_sign,
2002                 'include_base_amount': tax.include_base_amount,
2003                 'description':tax.description,
2004                 'company_id': company_id,
2005             }
2006             new_tax = obj_acc_tax.create(cr,uid,vals_tax)
2007             #as the accounts have not been created yet, we have to wait before filling these fields
2008             todo_dict[new_tax] = {
2009                 'account_collected_id': tax.account_collected_id and tax.account_collected_id.id or False,
2010                 'account_paid_id': tax.account_paid_id and tax.account_paid_id.id or False,
2011             }
2012             tax_template_ref[tax.id] = new_tax
2013
2014         #deactivate the parent_store functionnality on account_account for rapidity purpose
2015         self.pool._init = True
2016
2017         children_acc_template = obj_acc_template.search(cr, uid, [('parent_id','child_of',[obj_acc_root.id])])
2018         children_acc_template.sort()
2019         for account_template in obj_acc_template.browse(cr, uid, children_acc_template):
2020             tax_ids = []
2021             for tax in account_template.tax_ids:
2022                 tax_ids.append(tax_template_ref[tax.id])
2023             #create the account_account
2024
2025             dig = obj_multi.code_digits
2026             code_main = account_template.code and len(account_template.code) or 0
2027             code_acc = account_template.code or ''
2028             if code_main>0 and code_main<=dig and account_template.type != 'view':
2029                 code_acc=str(code_acc) + (str('0'*(dig-code_main)))
2030             vals={
2031                 'name': (obj_acc_root.id == account_template.id) and obj_multi.company_id.name or account_template.name,
2032                 #'sign': account_template.sign,
2033                 'currency_id': account_template.currency_id and account_template.currency_id.id or False,
2034                 'code': code_acc,
2035                 'type': account_template.type,
2036                 'user_type': account_template.user_type and account_template.user_type.id or False,
2037                 'reconcile': account_template.reconcile,
2038                 'shortcut': account_template.shortcut,
2039                 'note': account_template.note,
2040                 'parent_id': account_template.parent_id and ((account_template.parent_id.id in acc_template_ref) and acc_template_ref[account_template.parent_id.id]) or False,
2041                 'tax_ids': [(6,0,tax_ids)],
2042                 'company_id': company_id,
2043             }
2044             new_account = obj_acc.create(cr,uid,vals)
2045             acc_template_ref[account_template.id] = new_account
2046         #reactivate the parent_store functionnality on account_account
2047         self.pool._init = False
2048         self.pool.get('account.account')._parent_store_compute(cr)
2049
2050         for key,value in todo_dict.items():
2051             if value['account_collected_id'] or value['account_paid_id']:
2052                 obj_acc_tax.write(cr, uid, [key], vals={
2053                     'account_collected_id': acc_template_ref[value['account_collected_id']],
2054                     'account_paid_id': acc_template_ref[value['account_paid_id']],
2055                 })
2056
2057         # Creating Journals
2058         vals_journal={}
2059         view_id = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Journal View')])[0]
2060         seq_id = self.pool.get('ir.sequence').search(cr,uid,[('code','=','account.journal')])[0]
2061         seq_code = self.pool.get('ir.sequence').get(cr, uid, 'account.journal')
2062
2063         vals_journal['view_id']=view_id
2064         vals_journal['sequence_id']=seq_id
2065
2066         #Sales Journal
2067         vals_journal['name'] = _('Sales Journal')
2068         vals_journal['type'] = 'sale'
2069         vals_journal['code'] = _('SAJ')
2070
2071         if obj_multi.chart_template_id.property_account_receivable:
2072             vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_receivable.id]
2073             vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_receivable.id]
2074
2075         obj_journal.create(cr,uid,vals_journal)
2076
2077         # Purchase Journal
2078         vals_journal['name']=_('Purchase Journal')
2079         vals_journal['type']='purchase'
2080         vals_journal['code']=_('EXJ')
2081
2082         if obj_multi.chart_template_id.property_account_payable:
2083             vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_payable.id]
2084             vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_payable.id]
2085
2086         obj_journal.create(cr,uid,vals_journal)
2087
2088         # Bank Journals
2089         view_id_cash = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Cash Journal View')])[0]
2090         view_id_cur = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Multi-Currency Cash Journal View')])[0]
2091         ref_acc_bank = obj_multi.chart_template_id.bank_account_view_id
2092
2093         current_num = 1
2094         for line in obj_multi.bank_accounts_id:
2095             #create the account_account for this bank journal
2096             tmp = self.pool.get('res.partner.bank').name_get(cr, uid, [line.acc_no.id])[0][1]
2097             dig = obj_multi.code_digits
2098             vals={
2099                 'name': line.acc_no.bank and line.acc_no.bank.name+' '+tmp or tmp,
2100                 'currency_id': line.currency_id and line.currency_id.id or False,
2101                 'code': str(ref_acc_bank.code.ljust(dig,'0') + str(current_num)),
2102                 'type': 'other',
2103                 'user_type': account_template.user_type and account_template.user_type.id or False,
2104                 'reconcile': True,
2105                 'parent_id': acc_template_ref[ref_acc_bank.id] or False,
2106                 'company_id': company_id,
2107             }
2108             acc_cash_id  = obj_acc.create(cr,uid,vals)
2109
2110             #create the bank journal
2111             vals_journal['name']= vals['name']
2112             vals_journal['code']= _('BNK') + str(current_num)
2113             vals_journal['sequence_id'] = seq_id
2114             vals_journal['type'] = 'cash'
2115             if line.currency_id:
2116                 vals_journal['view_id'] = view_id_cur
2117                 vals_journal['currency'] = line.currency_id.id
2118             else:
2119                 vals_journal['view_id'] = view_id_cash
2120             vals_journal['default_credit_account_id'] = acc_cash_id
2121             vals_journal['default_debit_account_id']= acc_cash_id
2122             obj_journal.create(cr,uid,vals_journal)
2123
2124             current_num += 1
2125
2126         #create the properties
2127         property_obj = self.pool.get('ir.property')
2128         fields_obj = self.pool.get('ir.model.fields')
2129
2130         todo_list = [
2131             ('property_account_receivable','res.partner','account.account'),
2132             ('property_account_payable','res.partner','account.account'),
2133             ('property_account_expense_categ','product.category','account.account'),
2134             ('property_account_income_categ','product.category','account.account'),
2135             ('property_account_expense','product.template','account.account'),
2136             ('property_account_income','product.template','account.account')
2137         ]
2138         for record in todo_list:
2139             r = []
2140             r = property_obj.search(cr, uid, [('name','=', record[0] ),('company_id','=',company_id)])
2141             account = getattr(obj_multi.chart_template_id, record[0])
2142             field = fields_obj.search(cr, uid, [('name','=',record[0]),('model','=',record[1]),('relation','=',record[2])])
2143             vals = {
2144                 'name': record[0],
2145                 'company_id': company_id,
2146                 'fields_id': field[0],
2147                 'value': account and 'account.account,'+str(acc_template_ref[account.id]) or False,
2148             }
2149             if r:
2150                 #the property exist: modify it
2151                 property_obj.write(cr, uid, r, vals)
2152             else:
2153                 #create the property
2154                 property_obj.create(cr, uid, vals)
2155
2156         fp_ids = obj_fiscal_position_template.search(cr, uid,[])
2157         
2158         if fp_ids:
2159             for position in obj_fiscal_position_template.browse(cr, uid, fp_ids):
2160                 
2161                 vals_fp = {
2162                            'company_id' : company_id,
2163                            'name' : position.name,
2164                            }
2165                 new_fp = obj_fiscal_position.create(cr, uid, vals_fp)
2166                 
2167                 obj_tax_fp = self.pool.get('account.fiscal.position.tax')
2168                 obj_ac_fp = self.pool.get('account.fiscal.position.account')
2169                 
2170                 for tax in position.tax_ids:
2171                     vals_tax = {
2172                                 'tax_src_id' : tax_template_ref[tax.tax_src_id.id],
2173                                 'tax_dest_id' : tax_template_ref[tax.tax_dest_id.id],
2174                                 'position_id' : new_fp,
2175                                 }
2176                     obj_tax_fp.create(cr, uid, vals_tax)
2177                 
2178                 for acc in position.account_ids:
2179                     vals_acc = {
2180                                 'account_src_id' : acc_template_ref[acc.account_src_id.id],
2181                                 'account_dest_id' : acc_template_ref[acc.account_dest_id.id],
2182                                 'position_id' : new_fp,
2183                                 }
2184                     obj_ac_fp.create(cr, uid, vals_acc)
2185         
2186         return {
2187                 'view_type': 'form',
2188                 "view_mode": 'form',
2189                 'res_model': 'ir.actions.configuration.wizard',
2190                 'type': 'ir.actions.act_window',
2191                 'target':'new',
2192         }
2193     def action_cancel(self,cr,uid,ids,conect=None):
2194         return {
2195                 'view_type': 'form',
2196                 "view_mode": 'form',
2197                 'res_model': 'ir.actions.configuration.wizard',
2198                 'type': 'ir.actions.act_window',
2199                 'target':'new',
2200         }
2201
2202
2203 wizard_multi_charts_accounts()
2204
2205 class account_bank_accounts_wizard(osv.osv_memory):
2206     _name='account.bank.accounts.wizard'
2207
2208     _columns = {
2209         'acc_no':fields.many2one('res.partner.bank','Account No.',required=True),
2210         'bank_account_id':fields.many2one('wizard.multi.charts.accounts', 'Bank Account', required=True),
2211         'currency_id':fields.many2one('res.currency', 'Currency'),
2212     }
2213
2214 account_bank_accounts_wizard()
2215
2216 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
2217