bugfix_domain_account
[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     _parent_order = 'length(code),code'
134
135     def search(self, cr, uid, args, offset=0, limit=None, order=None,
136             context=None, count=False):
137         if context is None:
138             context = {}
139         pos = 0
140
141         while pos<len(args):
142
143             if args[pos][0]=='code' and args[pos][1] in ('like','ilike') and args[pos][2]:
144                 args[pos] = ('code', '=like', str(args[pos][2].replace('%',''))+'%')
145             if args[pos][0]=='journal_id':
146                 if not args[pos][2]:
147                     del args[pos]
148                     continue
149                 jour = self.pool.get('account.journal').browse(cr, uid, args[pos][2])
150                 if (not (jour.account_control_ids or jour.type_control_ids)) or not args[pos][2]:
151                     del args[pos]
152                     continue
153                 ids3 = map(lambda x: x.code, jour.type_control_ids)
154                 ids1 = super(account_account,self).search(cr, uid, [('type','in',ids3)])
155                 ids1 += map(lambda x: x.id, jour.account_control_ids)
156                 args[pos] = ('id','in',ids1)
157             pos+=1
158
159         if context and context.has_key('consolidate_childs'): #add consolidated childs of accounts
160             ids = super(account_account,self).search(cr, uid, args, offset, limit,
161                 order, context=context, count=count)
162             for consolidate_child in self.browse(cr, uid, context['account_id']).child_consol_ids:
163                 ids.append(consolidate_child.id)
164             return ids
165
166         return super(account_account,self).search(cr, uid, args, offset, limit,
167                 order, context=context, count=count)
168
169     def _get_children_and_consol(self, cr, uid, ids, context={}):
170         #this function search for all the children and all consolidated children (recursively) of the given account ids
171         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)], context=context)
172         ids3 = []
173         for rec in self.browse(cr, uid, ids2, context=context):
174             for child in rec.child_consol_ids:
175                 ids3.append(child.id)
176         if ids3:
177             ids3 = self._get_children_and_consol(cr, uid, ids3, context)
178         return ids2+ids3
179
180     def __compute(self, cr, uid, ids, field_names, arg, context={}, query=''):
181         #compute the balance/debit/credit accordingly to the value of field_name for the given account ids
182         mapping = {
183             'balance': "COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance ",
184             'debit': "COALESCE(SUM(l.debit), 0) as debit ",
185             'credit': "COALESCE(SUM(l.credit), 0) as credit "
186         }
187         #get all the necessary accounts
188         ids2 = self._get_children_and_consol(cr, uid, ids, context)
189         acc_set = ",".join(map(str, ids2))
190         #compute for each account the balance/debit/credit from the move lines
191         accounts = {}
192         if ids2:
193             query = self.pool.get('account.move.line')._query_get(cr, uid,
194                     context=context)
195             cr.execute(("SELECT l.account_id as id, " +\
196                     ' , '.join(map(lambda x: mapping[x], field_names)) +
197                     "FROM " \
198                         "account_move_line l " \
199                     "WHERE " \
200                         "l.account_id IN (%s) " \
201                         "AND " + query + " " \
202                     "GROUP BY l.account_id") % (acc_set, ))
203
204             for res in cr.dictfetchall():
205                 accounts[res['id']] = res
206
207         #for the asked accounts, get from the dictionnary 'accounts' the value of it
208         res = {}
209         for id in ids:
210             res[id] = self._get_account_values(cr, uid, id, accounts, field_names, context)
211         return res
212
213     def _get_account_values(self, cr, uid, id, accounts, field_names, context={}):
214         res = {}.fromkeys(field_names, 0.0)
215         browse_rec = self.browse(cr, uid, id)
216         if browse_rec.type == 'consolidation':
217             ids2 = self.read(cr, uid, [browse_rec.id], ['child_consol_ids'], context)[0]['child_consol_ids']
218             for t in self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])]):
219                 if t not in ids2 and t != browse_rec.id:
220                     ids2.append(t)
221             for i in ids2:
222                 tmp = self._get_account_values(cr, uid, i, accounts, field_names, context)
223                 for a in field_names:
224                     res[a] += tmp[a]
225         else:
226             ids2 = self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])])
227             for i in ids2:
228                 for a in field_names:
229                     res[a] += accounts.get(i, {}).get(a, 0.0)
230         return res
231
232     def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}):
233         result = {}
234         for rec in self.browse(cr, uid, ids, context):
235             result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code)
236         return result
237
238     def _get_child_ids(self, cr, uid, ids, field_name, arg, context={}):
239         result={}
240         for record in self.browse(cr, uid, ids, context):
241             if record.child_parent_ids:
242                 result[record.id]=[x.id for x in record.child_parent_ids]
243             else:
244                 result[record.id]=[]
245
246             if record.child_consol_ids:
247                 for acc in record.child_consol_ids:
248                     result[record.id].append(acc.id)
249
250         return result
251
252     _columns = {
253         'name': fields.char('Name', size=128, required=True, select=True),
254         'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
255         'code': fields.char('Code', size=64, required=True),
256         'type': fields.selection([
257             ('receivable','Receivable'),
258             ('payable','Payable'),
259             ('view','View'),
260             ('consolidation','Consolidation'),
261             ('other','Others'),
262             ('closed','Closed'),
263         ], 'Internal Type', required=True,),
264
265         'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
266         'parent_id': fields.many2one('account.account','Parent', ondelete='cascade'),
267         'child_parent_ids':fields.one2many('account.account','parent_id','Children'),
268         'child_consol_ids':fields.many2many('account.account', 'account_account_consol_rel', 'child_id', 'parent_id', 'Consolidated Children'),
269         'child_id': fields.function(_get_child_ids, method=True, type='many2many',relation="account.account",string="Children Accounts"),
270         'balance': fields.function(__compute, digits=(16,2), method=True, string='Balance', multi='balance'),
271         'credit': fields.function(__compute, digits=(16,2), method=True, string='Credit', multi='balance'),
272         'debit': fields.function(__compute, digits=(16,2), method=True, string='Debit', multi='balance'),
273         'reconcile': fields.boolean('Reconcile', help="Check this account if the user can make a reconciliation of the entries in this account."),
274         'shortcut': fields.char('Shortcut', size=12),
275         'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel',
276             'account_id','tax_id', 'Default Taxes'),
277         'note': fields.text('Note'),
278         'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Company Currency'),
279         'company_id': fields.many2one('res.company', 'Company', required=True),
280         'active': fields.boolean('Active', select=2),
281
282         'parent_left': fields.integer('Parent Left', select=1),
283         'parent_right': fields.integer('Parent Right', select=1),
284         'currency_mode': fields.selection([('current','At Date'),('average','Average Rate')], 'Outgoing Currencies Rate',
285             help=
286             'This will select how is computed the current currency rate for outgoing transactions. '\
287             'In most countries the legal method is "average" but only a few softwares are able to '\
288             'manage this. So if you import from another software, you may have to use the rate at date. ' \
289             'Incoming transactions, always use the rate at date.', \
290             required=True),
291         'check_history': fields.boolean('Display History',
292             help="Check this box if you want to print all entries when printing the General Ledger, "\
293             "otherwise it will only print its balance."),
294     }
295
296     def _default_company(self, cr, uid, context={}):
297         user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
298         if user.company_id:
299             return user.company_id.id
300         return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
301
302     _defaults = {
303         'type' : lambda *a :'view',
304         'reconcile': lambda *a: False,
305         'company_id': _default_company,
306         'active': lambda *a: True,
307         'check_history': lambda *a: True,
308         'currency_mode': lambda *a: 'current'
309     }
310
311     def _check_recursion(self, cr, uid, ids):
312         obj_self=self.browse(cr,uid,ids[0])
313         p_id=obj_self.parent_id and obj_self.parent_id.id
314         if (obj_self in obj_self.child_consol_ids) or (p_id and (p_id is obj_self.id)):
315             return False
316         while(ids):
317             cr.execute('select distinct child_id from account_account_consol_rel where parent_id in ('+','.join(map(str,ids))+')')
318             child_ids = filter(None, map(lambda x:x[0], cr.fetchall()))
319             c_ids=child_ids
320             if (p_id and (p_id in c_ids)) or (obj_self.id in c_ids):
321                 return False
322             while len(c_ids):
323                 s_ids=self.search(cr,uid,[('parent_id','in',c_ids)])
324                 if p_id and (p_id in s_ids):
325                     return False
326                 c_ids=s_ids
327             ids=child_ids
328         return True
329
330     _constraints = [
331         (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
332     ]
333     def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
334         if not args:
335             args=[]
336         if not context:
337             context = {}
338         args = args[:]
339         ids = []
340         try:
341             if name and str(name).startswith('partner:'):
342                 part_id = int(name.split(':')[1])
343                 part = self.pool.get('res.partner').browse(cr, user, part_id, context)
344                 args += [('id','in', (part.property_account_payable.id, part.property_account_receivable.id))]
345                 name = False
346             if name and str(name).startswith('type:'):
347                 type = name.split(':')[1]
348                 args += [('type','=', type)]
349                 name = False
350         except:
351             pass
352         if name:
353             ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit)
354             if not ids:
355                 ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit)
356             if not ids:
357                 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
358         else:
359             ids = self.search(cr, user, args, context=context, limit=limit)
360         return self.name_get(cr, user, ids, context=context)
361
362     def name_get(self, cr, uid, ids, context={}):
363         if not len(ids):
364             return []
365         reads = self.read(cr, uid, ids, ['name','code'], context)
366         res = []
367         for record in reads:
368             name = record['name']
369             if record['code']:
370                 name = record['code']+' '+name
371             res.append((record['id'],name ))
372         return res
373
374     def copy(self, cr, uid, id, default={}, context={},done_list=[]):
375         account = self.browse(cr, uid, id, context=context)
376         new_child_ids = []
377         if not default:
378             default={}
379         default=default.copy()
380         default['parent_id'] = False
381         if account.id in done_list:
382             return False
383         done_list.append(account.id)
384         if account:
385             for child in account.child_id:
386                 child_ids=self.copy(cr, uid, child.id, default, context=context,done_list=done_list)
387                 if child_ids:
388                     new_child_ids.append(child_ids)
389             default['child_parent_ids'] = [(6, 0, new_child_ids)]
390         else:
391             default['child_parent_ids'] = False
392         return super(account_account, self).copy(cr, uid, id, default, context=context)
393
394     def write(self, cr, uid, ids, vals, context=None):
395         if not context:
396             context={}
397         if 'active' in vals and not vals['active']:
398             line_obj = self.pool.get('account.move.line')
399             account_ids = self.search(cr, uid, [('id', 'child_of', ids)])
400             if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]):
401                 raise osv.except_osv(_('Error !'), _('You can not deactivate an account that contains account moves.'))
402         return super(account_account, self).write(cr, uid, ids, vals, context=context)
403 account_account()
404
405 class account_journal_view(osv.osv):
406     _name = "account.journal.view"
407     _description = "Journal View"
408     _columns = {
409         'name': fields.char('Journal View', size=64, required=True),
410         'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
411     }
412     _order = "name"
413 account_journal_view()
414
415
416 class account_journal_column(osv.osv):
417     def _col_get(self, cr, user, context={}):
418         result = []
419         cols = self.pool.get('account.move.line')._columns
420         for col in cols:
421             result.append( (col, cols[col].string) )
422         result.sort()
423         return result
424     _name = "account.journal.column"
425     _description = "Journal Column"
426     _columns = {
427         'name': fields.char('Column Name', size=64, required=True),
428         'field': fields.selection(_col_get, 'Field Name', method=True, required=True, size=32),
429         'view_id': fields.many2one('account.journal.view', 'Journal View', select=True),
430         'sequence': fields.integer('Sequence'),
431         'required': fields.boolean('Required'),
432         'readonly': fields.boolean('Readonly'),
433     }
434     _order = "sequence"
435 account_journal_column()
436
437 class account_journal(osv.osv):
438     _name = "account.journal"
439     _description = "Journal"
440     _columns = {
441         'name': fields.char('Journal Name', size=64, required=True, translate=True),
442         'code': fields.char('Code', size=16),
443         'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True),
444         'refund_journal': fields.boolean('Refund Journal'),
445
446         'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]),
447         'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]),
448
449         'active': fields.boolean('Active'),
450         '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."),
451         'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account', domain="[('type','!=','view')]"),
452         'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account', domain="[('type','!=','view')]"),
453         '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."),
454         'update_posted': fields.boolean('Allow Cancelling Entries'),
455         '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."),
456         'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="The sequence gives the display order for a list of journals", required=True),
457         'user_id': fields.many2one('res.users', 'User', help="The responsible user of this journal"),
458         'groups_id': fields.many2many('res.groups', 'account_journal_group_rel', 'journal_id', 'group_id', 'Groups'),
459         'currency': fields.many2one('res.currency', 'Currency', help='The currency used to enter statement'),
460         '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.'),
461         'company_id': fields.related('default_credit_account_id','company_id',type='many2one', relation="res.company", string="Company"),
462         'fy_seq_id': fields.one2many('fiscalyear.seq', 'journal_id', 'Sequences'),
463     }
464
465     _defaults = {
466         'active': lambda *a: 1,
467         'user_id': lambda self,cr,uid,context: uid,
468     }
469     def create(self, cr, uid, vals, context={}):
470         journal_id = super(osv.osv, self).create(cr, uid, vals, context)
471 #       journal_name = self.browse(cr, uid, [journal_id])[0].code
472 #       periods = self.pool.get('account.period')
473 #       ids = periods.search(cr, uid, [('date_stop','>=',time.strftime('%Y-%m-%d'))])
474 #       for period in periods.browse(cr, uid, ids):
475 #           self.pool.get('account.journal.period').create(cr, uid, {
476 #               'name': (journal_name or '')+':'+(period.code or ''),
477 #               'journal_id': journal_id,
478 #               'period_id': period.id
479 #           })
480         return journal_id
481     def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
482         if not args:
483             args=[]
484         if not context:
485             context={}
486         ids = []
487         if name:
488             ids = self.search(cr, user, [('code','ilike',name)]+ args, limit=limit)
489         if not ids:
490             ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
491         return self.name_get(cr, user, ids, context=context)
492 account_journal()
493
494 class account_fiscalyear(osv.osv):
495     _name = "account.fiscalyear"
496     _description = "Fiscal Year"
497     _columns = {
498         'name': fields.char('Fiscal Year', size=64, required=True),
499         'code': fields.char('Code', size=6, required=True),
500         'company_id': fields.many2one('res.company', 'Company',
501             help="Keep empty if the fiscal year belongs to several companies."),
502         'date_start': fields.date('Start date', required=True),
503         'date_stop': fields.date('End date', required=True),
504         'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'),
505         'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', redonly=True),
506     }
507
508     _defaults = {
509         'state': lambda *a: 'draft',
510     }
511     _order = "date_start"
512
513     def _check_duration(self,cr,uid,ids):
514         obj_fy=self.browse(cr,uid,ids[0])
515         if obj_fy.date_stop < obj_fy.date_start:
516             return False
517         return True
518
519     _constraints = [
520         (_check_duration, 'Error ! The date duration of the Fiscal Year is invalid. ', ['date_stop'])
521     ]
522
523     def create_period3(self,cr, uid, ids, context={}):
524         return self.create_period(cr, uid, ids, context, 3)
525
526     def create_period(self,cr, uid, ids, context={}, interval=1):
527         for fy in self.browse(cr, uid, ids, context):
528             dt = fy.date_start
529             ds = mx.DateTime.strptime(fy.date_start, '%Y-%m-%d')
530             while ds.strftime('%Y-%m-%d')<fy.date_stop:
531                 de = ds + RelativeDateTime(months=interval, days=-1)
532
533                 if de.strftime('%Y-%m-%d')>fy.date_stop:
534                     de=mx.DateTime.strptime(fy.date_stop, '%Y-%m-%d')
535
536                 self.pool.get('account.period').create(cr, uid, {
537                     'name': ds.strftime('%m/%Y'),
538                     'code': ds.strftime('%m/%Y'),
539                     'date_start': ds.strftime('%Y-%m-%d'),
540                     'date_stop': de.strftime('%Y-%m-%d'),
541                     'fiscalyear_id': fy.id,
542                 })
543                 ds = ds + RelativeDateTime(months=interval)
544         return True
545
546     def find(self, cr, uid, dt=None, exception=True, context={}):
547         if not dt:
548             dt = time.strftime('%Y-%m-%d')
549         ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)])
550         if not ids:
551             if exception:
552                 raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.'))
553             else:
554                 return False
555         return ids[0]
556 account_fiscalyear()
557
558 class account_period(osv.osv):
559     _name = "account.period"
560     _description = "Account period"
561     _columns = {
562         'name': fields.char('Period Name', size=64, required=True),
563         'code': fields.char('Code', size=12),
564         'special': fields.boolean('Opening/Closing Period', size=12,
565             help="These periods can overlap."),
566         'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}),
567         'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}),
568         'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True),
569         'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', readonly=True)
570     }
571     _defaults = {
572         'state': lambda *a: 'draft',
573     }
574     _order = "date_start"
575
576     def _check_duration(self,cr,uid,ids,context={}):
577         obj_period=self.browse(cr,uid,ids[0])
578         if obj_period.date_stop < obj_period.date_start:
579             return False
580         return True
581
582     def _check_year_limit(self,cr,uid,ids,context={}):
583         for obj_period in self.browse(cr,uid,ids):
584             if obj_period.special:
585                 continue
586             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:
587                 return False
588
589             pids = self.search(cr, uid, [('date_stop','>=',obj_period.date_start),('date_start','<=',obj_period.date_stop),('special','=',False),('id','<>',obj_period.id)])
590             for period in self.browse(cr, uid, pids):
591                 if period.fiscalyear_id.company_id.id==obj_period.fiscalyear_id.company_id.id:
592                     return False
593         return True
594
595     _constraints = [
596         (_check_duration, 'Error ! The date duration of the Period(s) is invalid. ', ['date_stop']),
597         (_check_year_limit, 'Invalid period ! Some periods overlap or the date duration is not in the limit of the fiscal year. ', ['date_stop'])
598     ]
599
600     def next(self, cr, uid, period, step, context={}):
601         ids = self.search(cr, uid, [('date_start','>',period.date_start)])
602         if len(ids)>=step:
603             return ids[step-1]
604         return False
605
606     def find(self, cr, uid, dt=None, context={}):
607         if not dt:
608             dt = time.strftime('%Y-%m-%d')
609 #CHECKME: shouldn't we check the state of the period?
610         ids = self.search(cr, uid, [('date_start','<=',dt),('date_stop','>=',dt)])
611         if not ids:
612             raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.'))
613         return ids
614
615     def action_draft(self, cr, uid, ids, *args):
616         users_roles = self.pool.get('res.users').browse(cr, uid, uid).roles_id
617         for role in users_roles:
618             if role.name=='Period':
619                 mode = 'draft'
620                 for id in ids:
621                     cr.execute('update account_journal_period set state=%s where period_id=%s', (mode, id))
622                     cr.execute('update account_period set state=%s where id=%s', (mode, id))
623         return True
624
625 account_period()
626
627 class account_journal_period(osv.osv):
628     _name = "account.journal.period"
629     _description = "Journal - Period"
630
631     def _icon_get(self, cr, uid, ids, field_name, arg=None, context={}):
632         result = {}.fromkeys(ids, 'STOCK_NEW')
633         for r in self.read(cr, uid, ids, ['state']):
634             result[r['id']] = {
635                 'draft': 'STOCK_NEW',
636                 'printed': 'STOCK_PRINT_PREVIEW',
637                 'done': 'STOCK_DIALOG_AUTHENTICATION',
638             }.get(r['state'], 'STOCK_NEW')
639         return result
640
641     _columns = {
642         'name': fields.char('Journal-Period Name', size=64, required=True),
643         'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"),
644         'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"),
645         'icon': fields.function(_icon_get, method=True, string='Icon', type='string'),
646         'active': fields.boolean('Active', required=True),
647         'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'Status', required=True, readonly=True)
648     }
649
650     def _check(self, cr, uid, ids, context={}):
651         for obj in self.browse(cr, uid, ids, context):
652             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))
653             res = cr.fetchall()
654             if res:
655                 raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !'))
656         return True
657
658     def write(self, cr, uid, ids, vals, context={}):
659         self._check(cr, uid, ids, context)
660         return super(account_journal_period, self).write(cr, uid, ids, vals, context)
661
662     def create(self, cr, uid, vals, context={}):
663         period_id=vals.get('period_id',False)
664         if period_id:
665             period = self.pool.get('account.period').browse(cr, uid,period_id)
666             vals['state']=period.state
667         return super(account_journal_period, self).create(cr, uid, vals, context)
668
669     def unlink(self, cr, uid, ids, context={}):
670         self._check(cr, uid, ids, context)
671         return super(account_journal_period, self).unlink(cr, uid, ids, context)
672
673     _defaults = {
674         'state': lambda *a: 'draft',
675         'active': lambda *a: True,
676     }
677     _order = "period_id"
678
679 account_journal_period()
680
681 class account_fiscalyear(osv.osv):
682     _inherit = "account.fiscalyear"
683     _description = "Fiscal Year"
684     _columns = {
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_year(self, cr, uid, ids, name, args, context):
1141         if 'fiscalyear_id' in context and context['fiscalyear_id']:
1142             fiscalyear_id = context['fiscalyear_id']
1143         else:
1144             fiscalyear_id = self.pool.get('account.fiscalyear').find(cr, uid, exception=False)
1145         where = ''
1146         if fiscalyear_id:
1147              pids = map(lambda x: str(x.id), self.pool.get('account.fiscalyear').browse(cr, uid, fiscalyear_id).period_ids)
1148              if pids:
1149                  where = ' and period_id in (' + (','.join(pids))+')'
1150         return self._sum(cr, uid, ids, name, args, context,
1151                 where=where)
1152
1153     def _sum_period(self, cr, uid, ids, name, args, context):
1154         if 'period_id' in context and context['period_id']:
1155             period_id = context['period_id']
1156         else:
1157             period_id = self.pool.get('account.period').find(cr, uid)
1158             if not len(period_id):
1159                 return dict.fromkeys(ids, 0.0)
1160             period_id = period_id[0]
1161         return self._sum(cr, uid, ids, name, args, context,
1162                 where=' and line.period_id='+str(period_id))
1163
1164     _name = 'account.tax.code'
1165     _description = 'Tax Code'
1166     _rec_name = 'code'
1167     _columns = {
1168         'name': fields.char('Tax Case Name', size=64, required=True),
1169         'code': fields.char('Case Code', size=64),
1170         'info': fields.text('Description'),
1171         'sum': fields.function(_sum_year, method=True, string="Year Sum"),
1172         'sum_period': fields.function(_sum_period, method=True, string="Period Sum"),
1173         'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True),
1174         'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'),
1175         'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'),
1176         'company_id': fields.many2one('res.company', 'Company', required=True),
1177         'sign': fields.float('Sign for parent', required=True),
1178     }
1179
1180     def name_get(self, cr, uid, ids, context=None):
1181         if not len(ids):
1182             return []
1183         if isinstance(ids, (int, long)):
1184             ids = [ids]
1185         reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1186         return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1187                 for x in reads]
1188
1189     def _default_company(self, cr, uid, context={}):
1190         user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1191         if user.company_id:
1192             return user.company_id.id
1193         return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1194     _defaults = {
1195         'company_id': _default_company,
1196         'sign': lambda *args: 1.0,
1197     }
1198     def _check_recursion(self, cr, uid, ids):
1199         level = 100
1200         while len(ids):
1201             cr.execute('select distinct parent_id from account_tax_code where id in ('+','.join(map(str,ids))+')')
1202             ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1203             if not level:
1204                 return False
1205             level -= 1
1206         return True
1207
1208     _constraints = [
1209         (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
1210     ]
1211     _order = 'code,name'
1212 account_tax_code()
1213
1214 class account_tax(osv.osv):
1215     """
1216     A tax object.
1217
1218     Type: percent, fixed, none, code
1219         PERCENT: tax = price * amount
1220         FIXED: tax = price + amount
1221         NONE: no tax line
1222         CODE: execute python code. localcontext = {'price_unit':pu, 'address':address_object}
1223             return result in the context
1224             Ex: result=round(price_unit*0.21,4)
1225     """
1226     _name = 'account.tax'
1227     _description = 'Tax'
1228     _columns = {
1229         'name': fields.char('Tax Name', size=64, required=True, help="This name will be used to be displayed on reports"),
1230         '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."),
1231         'amount': fields.float('Amount', required=True, digits=(14,4)),
1232         'active': fields.boolean('Active'),
1233         'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1234         'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True,
1235             help="If not applicable (computed through a Python code), the tax do not appears on the invoice."),
1236         '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."),
1237         'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'),
1238         'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'),
1239         'parent_id':fields.many2one('account.tax', 'Parent Tax Account', select=True),
1240         'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'),
1241         '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."),
1242         'python_compute':fields.text('Python Code'),
1243         'python_compute_inv':fields.text('Python Code (reverse)'),
1244         'python_applicable':fields.text('Python Code'),
1245         '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."),
1246
1247         #
1248         # Fields used for the VAT declaration
1249         #
1250         'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."),
1251         'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."),
1252         'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1253         'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1254
1255         # Same fields for refund invoices
1256
1257         'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the VAT declaration."),
1258         'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1259         'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1260         'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1261         '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"),
1262         'company_id': fields.many2one('res.company', 'Company', required=True),
1263         'description': fields.char('Internal Name',size=32),
1264         '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.")
1265     }
1266
1267     def name_get(self, cr, uid, ids, context={}):
1268         if not len(ids):
1269             return []
1270         res = []
1271         for record in self.read(cr, uid, ids, ['description','name'], context):
1272             name = record['description'] and record['description'] or record['name']
1273             res.append((record['id'],name ))
1274         return res
1275
1276     def _default_company(self, cr, uid, context={}):
1277         user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1278         if user.company_id:
1279             return user.company_id.id
1280         return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1281     _defaults = {
1282         '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''',
1283         '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''',
1284         'applicable_type': lambda *a: 'true',
1285         'type': lambda *a: 'percent',
1286         'amount': lambda *a: 0,
1287         'price_include': lambda *a: 0,
1288         'active': lambda *a: 1,
1289         'sequence': lambda *a: 1,
1290         'tax_group': lambda *a: 'vat',
1291         'ref_tax_sign': lambda *a: 1,
1292         'ref_base_sign': lambda *a: 1,
1293         'tax_sign': lambda *a: 1,
1294         'base_sign': lambda *a: 1,
1295         'include_base_amount': lambda *a: False,
1296         'company_id': _default_company,
1297     }
1298     _order = 'sequence'
1299
1300     def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1301         res = []
1302         for tax in taxes:
1303             if tax.applicable_type=='code':
1304                 localdict = {'price_unit':price_unit, 'address':self.pool.get('res.partner.address').browse(cr, uid, address_id), 'product':product, 'partner':partner}
1305                 exec tax.python_applicable in localdict
1306                 if localdict.get('result', False):
1307                     res.append(tax)
1308             else:
1309                 res.append(tax)
1310         return res
1311
1312     def _unit_compute(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1313         taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1314
1315         res = []
1316         cur_price_unit=price_unit
1317         for tax in taxes:
1318             # we compute the amount for the current tax object and append it to the result
1319
1320             data = {'id':tax.id,
1321                             'name':tax.name,
1322                             'account_collected_id':tax.account_collected_id.id,
1323                             'account_paid_id':tax.account_paid_id.id,
1324                             'base_code_id': tax.base_code_id.id,
1325                             'ref_base_code_id': tax.ref_base_code_id.id,
1326                             'sequence': tax.sequence,
1327                             'base_sign': tax.base_sign,
1328                             'tax_sign': tax.tax_sign,
1329                             'ref_base_sign': tax.ref_base_sign,
1330                             'ref_tax_sign': tax.ref_tax_sign,
1331                             'price_unit': cur_price_unit,
1332                             'tax_code_id': tax.tax_code_id.id,
1333                             'ref_tax_code_id': tax.ref_tax_code_id.id,
1334             }
1335             res.append(data)
1336             if tax.type=='percent':
1337                 amount = cur_price_unit * tax.amount
1338                 data['amount'] = amount
1339
1340             elif tax.type=='fixed':
1341                 data['amount'] = tax.amount
1342             elif tax.type=='code':
1343                 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1344                 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1345                 exec tax.python_compute in localdict
1346                 amount = localdict['result']
1347                 data['amount'] = amount
1348             amount2 = data['amount']
1349             if len(tax.child_ids):
1350                 if tax.child_depend:
1351                     del res[-1]
1352                 amount = amount2
1353                 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner)
1354                 res.extend(child_tax)
1355             if tax.include_base_amount:
1356                 cur_price_unit+=amount2
1357         return res
1358
1359     def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1360
1361         """
1362         Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1363
1364         RETURN:
1365             [ tax ]
1366             tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1367             one tax for each tax id in IDS and their childs
1368         """
1369         res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner)
1370         for r in res:
1371             r['amount'] *= quantity
1372         return res
1373
1374     def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1375         taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1376
1377         res = []
1378         taxes.reverse()
1379         cur_price_unit=price_unit
1380
1381         tax_parent_tot = 0.0
1382         for tax in taxes:
1383             if (tax.type=='percent') and not tax.include_base_amount:
1384                 tax_parent_tot+=tax.amount
1385
1386         for tax in taxes:
1387             if tax.type=='percent':
1388                 if tax.include_base_amount:
1389                     amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
1390                 else:
1391                     amount = (cur_price_unit / (1 + tax_parent_tot)) * tax.amount
1392
1393             elif tax.type=='fixed':
1394                 amount = tax.amount
1395
1396             elif tax.type=='code':
1397                 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1398                 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1399                 exec tax.python_compute_inv in localdict
1400                 amount = localdict['result']
1401
1402             if tax.include_base_amount:
1403                 cur_price_unit -= amount
1404                 todo = 0
1405             else:
1406                 todo = 1
1407             res.append({
1408                 'id': tax.id,
1409                 'todo': todo,
1410                 'name': tax.name,
1411                 'amount': amount,
1412                 'account_collected_id': tax.account_collected_id.id,
1413                 'account_paid_id': tax.account_paid_id.id,
1414                 'base_code_id': tax.base_code_id.id,
1415                 'ref_base_code_id': tax.ref_base_code_id.id,
1416                 'sequence': tax.sequence,
1417                 'base_sign': tax.base_sign,
1418                 'tax_sign': tax.tax_sign,
1419                 'ref_base_sign': tax.ref_base_sign,
1420                 'ref_tax_sign': tax.ref_tax_sign,
1421                 'price_unit': cur_price_unit,
1422                 'tax_code_id': tax.tax_code_id.id,
1423                 'ref_tax_code_id': tax.ref_tax_code_id.id,
1424             })
1425             if len(tax.child_ids):
1426                 if tax.child_depend:
1427                     del res[-1]
1428                     amount = price_unit
1429
1430             parent_tax = self._unit_compute_inv(cr, uid, tax.child_ids, amount, address_id, product, partner)
1431             res.extend(parent_tax)
1432
1433         total = 0.0
1434         for r in res:
1435             if r['todo']:
1436                 total += r['amount']
1437         for r in res:
1438             r['price_unit'] -= total
1439             r['todo'] = 0
1440         return res
1441
1442     def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1443         """
1444         Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1445         Price Unit is a VAT included price
1446
1447         RETURN:
1448             [ tax ]
1449             tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1450             one tax for each tax id in IDS and their childs
1451         """
1452         res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
1453         for r in res:
1454             r['amount'] *= quantity
1455         return res
1456 account_tax()
1457
1458 # ---------------------------------------------------------
1459 # Account Entries Models
1460 # ---------------------------------------------------------
1461
1462 class account_model(osv.osv):
1463     _name = "account.model"
1464     _description = "Account Model"
1465     _columns = {
1466         'name': fields.char('Model Name', size=64, required=True, help="This is a model for recurring accounting entries"),
1467         'ref': fields.char('Ref', size=64),
1468         'journal_id': fields.many2one('account.journal', 'Journal', required=True),
1469         'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'),
1470         'legend' :fields.text('Legend',readonly=True,size=100),
1471     }
1472
1473     _defaults = {
1474         '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'),
1475
1476     }
1477
1478 account_model()
1479
1480 class account_model_line(osv.osv):
1481     _name = "account.model.line"
1482     _description = "Account Model Entries"
1483     _columns = {
1484         'name': fields.char('Name', size=64, required=True),
1485         'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the resources from the lowest sequences to the higher ones"),
1486         'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity on entries"),
1487         'debit': fields.float('Debit', digits=(16,2)),
1488         'credit': fields.float('Credit', digits=(16,2)),
1489
1490         'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"),
1491
1492         'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True),
1493
1494         'ref': fields.char('Ref.', size=16),
1495
1496         'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."),
1497         'currency_id': fields.many2one('res.currency', 'Currency'),
1498
1499         'partner_id': fields.many2one('res.partner', 'Partner Ref.'),
1500         '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."),
1501         'date': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Current Date', required=True, help="The date of the generated entries"),
1502     }
1503     _defaults = {
1504         'date': lambda *a: 'today'
1505     }
1506     _order = 'sequence'
1507     _sql_constraints = [
1508         ('credit_debit1', 'CHECK (credit*debit=0)',  'Wrong credit or debit value in model !'),
1509         ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model !'),
1510     ]
1511 account_model_line()
1512
1513 # ---------------------------------------------------------
1514 # Account Subscription
1515 # ---------------------------------------------------------
1516
1517
1518 class account_subscription(osv.osv):
1519     _name = "account.subscription"
1520     _description = "Account Subscription"
1521     _columns = {
1522         'name': fields.char('Name', size=64, required=True),
1523         'ref': fields.char('Ref.', size=16),
1524         'model_id': fields.many2one('account.model', 'Model', required=True),
1525
1526         'date_start': fields.date('Starting date', required=True),
1527         'period_total': fields.integer('Number of period', required=True),
1528         'period_nbr': fields.integer('Period', required=True),
1529         'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True),
1530         'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status', required=True, readonly=True),
1531
1532         'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines')
1533     }
1534     _defaults = {
1535         'date_start': lambda *a: time.strftime('%Y-%m-%d'),
1536         'period_type': lambda *a: 'month',
1537         'period_total': lambda *a: 12,
1538         'period_nbr': lambda *a: 1,
1539         'state': lambda *a: 'draft',
1540     }
1541     def state_draft(self, cr, uid, ids, context={}):
1542         self.write(cr, uid, ids, {'state':'draft'})
1543         return False
1544
1545     def check(self, cr, uid, ids, context={}):
1546         todone = []
1547         for sub in self.browse(cr, uid, ids, context):
1548             ok = True
1549             for line in sub.lines_id:
1550                 if not line.move_id.id:
1551                     ok = False
1552                     break
1553             if ok:
1554                 todone.append(sub.id)
1555         if len(todone):
1556             self.write(cr, uid, todone, {'state':'done'})
1557         return False
1558
1559     def remove_line(self, cr, uid, ids, context={}):
1560         toremove = []
1561         for sub in self.browse(cr, uid, ids, context):
1562             for line in sub.lines_id:
1563                 if not line.move_id.id:
1564                     toremove.append(line.id)
1565         if len(toremove):
1566             self.pool.get('account.subscription.line').unlink(cr, uid, toremove)
1567         self.write(cr, uid, ids, {'state':'draft'})
1568         return False
1569
1570     def compute(self, cr, uid, ids, context={}):
1571         for sub in self.browse(cr, uid, ids, context):
1572             ds = sub.date_start
1573             for i in range(sub.period_total):
1574                 self.pool.get('account.subscription.line').create(cr, uid, {
1575                     'date': ds,
1576                     'subscription_id': sub.id,
1577                 })
1578                 if sub.period_type=='day':
1579                     ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(days=sub.period_nbr)).strftime('%Y-%m-%d')
1580                 if sub.period_type=='month':
1581                     ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(months=sub.period_nbr)).strftime('%Y-%m-%d')
1582                 if sub.period_type=='year':
1583                     ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(years=sub.period_nbr)).strftime('%Y-%m-%d')
1584         self.write(cr, uid, ids, {'state':'running'})
1585         return True
1586 account_subscription()
1587
1588 class account_subscription_line(osv.osv):
1589     _name = "account.subscription.line"
1590     _description = "Account Subscription Line"
1591     _columns = {
1592         'subscription_id': fields.many2one('account.subscription', 'Subscription', required=True, select=True),
1593         'date': fields.date('Date', required=True),
1594         'move_id': fields.many2one('account.move', 'Entry'),
1595     }
1596     _defaults = {
1597     }
1598     def move_create(self, cr, uid, ids, context={}):
1599         tocheck = {}
1600         for line in self.browse(cr, uid, ids, context):
1601             datas = {
1602                 'date': line.date,
1603             }
1604             ids = self.pool.get('account.model').generate(cr, uid, [line.subscription_id.model_id.id], datas, context)
1605             tocheck[line.subscription_id.id] = True
1606             self.write(cr, uid, [line.id], {'move_id':ids[0]})
1607         if tocheck:
1608             self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context)
1609         return True
1610     _rec_name = 'date'
1611 account_subscription_line()
1612
1613
1614 class account_config_wizard(osv.osv_memory):
1615     _name = 'account.config.wizard'
1616
1617     def _get_charts(self, cr, uid, context):
1618         module_obj=self.pool.get('ir.module.module')
1619         ids=module_obj.search(cr, uid, [('category_id', '=', 'Account Charts'), ('state', '<>', 'installed')])
1620         res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)]
1621         res.append((-1, 'None'))
1622         res.sort(key=lambda x: x[1])
1623         return res
1624
1625     _columns = {
1626         'name':fields.char('Name', required=True, size=64, help="Name of the fiscal year as displayed on screens."),
1627         'code':fields.char('Code', required=True, size=64, help="Name of the fiscal year as displayed in reports."),
1628         'date1': fields.date('Starting Date', required=True),
1629         'date2': fields.date('Ending Date', required=True),
1630         'period':fields.selection([('month','Month'),('3months','3 Months')], 'Periods', required=True),
1631         'charts' : fields.selection(_get_charts, 'Charts of Account',required=True)
1632     }
1633     _defaults = {
1634         'code': lambda *a: time.strftime('%Y'),
1635         'name': lambda *a: time.strftime('%Y'),
1636         'date1': lambda *a: time.strftime('%Y-01-01'),
1637         'date2': lambda *a: time.strftime('%Y-12-31'),
1638         'period':lambda *a:'month',
1639     }
1640     def action_cancel(self,cr,uid,ids,conect=None):
1641         return {
1642                 'view_type': 'form',
1643                 "view_mode": 'form',
1644                 'res_model': 'ir.actions.configuration.wizard',
1645                 'type': 'ir.actions.act_window',
1646                 'target':'new',
1647         }
1648
1649     def install_account_chart(self, cr, uid, ids, context=None):
1650         for res in self.read(cr,uid,ids):
1651             chart_id = res['charts']
1652             if chart_id > 0:
1653                 mod_obj = self.pool.get('ir.module.module')
1654                 mod_obj.button_install(cr, uid, [chart_id], context=context)
1655         cr.commit()
1656         db, pool = pooler.restart_pool(cr.dbname, update_module=True)
1657
1658     def action_create(self, cr, uid,ids, context=None):
1659         for res in self.read(cr,uid,ids):
1660             if 'date1' in res and 'date2' in res:
1661                 res_obj = self.pool.get('account.fiscalyear')
1662                 start_date=res['date1']
1663                 end_date=res['date2']
1664                 name=res['name']#DateTime.strptime(start_date, '%Y-%m-%d').strftime('%m.%Y') + '-' + DateTime.strptime(end_date, '%Y-%m-%d').strftime('%m.%Y')
1665                 vals={
1666                     'name':name,
1667                     'code':name,
1668                     'date_start':start_date,
1669                     'date_stop':end_date,
1670                 }
1671                 new_id=res_obj.create(cr, uid, vals, context=context)
1672                 if res['period']=='month':
1673                     res_obj.create_period(cr,uid,[new_id])
1674                 elif res['period']=='3months':
1675                     res_obj.create_period3(cr,uid,[new_id])
1676         self.install_account_chart(cr,uid,ids)
1677         return {
1678                 'view_type': 'form',
1679                 "view_mode": 'form',
1680                 'res_model': 'ir.actions.configuration.wizard',
1681                 'type': 'ir.actions.act_window',
1682                 'target':'new',
1683         }
1684
1685
1686
1687 account_config_wizard()
1688
1689
1690 #  ---------------------------------------------------------------
1691 #   Account Templates : Account, Tax, Tax Code and chart. + Wizard
1692 #  ---------------------------------------------------------------
1693
1694 class account_tax_template(osv.osv):
1695     _name = 'account.tax.template'
1696 account_tax_template()
1697
1698 class account_account_template(osv.osv):
1699     _order = "code"
1700     _name = "account.account.template"
1701     _description ='Templates for Accounts'
1702
1703     _columns = {
1704         'name': fields.char('Name', size=128, required=True, select=True),
1705         'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
1706         'code': fields.char('Code', size=64),
1707         'type': fields.selection([
1708             ('receivable','Receivable'),
1709             ('payable','Payable'),
1710             ('view','View'),
1711             ('consolidation','Consolidation'),
1712             ('other','Others'),
1713             ('closed','Closed'),
1714             ], 'Internal Type', required=True,),
1715         'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
1716         'reconcile': fields.boolean('Allow Reconciliation', help="Check this option if the user can make a reconciliation of the entries in this account."),
1717         'shortcut': fields.char('Shortcut', size=12),
1718         'note': fields.text('Note'),
1719         'parent_id': fields.many2one('account.account.template','Parent Account Template', ondelete='cascade'),
1720         'child_parent_ids':fields.one2many('account.account.template','parent_id','Children'),
1721         'tax_ids': fields.many2many('account.tax.template', 'account_account_template_tax_rel','account_id','tax_id', 'Default Taxes'),
1722     }
1723
1724     _defaults = {
1725         'reconcile': lambda *a: False,
1726         'type' : lambda *a :'view',
1727     }
1728
1729     def _check_recursion(self, cr, uid, ids):
1730         level = 100
1731         while len(ids):
1732             cr.execute('select parent_id from account_account_template where id in ('+','.join(map(str,ids))+')')
1733             ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1734             if not level:
1735                 return False
1736             level -= 1
1737         return True
1738
1739     _constraints = [
1740         (_check_recursion, 'Error ! You can not create recursive account templates.', ['parent_id'])
1741     ]
1742
1743
1744     def name_get(self, cr, uid, ids, context={}):
1745         if not len(ids):
1746             return []
1747         reads = self.read(cr, uid, ids, ['name','code'], context)
1748         res = []
1749         for record in reads:
1750             name = record['name']
1751             if record['code']:
1752                 name = record['code']+' '+name
1753             res.append((record['id'],name ))
1754         return res
1755
1756 account_account_template()
1757
1758 class account_tax_code_template(osv.osv):
1759
1760     _name = 'account.tax.code.template'
1761     _description = 'Tax Code Template'
1762     _order = 'code'
1763     _rec_name = 'code'
1764     _columns = {
1765         'name': fields.char('Tax Case Name', size=64, required=True),
1766         'code': fields.char('Case Code', size=64),
1767         'info': fields.text('Description'),
1768         'parent_id': fields.many2one('account.tax.code.template', 'Parent Code', select=True),
1769         'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Childs Codes'),
1770         'sign': fields.float('Sign for parent', required=True),
1771     }
1772
1773     _defaults = {
1774         'sign': lambda *args: 1.0,
1775     }
1776
1777     def name_get(self, cr, uid, ids, context=None):
1778         if not len(ids):
1779             return []
1780         if isinstance(ids, (int, long)):
1781             ids = [ids]
1782         reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1783         return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1784                 for x in reads]
1785
1786     def _check_recursion(self, cr, uid, ids):
1787         level = 100
1788         while len(ids):
1789             cr.execute('select distinct parent_id from account_tax_code_template where id in ('+','.join(map(str,ids))+')')
1790             ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1791             if not level:
1792                 return False
1793             level -= 1
1794         return True
1795
1796     _constraints = [
1797         (_check_recursion, 'Error ! You can not create recursive Tax Codes.', ['parent_id'])
1798     ]
1799     _order = 'code,name'
1800 account_tax_code_template()
1801
1802
1803 class account_chart_template(osv.osv):
1804     _name="account.chart.template"
1805     _description= "Templates for Account Chart"
1806
1807     _columns={
1808         'name': fields.char('Name', size=64, required=True),
1809         'account_root_id': fields.many2one('account.account.template','Root Account',required=True,domain=[('parent_id','=',False)]),
1810         'tax_code_root_id': fields.many2one('account.tax.code.template','Root Tax Code',required=True,domain=[('parent_id','=',False)]),
1811         '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'),
1812         'bank_account_view_id': fields.many2one('account.account.template','Bank Account',required=True),
1813         'property_account_receivable': fields.many2one('account.account.template','Receivable Account'),
1814         'property_account_payable': fields.many2one('account.account.template','Payable Account'),
1815         'property_account_expense_categ': fields.many2one('account.account.template','Expense Category Account'),
1816         'property_account_income_categ': fields.many2one('account.account.template','Income Category Account'),
1817         'property_account_expense': fields.many2one('account.account.template','Expense Account on Product Template'),
1818         'property_account_income': fields.many2one('account.account.template','Income Account on Product Template'),
1819     }
1820
1821 account_chart_template()
1822
1823 class account_tax_template(osv.osv):
1824
1825     _name = 'account.tax.template'
1826     _description = 'Templates for Taxes'
1827
1828     _columns = {
1829         'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
1830         'name': fields.char('Tax Name', size=64, required=True),
1831         '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."),
1832         'amount': fields.float('Amount', required=True, digits=(14,4)),
1833         'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1834         'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1835         '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."),
1836         'account_collected_id':fields.many2one('account.account.template', 'Invoice Tax Account'),
1837         'account_paid_id':fields.many2one('account.account.template', 'Refund Tax Account'),
1838         'parent_id':fields.many2one('account.tax.template', 'Parent Tax Account', select=True),
1839         '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."),
1840         'python_compute':fields.text('Python Code'),
1841         'python_compute_inv':fields.text('Python Code (reverse)'),
1842         'python_applicable':fields.text('Python Code'),
1843         '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."),
1844
1845         #
1846         # Fields used for the VAT declaration
1847         #
1848         'base_code_id': fields.many2one('account.tax.code.template', 'Base Code', help="Use this code for the VAT declaration."),
1849         'tax_code_id': fields.many2one('account.tax.code.template', 'Tax Code', help="Use this code for the VAT declaration."),
1850         'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1851         'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1852
1853         # Same fields for refund invoices
1854
1855         'ref_base_code_id': fields.many2one('account.tax.code.template', 'Refund Base Code', help="Use this code for the VAT declaration."),
1856         'ref_tax_code_id': fields.many2one('account.tax.code.template', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1857         'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1858         'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1859         '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."),
1860         'description': fields.char('Internal Name', size=32),
1861     }
1862
1863     def name_get(self, cr, uid, ids, context={}):
1864         if not len(ids):
1865             return []
1866         res = []
1867         for record in self.read(cr, uid, ids, ['description','name'], context):
1868             name = record['description'] and record['description'] or record['name']
1869             res.append((record['id'],name ))
1870         return res
1871
1872     def _default_company(self, cr, uid, context={}):
1873         user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1874         if user.company_id:
1875             return user.company_id.id
1876         return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1877
1878     _defaults = {
1879         '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''',
1880         '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''',
1881         'applicable_type': lambda *a: 'true',
1882         'type': lambda *a: 'percent',
1883         'amount': lambda *a: 0,
1884         'sequence': lambda *a: 1,
1885         'tax_group': lambda *a: 'vat',
1886         'ref_tax_sign': lambda *a: 1,
1887         'ref_base_sign': lambda *a: 1,
1888         'tax_sign': lambda *a: 1,
1889         'base_sign': lambda *a: 1,
1890         'include_base_amount': lambda *a: False,
1891     }
1892     _order = 'sequence'
1893
1894
1895 account_tax_template()
1896
1897 # Fiscal Position Templates
1898
1899 class account_fiscal_position_template(osv.osv):
1900     _name = 'account.fiscal.position.template'
1901     _description = 'Template for Fiscal Position'
1902
1903     _columns = {
1904         'name': fields.char('Fiscal Position Template', size=64, translate=True, required=True),
1905         'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
1906         'account_ids': fields.one2many('account.fiscal.position.account.template', 'position_id', 'Accounts Mapping'),
1907         'tax_ids': fields.one2many('account.fiscal.position.tax.template', 'position_id', 'Taxes Mapping')
1908     }
1909
1910 account_fiscal_position_template()
1911
1912 class account_fiscal_position_tax_template(osv.osv):
1913     _name = 'account.fiscal.position.tax.template'
1914     _description = 'Fiscal Position Template Taxes Mapping'
1915     _rec_name = 'position_id'
1916
1917     _columns = {
1918         'position_id': fields.many2one('account.fiscal.position.template', 'Fiscal Position', required=True, ondelete='cascade'),
1919         'tax_src_id': fields.many2one('account.tax.template', 'Tax Source', required=True),
1920         'tax_dest_id': fields.many2one('account.tax.template', 'Replacement Tax')
1921     }
1922
1923 account_fiscal_position_tax_template()
1924
1925 class account_fiscal_position_account_template(osv.osv):
1926     _name = 'account.fiscal.position.account.template'
1927     _description = 'Fiscal Position Template Accounts Mapping'
1928     _rec_name = 'position_id'
1929     _columns = {
1930         'position_id': fields.many2one('account.fiscal.position.template', 'Fiscal Position', required=True, ondelete='cascade'),
1931         'account_src_id': fields.many2one('account.account.template', 'Account Source', domain=[('type','<>','view')], required=True),
1932         'account_dest_id': fields.many2one('account.account.template', 'Account Destination', domain=[('type','<>','view')], required=True)
1933     }
1934
1935 account_fiscal_position_account_template()
1936
1937     # Multi charts of Accounts wizard
1938
1939 class wizard_multi_charts_accounts(osv.osv_memory):
1940     """
1941     Create a new account chart for a company.
1942     Wizards ask:
1943         * a company
1944         * an account chart template
1945         * a number of digits for formatting code of non-view accounts
1946         * a list of bank account owned by the company
1947     Then, the wizard:
1948         * generates all accounts from the template and assign them to the right company
1949         * generates all taxes and tax codes, changing account assignations
1950         * generates all accounting properties and assign correctly
1951     """
1952     _name='wizard.multi.charts.accounts'
1953
1954     _columns = {
1955         'company_id':fields.many2one('res.company','Company',required=True),
1956         'chart_template_id': fields.many2one('account.chart.template','Chart Template',required=True),
1957         'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Bank Accounts',required=True),
1958         'code_digits':fields.integer('# of Digits',required=True,help="No. of Digits to use for account code"),
1959     }
1960
1961     def _get_chart(self, cr, uid, context={}):
1962         ids = self.pool.get('account.chart.template').search(cr, uid, [], context=context)
1963         if ids:
1964             return ids[0]
1965         return False
1966     _defaults = {
1967         'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr,uid,[uid],c)[0].company_id.id,
1968         'chart_template_id': _get_chart,
1969         'code_digits': lambda *a:6,
1970     }
1971
1972     def action_create(self, cr, uid, ids, context=None):
1973         obj_multi = self.browse(cr,uid,ids[0])
1974         obj_acc = self.pool.get('account.account')
1975         obj_acc_tax = self.pool.get('account.tax')
1976         obj_journal = self.pool.get('account.journal')
1977         obj_acc_template = self.pool.get('account.account.template')
1978         obj_fiscal_position_template = self.pool.get('account.fiscal.position.template')
1979         obj_fiscal_position = self.pool.get('account.fiscal.position')
1980
1981         # Creating Account
1982         obj_acc_root = obj_multi.chart_template_id.account_root_id
1983         tax_code_root_id = obj_multi.chart_template_id.tax_code_root_id.id
1984         company_id = obj_multi.company_id.id
1985
1986         #new code
1987         acc_template_ref = {}
1988         tax_template_ref = {}
1989         tax_code_template_ref = {}
1990         todo_dict = {}
1991
1992         #create all the tax code
1993         children_tax_code_template = self.pool.get('account.tax.code.template').search(cr, uid, [('parent_id','child_of',[tax_code_root_id])], order='id')
1994         for tax_code_template in self.pool.get('account.tax.code.template').browse(cr, uid, children_tax_code_template):
1995             vals={
1996                 'name': (tax_code_root_id == tax_code_template.id) and obj_multi.company_id.name or tax_code_template.name,
1997                 'code': tax_code_template.code,
1998                 'info': tax_code_template.info,
1999                 '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,
2000                 'company_id': company_id,
2001                 'sign': tax_code_template.sign,
2002             }
2003             new_tax_code = self.pool.get('account.tax.code').create(cr,uid,vals)
2004             #recording the new tax code to do the mapping
2005             tax_code_template_ref[tax_code_template.id] = new_tax_code
2006
2007         #create all the tax
2008         for tax in obj_multi.chart_template_id.tax_template_ids:
2009             #create it
2010             vals_tax = {
2011                 'name':tax.name,
2012                 'sequence': tax.sequence,
2013                 'amount':tax.amount,
2014                 'type':tax.type,
2015                 'applicable_type': tax.applicable_type,
2016                 'domain':tax.domain,
2017                 'parent_id': tax.parent_id and ((tax.parent_id.id in tax_template_ref) and tax_template_ref[tax.parent_id.id]) or False,
2018                 'child_depend': tax.child_depend,
2019                 'python_compute': tax.python_compute,
2020                 'python_compute_inv': tax.python_compute_inv,
2021                 'python_applicable': tax.python_applicable,
2022                 'tax_group':tax.tax_group,
2023                 '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,
2024                 '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,
2025                 'base_sign': tax.base_sign,
2026                 'tax_sign': tax.tax_sign,
2027                 '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,
2028                 '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,
2029                 'ref_base_sign': tax.ref_base_sign,
2030                 'ref_tax_sign': tax.ref_tax_sign,
2031                 'include_base_amount': tax.include_base_amount,
2032                 'description':tax.description,
2033                 'company_id': company_id,
2034             }
2035             new_tax = obj_acc_tax.create(cr,uid,vals_tax)
2036             #as the accounts have not been created yet, we have to wait before filling these fields
2037             todo_dict[new_tax] = {
2038                 'account_collected_id': tax.account_collected_id and tax.account_collected_id.id or False,
2039                 'account_paid_id': tax.account_paid_id and tax.account_paid_id.id or False,
2040             }
2041             tax_template_ref[tax.id] = new_tax
2042
2043         #deactivate the parent_store functionnality on account_account for rapidity purpose
2044         self.pool._init = True
2045
2046         children_acc_template = obj_acc_template.search(cr, uid, [('parent_id','child_of',[obj_acc_root.id])])
2047         children_acc_template.sort()
2048         for account_template in obj_acc_template.browse(cr, uid, children_acc_template):
2049             tax_ids = []
2050             for tax in account_template.tax_ids:
2051                 tax_ids.append(tax_template_ref[tax.id])
2052             #create the account_account
2053
2054             dig = obj_multi.code_digits
2055             code_main = account_template.code and len(account_template.code) or 0
2056             code_acc = account_template.code or ''
2057             if code_main>0 and code_main<=dig and account_template.type != 'view':
2058                 code_acc=str(code_acc) + (str('0'*(dig-code_main)))
2059             vals={
2060                 'name': (obj_acc_root.id == account_template.id) and obj_multi.company_id.name or account_template.name,
2061                 #'sign': account_template.sign,
2062                 'currency_id': account_template.currency_id and account_template.currency_id.id or False,
2063                 'code': code_acc,
2064                 'type': account_template.type,
2065                 'user_type': account_template.user_type and account_template.user_type.id or False,
2066                 'reconcile': account_template.reconcile,
2067                 'shortcut': account_template.shortcut,
2068                 'note': account_template.note,
2069                 '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,
2070                 'tax_ids': [(6,0,tax_ids)],
2071                 'company_id': company_id,
2072             }
2073             new_account = obj_acc.create(cr,uid,vals)
2074             acc_template_ref[account_template.id] = new_account
2075         #reactivate the parent_store functionnality on account_account
2076         self.pool._init = False
2077         self.pool.get('account.account')._parent_store_compute(cr)
2078
2079         for key,value in todo_dict.items():
2080             if value['account_collected_id'] or value['account_paid_id']:
2081                 obj_acc_tax.write(cr, uid, [key], vals={
2082                     'account_collected_id': acc_template_ref[value['account_collected_id']],
2083                     'account_paid_id': acc_template_ref[value['account_paid_id']],
2084                 })
2085
2086         # Creating Journals
2087         vals_journal={}
2088         view_id = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Journal View')])[0]
2089         seq_id = self.pool.get('ir.sequence').search(cr,uid,[('code','=','account.journal')])[0]
2090         seq_code = self.pool.get('ir.sequence').get(cr, uid, 'account.journal')
2091
2092         vals_journal['view_id']=view_id
2093         vals_journal['sequence_id']=seq_id
2094
2095         #Sales Journal
2096         vals_journal['name'] = _('Sales Journal')
2097         vals_journal['type'] = 'sale'
2098         vals_journal['code'] = _('SAJ')
2099
2100         if obj_multi.chart_template_id.property_account_receivable:
2101             vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_income_categ.id]
2102             vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_income_categ.id]
2103
2104         obj_journal.create(cr,uid,vals_journal)
2105
2106         # Purchase Journal
2107         vals_journal['name']=_('Purchase Journal')
2108         vals_journal['type']='purchase'
2109         vals_journal['code']=_('EXJ')
2110
2111         if obj_multi.chart_template_id.property_account_payable:
2112             vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_expense_categ.id]
2113             vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_expense_categ.id]
2114
2115         obj_journal.create(cr,uid,vals_journal)
2116
2117         # Bank Journals
2118         view_id_cash = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Cash Journal View')])[0]
2119         view_id_cur = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Multi-Currency Cash Journal View')])[0]
2120         ref_acc_bank = obj_multi.chart_template_id.bank_account_view_id
2121
2122         current_num = 1
2123         for line in obj_multi.bank_accounts_id:
2124             #create the account_account for this bank journal
2125             tmp = self.pool.get('res.partner.bank').name_get(cr, uid, [line.acc_no.id])[0][1]
2126             dig = obj_multi.code_digits
2127             vals={
2128                 'name': line.acc_no.bank and line.acc_no.bank.name+' '+tmp or tmp,
2129                 'currency_id': line.currency_id and line.currency_id.id or False,
2130                 'code': str(ref_acc_bank.code.ljust(dig,'0') + str(current_num)),
2131                 'type': 'other',
2132                 'user_type': account_template.user_type and account_template.user_type.id or False,
2133                 'reconcile': True,
2134                 'parent_id': acc_template_ref[ref_acc_bank.id] or False,
2135                 'company_id': company_id,
2136             }
2137             acc_cash_id  = obj_acc.create(cr,uid,vals)
2138
2139             #create the bank journal
2140             vals_journal['name']= vals['name']
2141             vals_journal['code']= _('BNK') + str(current_num)
2142             vals_journal['sequence_id'] = seq_id
2143             vals_journal['type'] = 'cash'
2144             if line.currency_id:
2145                 vals_journal['view_id'] = view_id_cur
2146                 vals_journal['currency'] = line.currency_id.id
2147             else:
2148                 vals_journal['view_id'] = view_id_cash
2149             vals_journal['default_credit_account_id'] = acc_cash_id
2150             vals_journal['default_debit_account_id']= acc_cash_id
2151             obj_journal.create(cr,uid,vals_journal)
2152
2153             current_num += 1
2154
2155         #create the properties
2156         property_obj = self.pool.get('ir.property')
2157         fields_obj = self.pool.get('ir.model.fields')
2158
2159         todo_list = [
2160             ('property_account_receivable','res.partner','account.account'),
2161             ('property_account_payable','res.partner','account.account'),
2162             ('property_account_expense_categ','product.category','account.account'),
2163             ('property_account_income_categ','product.category','account.account'),
2164             ('property_account_expense','product.template','account.account'),
2165             ('property_account_income','product.template','account.account')
2166         ]
2167         for record in todo_list:
2168             r = []
2169             r = property_obj.search(cr, uid, [('name','=', record[0] ),('company_id','=',company_id)])
2170             account = getattr(obj_multi.chart_template_id, record[0])
2171             field = fields_obj.search(cr, uid, [('name','=',record[0]),('model','=',record[1]),('relation','=',record[2])])
2172             vals = {
2173                 'name': record[0],
2174                 'company_id': company_id,
2175                 'fields_id': field[0],
2176                 'value': account and 'account.account,'+str(acc_template_ref[account.id]) or False,
2177             }
2178             if r:
2179                 #the property exist: modify it
2180                 property_obj.write(cr, uid, r, vals)
2181             else:
2182                 #create the property
2183                 property_obj.create(cr, uid, vals)
2184
2185         fp_ids = obj_fiscal_position_template.search(cr, uid,[('chart_template_id', '=', obj_multi.chart_template_id.id)])
2186
2187         if fp_ids:
2188             for position in obj_fiscal_position_template.browse(cr, uid, fp_ids):
2189
2190                 vals_fp = {
2191                            'company_id' : company_id,
2192                            'name' : position.name,
2193                            }
2194                 new_fp = obj_fiscal_position.create(cr, uid, vals_fp)
2195
2196                 obj_tax_fp = self.pool.get('account.fiscal.position.tax')
2197                 obj_ac_fp = self.pool.get('account.fiscal.position.account')
2198
2199                 for tax in position.tax_ids:
2200                     vals_tax = {
2201                                 'tax_src_id' : tax_template_ref[tax.tax_src_id.id],
2202                                 'tax_dest_id' : tax_template_ref[tax.tax_dest_id.id],
2203                                 'position_id' : new_fp,
2204                                 }
2205                     obj_tax_fp.create(cr, uid, vals_tax)
2206
2207                 for acc in position.account_ids:
2208                     vals_acc = {
2209                                 'account_src_id' : acc_template_ref[acc.account_src_id.id],
2210                                 'account_dest_id' : acc_template_ref[acc.account_dest_id.id],
2211                                 'position_id' : new_fp,
2212                                 }
2213                     obj_ac_fp.create(cr, uid, vals_acc)
2214
2215         return {
2216                 'view_type': 'form',
2217                 "view_mode": 'form',
2218                 'res_model': 'ir.actions.configuration.wizard',
2219                 'type': 'ir.actions.act_window',
2220                 'target':'new',
2221         }
2222     def action_cancel(self,cr,uid,ids,conect=None):
2223         return {
2224                 'view_type': 'form',
2225                 "view_mode": 'form',
2226                 'res_model': 'ir.actions.configuration.wizard',
2227                 'type': 'ir.actions.act_window',
2228                 'target':'new',
2229         }
2230
2231
2232 wizard_multi_charts_accounts()
2233
2234 class account_bank_accounts_wizard(osv.osv_memory):
2235     _name='account.bank.accounts.wizard'
2236
2237     _columns = {
2238         'acc_no':fields.many2one('res.partner.bank','Account No.',required=True),
2239         'bank_account_id':fields.many2one('wizard.multi.charts.accounts', 'Bank Account', required=True),
2240         'currency_id':fields.many2one('res.currency', 'Currency'),
2241     }
2242
2243 account_bank_accounts_wizard()
2244
2245 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
2246