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