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