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