1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
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
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.
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.
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.
29 ##############################################################################
32 from osv import fields, osv
34 from tools.misc import currency
35 from tools.translate import _
38 from mx.DateTime import RelativeDateTime, now, DateTime, localtime
41 class account_payment_term(osv.osv):
42 _name = "account.payment.term"
43 _description = "Payment Term"
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'),
51 'active': lambda *a: 1,
54 def compute(self, cr, uid, id, value, date_ref=False, context={}):
56 date_ref = now().strftime('%Y-%m-%d')
57 pt = self.browse(cr, uid, id, context)
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)
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) )
75 account_payment_term()
77 class account_payment_term_line(osv.osv):
78 _name = "account.payment.term.line"
79 _description = "Payment Term Line"
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),
90 'value': lambda *a: 'balance',
91 'sequence': lambda *a: 5,
92 'condition': lambda *a: 'net days',
95 account_payment_term_line()
98 class account_account_type(osv.osv):
99 _name = "account.account.type"
100 _description = "Account Type"
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.'),
110 'close_method': lambda *a: 'none',
111 'sequence': lambda *a: 5,
112 'sign': lambda *a: 1,
115 account_account_type()
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]
123 #----------------------------------------------------------
125 #----------------------------------------------------------
127 class account_tax(osv.osv):
128 _name = 'account.tax'
131 class account_account(osv.osv):
132 _order = "parent_left"
133 _parent_order = "code"
134 _name = "account.account"
135 _description = "Account"
138 def search(self, cr, uid, args, offset=0, limit=None, order=None,
139 context=None, count=False):
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':
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]:
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)
159 return super(account_account,self).search(cr, uid, args, offset, limit,
160 order, context=context, count=count)
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)')
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)')
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)')
171 def __compute(self, cr, uid, ids, field_names, arg, context={}, query=''):
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 "
177 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
178 acc_set = ",".join(map(str, ids2))
181 query = self.pool.get('account.move.line')._query_get(cr, uid,
183 cr.execute(("SELECT l.account_id, " +\
184 ' , '.join(map(lambda x: mapping[x], field_names)) +
186 "account_move_line l " \
188 "l.account_id IN (%s) " \
189 "AND " + query + " " \
190 "GROUP BY l.account_id") % (acc_set, ))
192 for res in cr.fetchall():
193 accounts[res[0]] = res[1:]
197 res[id] = map(lambda x: 0.0, field_names)
198 ids2 = self.search(cr, uid, [('parent_id', 'child_of', [id])])
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
207 def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}):
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)
213 def _get_child_ids(self, cr, uid, ids, field_name, arg, context={}):
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]
221 if record.child_consol_ids:
222 for acc in record.child_consol_ids:
223 result[record.id].append(acc.id)
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'),
235 ('consolidation','Consolidation'),
237 ('expense','Expense'),
241 #('equity','Equity'),
243 ], 'Internal Type', required=True,),
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"),
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),
265 'parent_left': fields.integer('Parent Left', select=1),
266 'parent_right': fields.integer('Parent Right', select=1),
269 def _default_company(self, cr, uid, context={}):
270 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
272 return user.company_id.id
273 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
276 'type' : lambda *a :'view',
277 'reconcile': lambda *a: False,
278 'company_id': _default_company,
279 'active': lambda *a: True,
282 # def _check_recursion(self, cr, uid, 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()))
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
296 if (obj_self in obj_self.child_consol_ids) or (p_id and (p_id is obj_self.id)):
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()))
303 if (p_id and (p_id in c_ids)) or (obj_self.id in c_ids):
306 s_ids=self.search(cr,uid,[('parent_id','in',c_ids)])
307 if p_id and (p_id in s_ids):
315 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
317 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
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))]
330 if name and str(name).startswith('type:'):
331 type = name.split(':')[1]
332 args += [('type','=', type)]
337 ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit)
339 ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit)
341 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
343 ids = self.search(cr, user, args, context=context, limit=limit)
344 return self.name_get(cr, user, ids, context=context)
346 def name_get(self, cr, uid, ids, context={}):
349 reads = self.read(cr, uid, ids, ['name','code'], context)
352 name = record['name']
354 name = record['code']+' '+name
355 res.append((record['id'],name ))
358 def copy(self, cr, uid, id, default={}, context={},done_list=[]):
359 account = self.browse(cr, uid, id, context=context)
363 default=default.copy()
364 default['parent_id'] = False
365 if account.id in done_list:
367 done_list.append(account.id)
369 for child in account.child_id:
370 child_ids=self.copy(cr, uid, child.id, default, context=context,done_list=done_list)
372 new_child_ids.append(child_ids)
373 default['child_parent_ids'] = [(6, 0, new_child_ids)]
375 default['child_parent_ids'] = False
376 return super(account_account, self).copy(cr, uid, id, default, context=context)
378 def write(self, cr, uid, ids, vals, context=None):
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)
389 class account_journal_view(osv.osv):
390 _name = "account.journal.view"
391 _description = "Journal View"
393 'name': fields.char('Journal View', size=64, required=True),
394 'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
397 account_journal_view()
400 class account_journal_column(osv.osv):
401 def _col_get(self, cr, user, context={}):
403 cols = self.pool.get('account.move.line')._columns
405 result.append( (col, cols[col].string) )
408 _name = "account.journal.column"
409 _description = "Journal Column"
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'),
419 account_journal_column()
421 class account_journal(osv.osv):
422 _name = "account.journal"
423 _description = "Journal"
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),
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')]),
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.'),
445 'active': lambda *a: 1,
446 'user_id': lambda self,cr,uid,context: uid,
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
460 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
467 ids = self.search(cr, user, [('code','ilike',name)]+ args, limit=limit)
469 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
470 return self.name_get(cr, user, ids, context=context)
473 class account_fiscalyear(osv.osv):
474 _name = "account.fiscalyear"
475 _description = "Fiscal Year"
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),
486 'state': lambda *a: 'draft',
488 _order = "date_start"
489 def create_period3(self,cr, uid, ids, context={}):
490 return self.create_period(cr, uid, ids, context, 3)
492 def create_period(self,cr, uid, ids, context={}, interval=1):
493 for fy in self.browse(cr, uid, ids, context):
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,
505 ds = ds + RelativeDateTime(months=interval)
508 def find(self, cr, uid, dt=None, exception=True, context={}):
510 dt = time.strftime('%Y-%m-%d')
511 ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)])
514 raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.'))
520 class account_period(osv.osv):
521 _name = "account.period"
522 _description = "Account period"
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)
532 'state': lambda *a: 'draft',
534 _order = "date_start"
535 def next(self, cr, uid, period, step, context={}):
536 ids = self.search(cr, uid, [('date_start','>',period.date_start)])
541 def find(self, cr, uid, dt=None, context={}):
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)])
547 raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.'))
551 class account_journal_period(osv.osv):
552 _name = "account.journal.period"
553 _description = "Journal - Period"
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']):
559 'draft': 'STOCK_NEW',
560 'printed': 'STOCK_PRINT_PREVIEW',
561 'done': 'STOCK_DIALOG_AUTHENTICATION',
562 }.get(r['state'], 'STOCK_NEW')
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)
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))
579 raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !'))
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)
586 def create(self, cr, uid, vals, context={}):
587 period_id=vals.get('period_id',False)
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)
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)
598 'state': lambda *a: 'draft',
599 'active': lambda *a: True,
603 account_journal_period()
605 class account_fiscalyear(osv.osv):
606 _inherit = "account.fiscalyear"
607 _description = "Fiscal Year"
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),
614 #----------------------------------------------------------
616 #----------------------------------------------------------
617 class account_move(osv.osv):
618 _name = "account.move"
619 _description = "Account Entry"
621 def name_get(self, cursor, user, ids, context=None):
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
631 res.append((move.id, name))
635 def _get_period(self, cr, uid, context):
636 periods = self.pool.get('account.period').find(cr, uid)
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'),
651 'state': lambda *a: 'draft',
652 'period_id': _get_period,
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),
662 if len(move_ids) > 1:
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:
671 if line.journal_id.id != move.journal_id.id:
676 (_check_centralisation,
677 'You can not create more than one move per period on centralized journal',
679 (_check_period_journal,
680 'You can not create entries on different period/journal in the same move',
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',))
687 raise osv.except_osv(_('Integrity Error !'), _('You can not validate a non balanced entry !'))
690 def button_validate(self, cursor, user, ids, context=None):
691 return self.post(cursor, user, ids, context=context)
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 !'))
698 cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('draft',))
701 def write(self, cr, uid, ids, vals, context={}):
703 c['novalidate'] = True
704 result = super(osv.osv, self).write(cr, uid, ids, vals, c)
705 self.validate(cr, uid, ids, context)
709 # TODO: Check if period is closed !
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']:
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']:
721 l[2]['period_id'] = vals['period_id']
722 context['period_id'] = vals['period_id']
724 default_period = self._get_period(cr, uid, context)
725 for l in vals['line_id']:
727 l[2]['period_id'] = default_period
728 context['period_id'] = default_period
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)
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:
739 c['novalidate'] = True
740 result = super(account_move, self).create(cr, uid, vals, c)
741 self.validate(cr, uid, [result], context)
743 result = super(account_move, self).create(cr, uid, vals, context)
746 def unlink(self, cr, uid, ids, context={}, check=True):
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"!') % \
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)
761 def _compute_balance(self, cr, uid, id, context={}):
762 move = self.browse(cr, uid, [id])[0]
764 for line in move.line_id:
765 amount+= (line.debit - line.credit)
768 def _centralise(self, cr, uid, move, mode):
770 account_id = move.journal_id.default_debit_account_id.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)
777 account_id = move.journal_id.default_credit_account_id.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)
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))
791 line_id = self.pool.get('account.move.line').create(cr, uid, {
792 'name': 'Centralisation '+mode,
793 'centralisation': mode,
794 'account_id': account_id,
796 'journal_id': move.journal_id.id,
797 'period_id': move.period_id.id,
798 'date': move.period_id.date_stop,
801 }, {'journal_id': move.journal_id.id, 'period_id': move.period_id.id})
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))
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))
818 # Validate a balanced move. If it is a centralised journal, create a move.
820 def validate(self, cr, uid, ids, context={}):
822 for move in self.browse(cr, uid, ids, context):
823 journal = move.journal_id
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)
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'))
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'))
843 if abs(amount) < 0.0001:
844 if not len(line_draft_ids):
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,
850 }, context, check=False)
854 if journal.type not in ('purchase','sale'):
857 for line in move.line_id:
858 if move.journal_id.type == 'sale':
861 key = 'account_paid_id'
864 key = 'account_collected_id'
868 key = 'account_collected_id'
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:
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)
888 if code and not (line.tax_code_id or line.tax_amount):
889 self.pool.get('account.move.line').write(cr, uid,
893 }, context=context, check=False)
898 key = (line.account_id.id, line.tax_code_id.id)
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], {
909 }, context, check=False)
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, {
919 }, context, check=False)
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,
928 }, context, check=False)
933 class account_move_reconcile(osv.osv):
934 _name = "account.move.reconcile"
935 _description = "Account Reconciliation"
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),
944 'name': lambda self,cr,uid,ctx={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile') or '/',
946 def reconcile_partial_check(self, cr, uid, ids, type='auto', context={}):
947 for rec in self.browse(cr, uid, ids, context):
949 for line in rec.line_partial_ids:
950 total += (line.debit or 0.0) - (line.credit or 0.0)
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 }
957 def name_get(self, cr, uid, ids, context=None):
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)
962 result[r.id] = '%s (%.2f)' % (r.name, total)
964 result[r.id] = r.name
966 account_move_reconcile()
968 #----------------------------------------------------------
970 #----------------------------------------------------------
973 child_depend: la taxe depend des taxes filles
975 class account_tax_code(osv.osv):
977 A code for the tax object.
979 This code is used for some tax declarations.
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')
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
1007 res[record.id] = round(_rec_get(record), 2)
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]
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))
1021 _name = 'account.tax.code'
1022 _description = 'Tax Code'
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),
1036 def name_get(self, cr, uid, ids, context=None):
1039 if isinstance(ids, (int, long)):
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']) \
1045 def _default_company(self, cr, uid, context={}):
1046 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1048 return user.company_id.id
1049 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1051 'company_id': _default_company,
1052 'sign': lambda *args: 1.0,
1054 def _check_recursion(self, cr, uid, 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()))
1065 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
1067 _order = 'code,name'
1070 class account_tax(osv.osv):
1074 Type: percent, fixed, none, code
1075 PERCENT: tax = price * amount
1076 FIXED: tax = price + amount
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)
1082 _name = 'account.tax'
1083 _description = 'Tax'
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."),
1103 # Fields used for the VAT declaration
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."),
1110 # Same fields for refund invoices
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),
1121 def name_get(self, cr, uid, ids, context={}):
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 ))
1130 def _default_company(self, cr, uid, context={}):
1131 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1133 return user.company_id.id
1134 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
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,
1153 def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
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):
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)
1169 cur_price_unit=price_unit
1171 # we compute the amount for the current tax object and append it to the result
1173 if tax.type=='percent':
1174 amount = cur_price_unit * tax.amount
1175 res.append({'id':tax.id,
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,
1192 elif tax.type=='fixed':
1193 res.append({'id':tax.id,
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,
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']
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,
1230 amount2 = res[-1]['amount']
1231 if len(tax.child_ids):
1232 if tax.child_depend:
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
1241 def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1244 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
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
1251 res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner)
1253 r['amount'] *= quantity
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)
1261 cur_price_unit=price_unit
1263 # we compute the amount for the current tax object and append it to the result
1265 if tax.type=='percent':
1266 amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
1267 res.append({'id':tax.id,
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,})
1283 elif tax.type=='fixed':
1284 res.append({'id':tax.id,
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,
1297 'tax_code_id': tax.tax_code_id.id,
1298 'ref_tax_code_id': tax.ref_tax_code_id.id,})
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']
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,
1323 amount2 = res[-1]['amount']
1324 if len(tax.child_ids):
1325 if tax.child_depend:
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
1338 def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1340 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1341 Price Unit is a VAT included price
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
1348 res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
1350 r['amount'] *= quantity
1354 # ---------------------------------------------------------
1356 # ---------------------------------------------------------
1358 class account_budget_post(osv.osv):
1359 _name = 'account.budget.post'
1360 _description = 'Budget item'
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'),
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)]))
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})
1382 account_budget_post()
1384 class account_budget_post_dotation(osv.osv):
1385 _name = 'account.budget.post.dotation'
1386 _description = "Budget item endowment"
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)),
1394 account_budget_post_dotation()
1397 # ---------------------------------------------------------
1398 # Account Entries Models
1399 # ---------------------------------------------------------
1401 class account_model(osv.osv):
1402 _name = "account.model"
1403 _description = "Account Model"
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),
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''',
1418 class account_model_line(osv.osv):
1419 _name = "account.model.line"
1420 _description = "Account Model Entries"
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)),
1428 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"),
1430 'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True),
1432 'ref': fields.char('Ref.', size=16),
1434 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."),
1435 'currency_id': fields.many2one('res.currency', 'Currency'),
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"),
1442 'date': lambda *a: 'today'
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 !'),
1449 account_model_line()
1451 # ---------------------------------------------------------
1452 # Account Subscription
1453 # ---------------------------------------------------------
1456 class account_subscription(osv.osv):
1457 _name = "account.subscription"
1458 _description = "Account Subscription"
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),
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),
1470 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines')
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',
1479 def state_draft(self, cr, uid, ids, context={}):
1480 self.write(cr, uid, ids, {'state':'draft'})
1483 def check(self, cr, uid, ids, context={}):
1485 for sub in self.browse(cr, uid, ids, context):
1487 for line in sub.lines_id:
1488 if not line.move_id.id:
1492 todone.append(sub.id)
1494 self.write(cr, uid, todone, {'state':'done'})
1497 def remove_line(self, cr, uid, ids, context={}):
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)
1504 self.pool.get('account.subscription.line').unlink(cr, uid, toremove)
1505 self.write(cr, uid, ids, {'state':'draft'})
1508 def compute(self, cr, uid, ids, context={}):
1509 for sub in self.browse(cr, uid, ids, context):
1511 for i in range(sub.period_total):
1512 self.pool.get('account.subscription.line').create(cr, uid, {
1514 'subscription_id': sub.id,
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'})
1524 account_subscription()
1526 class account_subscription_line(osv.osv):
1527 _name = "account.subscription.line"
1528 _description = "Account Subscription Line"
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'),
1536 def move_create(self, cr, uid, ids, context={}):
1538 for line in self.browse(cr, uid, ids, context):
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]})
1546 self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context)
1549 account_subscription_line()
1552 class account_config_fiscalyear(osv.osv_memory):
1553 _name = 'account.config.fiscalyear'
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),
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'),
1565 def action_cancel(self,cr,uid,ids,conect=None):
1567 'view_type': 'form',
1568 "view_mode": 'form',
1569 'res_model': 'ir.module.module.configuration.wizard',
1570 'type': 'ir.actions.act_window',
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')
1583 'date_start':start_date,
1584 'date_stop':end_date,
1586 new_id=res_obj.create(cr, uid, vals, context=context)
1587 res_obj.create_period(cr,uid,[new_id])
1589 'view_type': 'form',
1590 "view_mode": 'form',
1591 'res_model': 'ir.module.module.configuration.wizard',
1592 'type': 'ir.actions.act_window',
1596 account_config_fiscalyear()
1600 class account_config_journal_bank_accounts(osv.osv_memory):
1601 _name='account.config.journal.bank.account'
1603 'name':fields.char('Journal Name', size=64),
1604 'lines_id': fields.one2many('account.config.journal.bank.account.line', 'journal_id', 'Journal Lines'),
1607 def action_cancel(self,cr,uid,ids,conect=None):
1609 'view_type': 'form',
1610 "view_mode": 'form',
1611 'res_model': 'ir.module.module.configuration.wizard',
1612 'type': 'ir.actions.act_window',
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'])
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):
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]
1633 res_obj.create(cr, uid, vals, context=context)
1635 'view_type': 'form',
1636 "view_mode": 'form',
1637 'res_model': 'ir.module.module.configuration.wizard',
1638 'type': 'ir.actions.act_window',
1642 account_config_journal_bank_accounts()
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]
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),
1657 account_config_journal_bank_accounts_line()
1659 # ----------------------------------------------
1660 # Account Templates : Account, Tax and charts.
1661 # ----------------------------------------------
1663 class account_tax_template(osv.osv):
1664 _name = 'account.tax.template'
1665 account_tax_template()
1667 class account_account_template(osv.osv):
1669 _name = "account.account.template"
1670 _description ='Templates for Accounts'
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'),
1681 ('consolidation','Consolidation'),
1682 ('income','Income'),
1683 ('expense','Expense'),
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'),
1700 #'sign': lambda *a: 1,
1701 'reconcile': lambda *a: False,
1702 'type' : lambda *a :'view',
1705 def _check_recursion(self, cr, uid, 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()))
1716 (_check_recursion, 'Error ! You can not create recursive account templates.', ['parent_id'])
1720 def name_get(self, cr, uid, ids, context={}):
1723 reads = self.read(cr, uid, ids, ['name','code'], context)
1725 for record in reads:
1726 name = record['name']
1728 name = record['code']+' '+name
1729 res.append((record['id'],name ))
1732 account_account_template()
1734 class account_tax_code_template(osv.osv):
1736 _name = 'account.tax.code.template'
1737 _description = 'Tax Code Template'
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),
1748 def name_get(self, cr, uid, ids, context=None):
1751 if isinstance(ids, (int, long)):
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']) \
1757 def _default_company(self, cr, uid, context={}):
1758 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1760 return user.company_id.id
1761 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1764 'company_id': _default_company,
1765 'sign': lambda *args: 1.0,
1768 def _check_recursion(self, cr, uid, 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()))
1779 (_check_recursion, 'Error ! You can not create recursive Tax Codes.', ['parent_id'])
1781 _order = 'code,name'
1782 account_tax_code_template()
1785 class account_chart_template(osv.osv):
1786 _name="account.chart.template"
1787 _description= "Templates for Account Chart"
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'),
1804 account_chart_template()
1806 class account_tax_template(osv.osv):
1808 _name = 'account.tax.template'
1809 _description = 'Templates for Taxes'
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."),
1830 # Fields used for the VAT declaration
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."),
1837 # Same fields for refund invoices
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),
1847 def name_get(self, cr, uid, ids, context={}):
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 ))
1856 def _default_company(self, cr, uid, context={}):
1857 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1859 return user.company_id.id
1860 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
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,
1879 account_tax_template()
1881 # Multi charts of Accounts wizard
1883 class wizard_multi_charts_accounts(osv.osv_memory):
1884 _name='wizard.multi.charts.accounts'
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),
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')
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
1908 acc_template_ref = {}
1909 tax_template_ref = {}
1910 tax_code_template_ref = {}
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
1919 # name = tax_code_template.name
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,
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
1933 for tax in obj_multi.chart_template_id.tax_template_ids:
1937 'sequence': tax.sequence,
1938 'amount':tax.amount,
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,
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,
1966 tax_template_ref[tax.id] = new_tax
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):
1974 for tax in account_template.tax_ids:
1975 tax_ids.append(tax_template_ref[tax.id])
1976 #create the account_account
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,
1991 new_account = obj_acc.create(cr,uid,vals)
1992 acc_template_ref[account_template.id] = new_account
1994 #reactivate the parent_store functionnality on account_account
1995 self.pool._init = False
1996 self.pool.get('account.account')._parent_store_compute(cr)
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'],
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')
2011 vals_journal['view_id']=view_id
2012 vals_journal['sequence_id']=seq_id
2015 vals_journal['name'] = 'Sales Journal '+ str(seq_code)
2016 vals_journal['type'] = 'sale'
2017 vals_journal['code'] = 'SAJ' + str(seq_code)
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]
2023 obj_journal.create(cr,uid,vals_journal)
2026 vals_journal['name']='Purchase Journal '+ str(seq_code)
2027 vals_journal['type']='purchase'
2028 vals_journal['code']='EXJ' + str(seq_code)
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]
2034 obj_journal.create(cr,uid,vals_journal)
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
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]
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),
2051 'user_type': account_template.user_type or False,
2053 'parent_id': acc_template_ref[ref_acc_bank.id] or False,
2054 'company_id': company_id,
2056 acc_cash_id = obj_acc.create(cr,uid,vals)
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
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)
2074 #create the properties
2075 property_obj = self.pool.get('ir.property')
2076 fields_obj = self.pool.get('ir.model.fields')
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')
2087 for record in todo_list:
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])])
2094 'company_id': company_id,
2095 'fields_id': field[0],
2096 'value': account and 'account.account,'+str(acc_template_ref[account.id]) or False,
2099 #the property exist: modify it
2100 property_obj.write(cr, uid, r, vals)
2102 #create the property
2103 property_obj.create(cr, uid, vals)
2107 wizard_multi_charts_accounts()
2109 class account_bank_accounts_wizard(osv.osv_memory):
2110 _name='account.bank.accounts.wizard'
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'),
2118 account_bank_accounts_wizard()
2120 class wizard_account_chart_duplicate(osv.osv_memory):
2122 Create a new account chart for a new company.
2124 * an accuont chart (parent_id,=,False)
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
2131 _name = 'wizard.account.chart.duplicate'
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),
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])
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']
2170 return {'type':'ir.actions.act_window_close'}
2172 wizard_account_chart_duplicate()
2174 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: