1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
24 from osv import fields, osv
26 from tools.misc import currency
27 from tools.translate import _
30 from mx.DateTime import RelativeDateTime, now, DateTime, localtime
33 class account_payment_term(osv.osv):
34 _name = "account.payment.term"
35 _description = "Payment Term"
37 'name': fields.char('Payment Term', size=32, translate=True, required=True),
38 'active': fields.boolean('Active'),
39 'note': fields.text('Description', translate=True),
40 'line_ids': fields.one2many('account.payment.term.line', 'payment_id', 'Terms'),
43 'active': lambda *a: 1,
47 def compute(self, cr, uid, id, value, date_ref=False, context={}):
49 date_ref = now().strftime('%Y-%m-%d')
50 pt = self.browse(cr, uid, id, context)
53 for line in pt.line_ids:
54 if line.value=='fixed':
55 amt = round(line.value_amount, 2)
56 elif line.value=='procent':
57 amt = round(value * line.value_amount, 2)
58 elif line.value=='balance':
59 amt = round(amount, 2)
61 next_date = mx.DateTime.strptime(date_ref, '%Y-%m-%d') + RelativeDateTime(days=line.days)
63 next_date += RelativeDateTime(day=line.days2)
65 next_date += RelativeDateTime(day=line.days2, months=1)
66 result.append( (next_date.strftime('%Y-%m-%d'), amt) )
70 account_payment_term()
72 class account_payment_term_line(osv.osv):
73 _name = "account.payment.term.line"
74 _description = "Payment Term Line"
76 'name': fields.char('Line Name', size=32,required=True),
77 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the payment term lines from the lowest sequences to the higher ones"),
78 'value': fields.selection([('procent','Percent'),('balance','Balance'),('fixed','Fixed Amount')], 'Value',required=True),
79 'value_amount': fields.float('Value Amount'),
80 'days': fields.integer('Number of Days',required=True, help="Number of days to add before computation of the day of month."),
81 'days2': fields.integer('Day of the Month',required=True, help="Day of the month, set -1 for the last day of the current month. If it's positive, it gives the day of the next month. Set 0 for net days (otherwise it's based on the end of the month)."),
82 'payment_id': fields.many2one('account.payment.term','Payment Term', required=True, select=True),
85 'value': lambda *a: 'balance',
86 'sequence': lambda *a: 5,
87 'days2': lambda *a: 0,
90 account_payment_term_line()
93 class account_account_type(osv.osv):
94 _name = "account.account.type"
95 _description = "Account Type"
97 'name': fields.char('Acc. Type Name', size=64, required=True, translate=True),
98 'code': fields.char('Code', size=32, required=True),
99 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of account types."),
100 'partner_account': fields.boolean('Partner account'),
101 'close_method': fields.selection([('none','None'), ('balance','Balance'), ('detail','Detail'),('unreconciled','Unreconciled')], 'Deferral Method', required=True),
102 'sign': fields.selection([(-1, 'Negative'), (1, 'Positive')], 'Sign on Reports', required=True, help='Allows to change the displayed amount of the balance in the reports, in order to see positive results instead of negative ones in expenses accounts.'),
105 'close_method': lambda *a: 'none',
106 'sequence': lambda *a: 5,
107 'sign': lambda *a: 1,
110 account_account_type()
112 def _code_get(self, cr, uid, context={}):
113 acc_type_obj = self.pool.get('account.account.type')
114 ids = acc_type_obj.search(cr, uid, [])
115 res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context)
116 return [(r['code'], r['name']) for r in res]
118 #----------------------------------------------------------
120 #----------------------------------------------------------
122 class account_tax(osv.osv):
123 _name = 'account.tax'
126 class account_account(osv.osv):
127 _order = "parent_left"
128 _parent_order = "code"
129 _name = "account.account"
130 _description = "Account"
133 def search(self, cr, uid, args, offset=0, limit=None, order=None,
134 context=None, count=False):
141 if args[pos][0]=='code' and args[pos][1] in ('like','ilike') and args[pos][2]:
142 args[pos] = ('code', '=like', str(args[pos][2].replace('%',''))+'%')
143 if args[pos][0]=='journal_id':
147 jour = self.pool.get('account.journal').browse(cr, uid, args[pos][2])
148 if (not (jour.account_control_ids or jour.type_control_ids)) or not args[pos][2]:
151 ids3 = map(lambda x: x.code, jour.type_control_ids)
152 ids1 = super(account_account,self).search(cr, uid, [('type','in',ids3)])
153 ids1 += map(lambda x: x.id, jour.account_control_ids)
154 args[pos] = ('id','in',ids1)
157 return super(account_account,self).search(cr, uid, args, offset, limit,
158 order, context=context, count=count)
160 def _get_children_and_consol(self, cr, uid, ids, context={}):
161 #this function search for all the children and all consolidated children (recursively) of the given account ids
162 res = self.search(cr, uid, [('parent_id', 'child_of', ids)])
164 this = self.browse(cr, uid, id, context)
165 for child in this.child_consol_ids:
166 if child.id not in res:
168 if len(res) != len(ids):
169 return self._get_children_and_consol(cr, uid, res, context)
172 def __compute(self, cr, uid, ids, field_names, arg, context={}, query=''):
173 #compute the balance/debit/credit accordingly to the value of field_name for the given account ids
175 'balance': "COALESCE(SUM(l.debit) - SUM(l.credit), 0) as balance ",
176 'debit': "COALESCE(SUM(l.debit), 0) as debit ",
177 'credit': "COALESCE(SUM(l.credit), 0) as credit "
179 #get all the necessary accounts
180 ids2 = self._get_children_and_consol(cr, uid, ids, context)
181 acc_set = ",".join(map(str, ids2))
182 #compute for each account the balance/debit/credit from the move lines
185 query = self.pool.get('account.move.line')._query_get(cr, uid,
187 cr.execute(("SELECT l.account_id as id, " +\
188 ' , '.join(map(lambda x: mapping[x], field_names)) +
190 "account_move_line l " \
192 "l.account_id IN (%s) " \
193 "AND " + query + " " \
194 "GROUP BY l.account_id") % (acc_set, ))
196 for res in cr.dictfetchall():
197 accounts[res['id']] = res
199 #for the asked accounts, get from the dictionnary 'accounts' the value of it
202 res[id] = self._get_account_values(cr, uid, id, accounts, field_names, context)
205 def _get_account_values(self, cr, uid, id, accounts, field_names, context={}):
206 res = {}.fromkeys(field_names, 0.0)
207 browse_rec = self.browse(cr, uid, id)
208 if browse_rec.type == 'consolidation':
209 ids2 = self.read(cr, uid, [browse_rec.id], ['child_consol_ids'], context)[0]['child_consol_ids']
210 for t in self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])]):
211 if t not in ids2 and t != browse_rec.id:
214 tmp = self._get_account_values(cr, uid, i, accounts, field_names, context)
215 for a in field_names:
218 ids2 = self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])])
220 for a in field_names:
221 res[a] += accounts.get(i, {}).get(a, 0.0)
224 def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}):
226 for rec in self.browse(cr, uid, ids, context):
227 result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code)
230 def _get_child_ids(self, cr, uid, ids, field_name, arg, context={}):
232 for record in self.browse(cr, uid, ids, context):
233 if record.child_parent_ids:
234 result[record.id]=[x.id for x in record.child_parent_ids]
238 if record.child_consol_ids:
239 for acc in record.child_consol_ids:
240 result[record.id].append(acc.id)
245 'name': fields.char('Name', size=128, required=True, select=True),
246 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
247 'code': fields.char('Code', size=64, required=True),
248 'type': fields.selection([
249 ('receivable','Receivable'),
250 ('payable','Payable'),
252 ('consolidation','Consolidation'),
255 ], 'Internal Type', required=True,),
257 'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
258 'parent_id': fields.many2one('account.account','Parent', ondelete='cascade'),
259 'child_parent_ids':fields.one2many('account.account','parent_id','Children'),
260 'child_consol_ids':fields.many2many('account.account', 'account_account_consol_rel', 'child_id', 'parent_id', 'Consolidated Children'),
261 'child_id': fields.function(_get_child_ids, method=True, type='many2many',relation="account.account",string="Children Accounts"),
262 'balance': fields.function(__compute, digits=(16,2), method=True, string='Balance', multi='balance'),
263 'credit': fields.function(__compute, digits=(16,2), method=True, string='Credit', multi='balance'),
264 'debit': fields.function(__compute, digits=(16,2), method=True, string='Debit', multi='balance'),
265 'reconcile': fields.boolean('Reconcile', help="Check this account if the user can make a reconciliation of the entries in this account."),
266 'shortcut': fields.char('Shortcut', size=12),
267 'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel',
268 'account_id','tax_id', 'Default Taxes'),
269 'note': fields.text('Note'),
270 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Company Currency'),
271 'company_id': fields.many2one('res.company', 'Company', required=True),
272 'active': fields.boolean('Active', select=2),
274 'parent_left': fields.integer('Parent Left', select=1),
275 'parent_right': fields.integer('Parent Right', select=1),
276 'currency_mode': fields.selection([('current','At Date'),('average','Average Rate')], 'Outgoing Currencies Rate',
278 'This will select how is computed the current currency rate for outgoing transactions. '\
279 'In most countries the legal method is "average" but only a few softwares are able to '\
280 'manage this. So if you import from another software, you may have to use the rate at date. ' \
281 'Incoming transactions, always use the rate at date.', \
283 'check_history': fields.boolean('Display History',
284 help="Check this box if you want to print all entries when printing the General Ledger, "\
285 "otherwise it will only print its balance."),
286 'merge_invoice': fields.boolean('Merge Invoice Entries',help="Check this box if you want that all lines of "\
287 "a customer or supplier invoice using this account are created in one line only"),
290 def _default_company(self, cr, uid, context={}):
291 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
293 return user.company_id.id
294 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
297 'type' : lambda *a :'view',
298 'reconcile': lambda *a: False,
299 'company_id': _default_company,
300 'active': lambda *a: True,
301 'check_history': lambda *a: True,
302 'currency_mode': lambda *a: 'current'
305 def _check_recursion(self, cr, uid, ids):
306 obj_self=self.browse(cr,uid,ids[0])
307 p_id=obj_self.parent_id and obj_self.parent_id.id
308 if (obj_self in obj_self.child_consol_ids) or (p_id and (p_id is obj_self.id)):
311 cr.execute('select distinct child_id from account_account_consol_rel where parent_id in ('+','.join(map(str,ids))+')')
312 child_ids = filter(None, map(lambda x:x[0], cr.fetchall()))
314 if (p_id and (p_id in c_ids)) or (obj_self.id in c_ids):
317 s_ids=self.search(cr,uid,[('parent_id','in',c_ids)])
318 if p_id and (p_id in s_ids):
325 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
327 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
335 if name and str(name).startswith('partner:'):
336 part_id = int(name.split(':')[1])
337 part = self.pool.get('res.partner').browse(cr, user, part_id, context)
338 args += [('id','in', (part.property_account_payable.id, part.property_account_receivable.id))]
340 if name and str(name).startswith('type:'):
341 type = name.split(':')[1]
342 args += [('type','=', type)]
347 ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit)
349 ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit)
351 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
353 ids = self.search(cr, user, args, context=context, limit=limit)
354 return self.name_get(cr, user, ids, context=context)
356 def name_get(self, cr, uid, ids, context={}):
359 reads = self.read(cr, uid, ids, ['name','code'], context)
362 name = record['name']
364 name = record['code']+' '+name
365 res.append((record['id'],name ))
368 def copy(self, cr, uid, id, default={}, context={},done_list=[]):
369 account = self.browse(cr, uid, id, context=context)
373 default=default.copy()
374 default['parent_id'] = False
375 if account.id in done_list:
377 done_list.append(account.id)
379 for child in account.child_id:
380 child_ids=self.copy(cr, uid, child.id, default, context=context,done_list=done_list)
382 new_child_ids.append(child_ids)
383 default['child_parent_ids'] = [(6, 0, new_child_ids)]
385 default['child_parent_ids'] = False
386 return super(account_account, self).copy(cr, uid, id, default, context=context)
388 def write(self, cr, uid, ids, vals, context=None):
391 if 'active' in vals and not vals['active']:
392 line_obj = self.pool.get('account.move.line')
393 account_ids = self.search(cr, uid, [('id', 'child_of', ids)])
394 if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]):
395 raise osv.except_osv(_('Error !'), _('You can not deactivate an account that contains account moves.'))
396 return super(account_account, self).write(cr, uid, ids, vals, context=context)
399 class account_journal_view(osv.osv):
400 _name = "account.journal.view"
401 _description = "Journal View"
403 'name': fields.char('Journal View', size=64, required=True),
404 'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
407 account_journal_view()
410 class account_journal_column(osv.osv):
411 def _col_get(self, cr, user, context={}):
413 cols = self.pool.get('account.move.line')._columns
415 result.append( (col, cols[col].string) )
418 _name = "account.journal.column"
419 _description = "Journal Column"
421 'name': fields.char('Column Name', size=64, required=True),
422 'field': fields.selection(_col_get, 'Field Name', method=True, required=True, size=32),
423 'view_id': fields.many2one('account.journal.view', 'Journal View', select=True),
424 'sequence': fields.integer('Sequence'),
425 'required': fields.boolean('Required'),
426 'readonly': fields.boolean('Readonly'),
429 account_journal_column()
431 class account_journal(osv.osv):
432 _name = "account.journal"
433 _description = "Journal"
435 'name': fields.char('Journal Name', size=64, required=True, translate=True),
436 'code': fields.char('Code', size=16),
437 'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True),
439 'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]),
440 'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]),
442 'active': fields.boolean('Active'),
443 '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."),
444 'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account'),
445 'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account'),
446 '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."),
447 'update_posted': fields.boolean('Allow Cancelling Entries'),
448 'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="The sequence gives the display order for a list of journals", required=True),
449 'user_id': fields.many2one('res.users', 'User', help="The responsible user of this journal"),
450 'groups_id': fields.many2many('res.groups', 'account_journal_group_rel', 'journal_id', 'group_id', 'Groups'),
451 'currency': fields.many2one('res.currency', 'Currency', help='The currency used to enter statement'),
452 '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.'),
453 'company_id': fields.related('default_credit_account_id','company_id',type='many2one', relation="res.company", string="Company"),
457 'active': lambda *a: 1,
458 'user_id': lambda self,cr,uid,context: uid,
460 def create(self, cr, uid, vals, context={}):
461 journal_id = super(osv.osv, self).create(cr, uid, vals, context)
462 # journal_name = self.browse(cr, uid, [journal_id])[0].code
463 # periods = self.pool.get('account.period')
464 # ids = periods.search(cr, uid, [('date_stop','>=',time.strftime('%Y-%m-%d'))])
465 # for period in periods.browse(cr, uid, ids):
466 # self.pool.get('account.journal.period').create(cr, uid, {
467 # 'name': (journal_name or '')+':'+(period.code or ''),
468 # 'journal_id': journal_id,
469 # 'period_id': period.id
472 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
479 ids = self.search(cr, user, [('code','ilike',name)]+ args, limit=limit)
481 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
482 return self.name_get(cr, user, ids, context=context)
485 class account_fiscalyear(osv.osv):
486 _name = "account.fiscalyear"
487 _description = "Fiscal Year"
489 'name': fields.char('Fiscal Year', size=64, required=True),
490 'code': fields.char('Code', size=6, required=True),
491 'date_start': fields.date('Start date', required=True),
492 'date_stop': fields.date('End date', required=True),
493 'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'),
494 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', redonly=True),
498 'state': lambda *a: 'draft',
500 _order = "date_start"
501 def create_period3(self,cr, uid, ids, context={}):
502 return self.create_period(cr, uid, ids, context, 3)
504 def create_period(self,cr, uid, ids, context={}, interval=1):
505 for fy in self.browse(cr, uid, ids, context):
507 ds = mx.DateTime.strptime(fy.date_start, '%Y-%m-%d')
508 while ds.strftime('%Y-%m-%d')<fy.date_stop:
509 de = ds + RelativeDateTime(months=interval, days=-1)
510 self.pool.get('account.period').create(cr, uid, {
511 'name': ds.strftime('%m/%Y'),
512 'code': ds.strftime('%m/%Y'),
513 'date_start': ds.strftime('%Y-%m-%d'),
514 'date_stop': de.strftime('%Y-%m-%d'),
515 'fiscalyear_id': fy.id,
517 ds = ds + RelativeDateTime(months=interval)
520 def find(self, cr, uid, dt=None, exception=True, context={}):
522 dt = time.strftime('%Y-%m-%d')
523 ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)])
526 raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.'))
532 class account_period(osv.osv):
533 _name = "account.period"
534 _description = "Account period"
536 'name': fields.char('Period Name', size=64, required=True),
537 'code': fields.char('Code', size=12),
538 'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}),
539 'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}),
540 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True),
541 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', readonly=True)
544 'state': lambda *a: 'draft',
546 _order = "date_start"
547 def next(self, cr, uid, period, step, context={}):
548 ids = self.search(cr, uid, [('date_start','>',period.date_start)])
553 def find(self, cr, uid, dt=None, context={}):
555 dt = time.strftime('%Y-%m-%d')
556 #CHECKME: shouldn't we check the state of the period?
557 ids = self.search(cr, uid, [('date_start','<=',dt),('date_stop','>=',dt)])
559 raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.'))
562 def action_draft(self, cr, uid, ids, *args):
563 users_roles = self.pool.get('res.users').browse(cr, uid, uid).roles_id
564 for role in users_roles:
565 if role.name=='Period':
568 cr.execute('update account_journal_period set state=%s where period_id=%d', (mode, id))
569 cr.execute('update account_period set state=%s where id=%d', (mode, id))
574 class account_journal_period(osv.osv):
575 _name = "account.journal.period"
576 _description = "Journal - Period"
578 def _icon_get(self, cr, uid, ids, field_name, arg=None, context={}):
579 result = {}.fromkeys(ids, 'STOCK_NEW')
580 for r in self.read(cr, uid, ids, ['state']):
582 'draft': 'STOCK_NEW',
583 'printed': 'STOCK_PRINT_PREVIEW',
584 'done': 'STOCK_DIALOG_AUTHENTICATION',
585 }.get(r['state'], 'STOCK_NEW')
589 'name': fields.char('Journal-Period Name', size=64, required=True),
590 'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"),
591 'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"),
592 'icon': fields.function(_icon_get, method=True, string='Icon', type='string'),
593 'active': fields.boolean('Active', required=True),
594 'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'Status', required=True, readonly=True)
597 def _check(self, cr, uid, ids, context={}):
598 for obj in self.browse(cr, uid, ids, context):
599 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))
602 raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !'))
605 def write(self, cr, uid, ids, vals, context={}):
606 self._check(cr, uid, ids, context)
607 return super(account_journal_period, self).write(cr, uid, ids, vals, context)
609 def create(self, cr, uid, vals, context={}):
610 period_id=vals.get('period_id',False)
612 period = self.pool.get('account.period').browse(cr, uid,period_id)
613 vals['state']=period.state
614 return super(account_journal_period, self).create(cr, uid, vals, context)
616 def unlink(self, cr, uid, ids, context={}):
617 self._check(cr, uid, ids, context)
618 return super(account_journal_period, self).unlink(cr, uid, ids, context)
621 'state': lambda *a: 'draft',
622 'active': lambda *a: True,
626 account_journal_period()
628 class account_fiscalyear(osv.osv):
629 _inherit = "account.fiscalyear"
630 _description = "Fiscal Year"
632 'start_journal_period_id':fields.many2one('account.journal.period','New Entries Journal'),
633 'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True),
637 #----------------------------------------------------------
639 #----------------------------------------------------------
640 class account_move(osv.osv):
641 _name = "account.move"
642 _description = "Account Entry"
644 def name_get(self, cursor, user, ids, context=None):
648 data_move = self.pool.get('account.move').browse(cursor,user,ids)
649 for move in data_move:
650 if move.state=='draft':
651 name = '*' + str(move.id)
654 res.append((move.id, name))
658 def _get_period(self, cr, uid, context):
659 periods = self.pool.get('account.period').find(cr, uid)
665 def _amount_compute(self, cr, uid, ids, name, args, context, where =''):
666 if not ids: return {}
667 cr.execute('select move_id,sum(debit) from account_move_line where move_id in ('+','.join(map(str,ids))+') group by move_id')
668 result = dict(cr.fetchall())
670 result.setdefault(id, 0.0)
674 'name': fields.char('Entry Number', size=64, required=True),
675 'ref': fields.char('Ref', size=64),
676 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}),
677 'journal_id': fields.many2one('account.journal', 'Journal', required=True, states={'posted':[('readonly',True)]}),
678 'state': fields.selection([('draft','Draft'), ('posted','Posted')], 'Status', required=True, readonly=True),
679 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}),
680 'to_check': fields.boolean('To Be Verified'),
681 'partner_id': fields.related('line_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store=True),
682 'amount': fields.function(_amount_compute, method=True, string='Amount', digits=(16,2), store=True),
683 'type': fields.selection([
684 ('pay_voucher','Cash Payment'),
685 ('bank_pay_voucher','Bank Payment'),
686 ('rec_voucher','Cash Receipt'),
687 ('bank_rec_voucher','Bank Receipt'),
688 ('cont_voucher','Contra'),
689 ('journal_sale_vou','Journal Sale'),
690 ('journal_pur_voucher','Journal Purchase'),
691 ('journal_voucher','Journal Voucher'),
692 ],'Type', readonly=True, select=True, states={'draft':[('readonly',False)]}),
695 'name': lambda *a: '/',
696 'state': lambda *a: 'draft',
697 'period_id': _get_period,
698 'type' : lambda *a : 'journal_voucher',
701 def _check_centralisation(self, cursor, user, ids):
702 for move in self.browse(cursor, user, ids):
703 if move.journal_id.centralisation:
704 move_ids = self.search(cursor, user, [
705 ('period_id', '=', move.period_id.id),
706 ('journal_id', '=', move.journal_id.id),
708 if len(move_ids) > 1:
712 def _check_period_journal(self, cursor, user, ids):
713 for move in self.browse(cursor, user, ids):
714 for line in move.line_id:
715 if line.period_id.id != move.period_id.id:
717 if line.journal_id.id != move.journal_id.id:
722 (_check_centralisation,
723 'You can not create more than one move per period on centralized journal',
725 (_check_period_journal,
726 'You can not create entries on different period/journal in the same move',
729 def post(self, cr, uid, ids, context=None):
730 if self.validate(cr, uid, ids, context) and len(ids):
731 for move in self.browse(cr, uid, ids):
734 journal = move.journal_id
735 if journal.sequence_id:
736 new_name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id)
738 raise osv.except_osv(_('Error'), _('No sequence defined in the journal !'))
740 self.write(cr, uid, [move.id], {'name':new_name})
742 cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('posted',))
744 raise osv.except_osv(_('Integrity Error !'), _('You can not validate a non balanced entry !'))
747 def button_validate(self, cursor, user, ids, context=None):
748 return self.post(cursor, user, ids, context=context)
750 def button_cancel(self, cr, uid, ids, context={}):
751 for line in self.browse(cr, uid, ids, context):
752 if not line.journal_id.update_posted:
753 raise osv.except_osv(_('Error !'), _('You can not modify a posted entry of this journal !'))
755 cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('draft',))
758 def write(self, cr, uid, ids, vals, context={}):
760 c['novalidate'] = True
761 result = super(osv.osv, self).write(cr, uid, ids, vals, c)
762 self.validate(cr, uid, ids, context)
766 # TODO: Check if period is closed !
768 def create(self, cr, uid, vals, context={}):
769 if 'line_id' in vals:
770 if 'journal_id' in vals:
771 for l in vals['line_id']:
773 l[2]['journal_id'] = vals['journal_id']
774 context['journal_id'] = vals['journal_id']
775 if 'period_id' in vals:
776 for l in vals['line_id']:
778 l[2]['period_id'] = vals['period_id']
779 context['period_id'] = vals['period_id']
781 default_period = self._get_period(cr, uid, context)
782 for l in vals['line_id']:
784 l[2]['period_id'] = default_period
785 context['period_id'] = default_period
787 accnt_journal = self.pool.get('account.journal').browse(cr, uid, vals['journal_id'])
788 if 'line_id' in vals:
790 c['novalidate'] = True
791 result = super(account_move, self).create(cr, uid, vals, c)
792 self.validate(cr, uid, [result], context)
794 result = super(account_move, self).create(cr, uid, vals, context)
797 def copy(self, cr, uid, id, default=None, context=None):
800 default = default.copy()
801 default.update({'state':'draft', 'name':'/',})
802 return super(account_move, self).copy(cr, uid, id, default, context)
804 def unlink(self, cr, uid, ids, context={}, check=True):
806 for move in self.browse(cr, uid, ids, context):
807 if move['state'] <> 'draft':
808 raise osv.except_osv(_('UserError'),
809 _('You can not delete posted movement: "%s"!') % \
811 line_ids = map(lambda x: x.id, move.line_id)
812 context['journal_id'] = move.journal_id.id
813 context['period_id'] = move.period_id.id
814 self.pool.get('account.move.line')._update_check(cr, uid, line_ids, context)
815 toremove.append(move.id)
816 result = super(account_move, self).unlink(cr, uid, toremove, context)
819 def _compute_balance(self, cr, uid, id, context={}):
820 move = self.browse(cr, uid, [id])[0]
822 for line in move.line_id:
823 amount+= (line.debit - line.credit)
826 def _centralise(self, cr, uid, move, mode):
828 account_id = move.journal_id.default_debit_account_id.id
831 raise osv.except_osv(_('UserError'),
832 _('There is no default default debit account defined \n' \
833 'on journal "%s"') % move.journal_id.name)
835 account_id = move.journal_id.default_credit_account_id.id
838 raise osv.except_osv(_('UserError'),
839 _('There is no default default credit account defined \n' \
840 'on journal "%s"') % move.journal_id.name)
842 # find the first line of this move with the current mode
843 # or create it if it doesn't exist
844 cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode))
849 line_id = self.pool.get('account.move.line').create(cr, uid, {
850 'name': 'Centralisation '+mode,
851 'centralisation': mode,
852 'account_id': account_id,
854 'journal_id': move.journal_id.id,
855 'period_id': move.period_id.id,
856 'date': move.period_id.date_stop,
859 }, {'journal_id': move.journal_id.id, 'period_id': move.period_id.id})
861 # find the first line of this move with the other mode
862 # so that we can exclude it from our calculation
863 cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode2))
870 cr.execute('select sum('+mode+') from account_move_line where move_id=%d and id<>%d', (move.id, line_id2))
871 result = cr.fetchone()[0] or 0.0
872 cr.execute('update account_move_line set '+mode2+'=%f where id=%d', (result, line_id))
876 # Validate a balanced move. If it is a centralised journal, create a move.
878 def validate(self, cr, uid, ids, context={}):
880 for move in self.browse(cr, uid, ids, context):
881 journal = move.journal_id
886 for line in move.line_id:
887 amount += line.debit - line.credit
888 line_ids.append(line.id)
889 if line.state=='draft':
890 line_draft_ids.append(line.id)
893 company_id = line.account_id.company_id.id
894 if not company_id == line.account_id.company_id.id:
895 raise osv.except_osv(_('Error'), _('Couldn\'t create move between different companies'))
897 if line.account_id.currency_id:
898 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):
899 raise osv.except_osv(_('Error'), _('Couldn\'t create move with currency different than the secondary currency of the account "%s - %s". Clear the secondary currency field of the account definition if you want to accept all currencies.' % (line.account_id.code, line.account_id.name)))
901 if abs(amount) < 0.0001:
902 if not len(line_draft_ids):
904 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
905 'journal_id': move.journal_id.id,
906 'period_id': move.period_id.id,
908 }, context, check=False)
912 if journal.type not in ('purchase','sale'):
915 for line in move.line_id:
916 if move.journal_id.type == 'sale':
919 key = 'account_paid_id'
922 key = 'account_collected_id'
926 key = 'account_collected_id'
929 key = 'account_paid_id'
930 if line.account_id.tax_ids:
931 code = amount = False
932 for tax in line.account_id.tax_ids:
934 acc = getattr(tax, key).id
935 account[acc] = (getattr(tax,
936 field_base + 'tax_code_id').id,
937 getattr(tax, field_base + 'tax_sign'))
938 account2[(acc,getattr(tax,
939 field_base + 'tax_code_id').id)] = (getattr(tax,
940 field_base + 'tax_code_id').id,
941 getattr(tax, field_base + 'tax_sign'))
942 code = getattr(tax, field_base + 'base_code_id').id
943 amount = getattr(tax, field_base+'base_sign') * \
944 (line.debit + line.credit)
946 if code and not (line.tax_code_id or line.tax_amount):
947 self.pool.get('account.move.line').write(cr, uid,
951 }, context=context, check=False)
956 key = (line.account_id.id, line.tax_code_id.id)
958 code = account2[key][0]
959 amount = account2[key][1] * (line.debit + line.credit)
960 elif line.account_id.id in account:
961 code = account[line.account_id.id][0]
962 amount = account[line.account_id.id][1] * (line.debit + line.credit)
963 if (code or amount) and not (line.tax_code_id or line.tax_amount):
964 self.pool.get('account.move.line').write(cr, uid, [line.id], {
967 }, context, check=False)
972 if journal.centralisation:
973 self._centralise(cr, uid, move, 'debit')
974 self._centralise(cr, uid, move, 'credit')
975 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
977 }, context, check=False)
980 self.pool.get('account.move.line').write(cr, uid, line_ids, {
981 'journal_id': move.journal_id.id,
982 'period_id': move.period_id.id,
983 #'tax_code_id': False,
984 #'tax_amount': False,
986 }, context, check=False)
991 class account_move_reconcile(osv.osv):
992 _name = "account.move.reconcile"
993 _description = "Account Reconciliation"
995 'name': fields.char('Name', size=64, required=True),
996 'type': fields.char('Type', size=16, required=True),
997 'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry lines'),
998 'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'),
999 'create_date': fields.date('Creation date', readonly=True),
1002 'name': lambda self,cr,uid,ctx={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile') or '/',
1004 def reconcile_partial_check(self, cr, uid, ids, type='auto', context={}):
1005 for rec in self.browse(cr, uid, ids, context):
1007 for line in rec.line_partial_ids:
1008 total += (line.debit or 0.0) - (line.credit or 0.0)
1010 self.pool.get('account.move.line').write(cr, uid,
1011 map(lambda x: x.id, rec.line_partial_ids),
1012 {'reconcile_id': rec.id }
1016 def name_get(self, cr, uid, ids, context=None):
1020 for r in self.browse(cr, uid, ids, context):
1021 total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0)
1023 name = '%s (%.2f)' % (r.name, total)
1024 result.append((r.id,name))
1026 result.append((r.id,r.name))
1030 account_move_reconcile()
1032 #----------------------------------------------------------
1034 #----------------------------------------------------------
1037 child_depend: la taxe depend des taxes filles
1039 class account_tax_code(osv.osv):
1041 A code for the tax object.
1043 This code is used for some tax declarations.
1045 def _sum(self, cr, uid, ids, name, args, context, where =''):
1046 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
1047 acc_set = ",".join(map(str, ids2))
1048 if context.get('based_on', 'invoices') == 'payments':
1049 cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1050 FROM account_move_line AS line, \
1051 account_move AS move \
1052 LEFT JOIN account_invoice invoice ON \
1053 (invoice.move_id = move.id) \
1054 WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1055 AND move.id = line.move_id \
1056 AND ((invoice.state = \'paid\') \
1057 OR (invoice.id IS NULL)) \
1058 GROUP BY line.tax_code_id')
1060 cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1061 FROM account_move_line AS line \
1062 WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1063 GROUP BY line.tax_code_id')
1064 res=dict(cr.fetchall())
1065 for record in self.browse(cr, uid, ids, context):
1066 def _rec_get(record):
1067 amount = res.get(record.id, 0.0)
1068 for rec in record.child_ids:
1069 amount += _rec_get(rec) * rec.sign
1071 res[record.id] = round(_rec_get(record), 2)
1074 def _sum_period(self, cr, uid, ids, name, args, context):
1075 if not 'period_id' in context:
1076 period_id = self.pool.get('account.period').find(cr, uid)
1077 if not len(period_id):
1078 return dict.fromkeys(ids, 0.0)
1079 period_id = period_id[0]
1081 period_id = context['period_id']
1082 return self._sum(cr, uid, ids, name, args, context,
1083 where=' and line.period_id='+str(period_id))
1085 _name = 'account.tax.code'
1086 _description = 'Tax Code'
1089 'name': fields.char('Tax Case Name', size=64, required=True),
1090 'code': fields.char('Case Code', size=64),
1091 'info': fields.text('Description'),
1092 'sum': fields.function(_sum, method=True, string="Year Sum"),
1093 'sum_period': fields.function(_sum_period, method=True, string="Period Sum"),
1094 'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True),
1095 'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'),
1096 'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'),
1097 'company_id': fields.many2one('res.company', 'Company', required=True),
1098 'sign': fields.float('Sign for parent', required=True),
1101 def name_get(self, cr, uid, ids, context=None):
1104 if isinstance(ids, (int, long)):
1106 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1107 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1110 def _default_company(self, cr, uid, context={}):
1111 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1113 return user.company_id.id
1114 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1116 'company_id': _default_company,
1117 'sign': lambda *args: 1.0,
1119 def _check_recursion(self, cr, uid, ids):
1122 cr.execute('select distinct parent_id from account_tax_code where id in ('+','.join(map(str,ids))+')')
1123 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1130 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
1132 _order = 'code,name'
1135 class account_tax(osv.osv):
1139 Type: percent, fixed, none, code
1140 PERCENT: tax = price * amount
1141 FIXED: tax = price + amount
1143 CODE: execute python code. localcontext = {'price_unit':pu, 'address':address_object}
1144 return result in the context
1145 Ex: result=round(price_unit*0.21,4)
1147 _name = 'account.tax'
1148 _description = 'Tax'
1150 'name': fields.char('Tax Name', size=64, required=True, help="This name will be used to be displayed on reports"),
1151 '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."),
1152 'amount': fields.float('Amount', required=True, digits=(14,4)),
1153 'active': fields.boolean('Active'),
1154 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1155 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1156 '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."),
1157 'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'),
1158 'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'),
1159 'parent_id':fields.many2one('account.tax', 'Parent Tax Account', select=True),
1160 'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'),
1161 '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."),
1162 'python_compute':fields.text('Python Code'),
1163 'python_compute_inv':fields.text('Python Code (reverse)'),
1164 'python_applicable':fields.text('Python Code'),
1165 '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."),
1168 # Fields used for the VAT declaration
1170 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."),
1171 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."),
1172 'base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."),
1173 'tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."),
1175 # Same fields for refund invoices
1177 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the VAT declaration."),
1178 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1179 'ref_base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."),
1180 'ref_tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."),
1181 '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"),
1182 'company_id': fields.many2one('res.company', 'Company', required=True),
1183 'description': fields.char('Internal Name',size=32),
1186 def name_get(self, cr, uid, ids, context={}):
1190 for record in self.read(cr, uid, ids, ['description','name'], context):
1191 name = record['description'] and record['description'] or record['name']
1192 res.append((record['id'],name ))
1195 def _default_company(self, cr, uid, context={}):
1196 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1198 return user.company_id.id
1199 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1201 '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''',
1202 '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''',
1203 'applicable_type': lambda *a: 'true',
1204 'type': lambda *a: 'percent',
1205 'amount': lambda *a: 0,
1206 'active': lambda *a: 1,
1207 'sequence': lambda *a: 1,
1208 'tax_group': lambda *a: 'vat',
1209 'ref_tax_sign': lambda *a: 1,
1210 'ref_base_sign': lambda *a: 1,
1211 'tax_sign': lambda *a: 1,
1212 'base_sign': lambda *a: 1,
1213 'include_base_amount': lambda *a: False,
1214 'company_id': _default_company,
1218 def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1221 if tax.applicable_type=='code':
1222 localdict = {'price_unit':price_unit, 'address':self.pool.get('res.partner.address').browse(cr, uid, address_id), 'product':product, 'partner':partner}
1223 exec tax.python_applicable in localdict
1224 if localdict.get('result', False):
1230 def _unit_compute(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1231 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1234 cur_price_unit=price_unit
1236 # we compute the amount for the current tax object and append it to the result
1238 if tax.type=='percent':
1239 amount = cur_price_unit * tax.amount
1240 res.append({'id':tax.id,
1243 'account_collected_id':tax.account_collected_id.id,
1244 'account_paid_id':tax.account_paid_id.id,
1245 'base_code_id': tax.base_code_id.id,
1246 'ref_base_code_id': tax.ref_base_code_id.id,
1247 'sequence': tax.sequence,
1248 'base_sign': tax.base_sign,
1249 'tax_sign': tax.tax_sign,
1250 'ref_base_sign': tax.ref_base_sign,
1251 'ref_tax_sign': tax.ref_tax_sign,
1252 'price_unit': cur_price_unit,
1253 'tax_code_id': tax.tax_code_id.id,
1254 'ref_tax_code_id': tax.ref_tax_code_id.id,
1257 elif tax.type=='fixed':
1258 res.append({'id':tax.id,
1260 'amount':tax.amount,
1261 'account_collected_id':tax.account_collected_id.id,
1262 'account_paid_id':tax.account_paid_id.id,
1263 'base_code_id': tax.base_code_id.id,
1264 'ref_base_code_id': tax.ref_base_code_id.id,
1265 'sequence': tax.sequence,
1266 'base_sign': tax.base_sign,
1267 'tax_sign': tax.tax_sign,
1268 'ref_base_sign': tax.ref_base_sign,
1269 'ref_tax_sign': tax.ref_tax_sign,
1271 'tax_code_id': tax.tax_code_id.id,
1272 'ref_tax_code_id': tax.ref_tax_code_id.id,})
1273 elif tax.type=='code':
1274 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1275 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1276 exec tax.python_compute in localdict
1277 amount = localdict['result']
1282 'account_collected_id': tax.account_collected_id.id,
1283 'account_paid_id': tax.account_paid_id.id,
1284 'base_code_id': tax.base_code_id.id,
1285 'ref_base_code_id': tax.ref_base_code_id.id,
1286 'sequence': tax.sequence,
1287 'base_sign': tax.base_sign,
1288 'tax_sign': tax.tax_sign,
1289 'ref_base_sign': tax.ref_base_sign,
1290 'ref_tax_sign': tax.ref_tax_sign,
1291 'price_unit': cur_price_unit,
1292 'tax_code_id': tax.tax_code_id.id,
1293 'ref_tax_code_id': tax.ref_tax_code_id.id,
1295 amount2 = res[-1]['amount']
1296 if len(tax.child_ids):
1297 if tax.child_depend:
1300 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner)
1301 res.extend(child_tax)
1302 if tax.include_base_amount:
1303 cur_price_unit+=amount2
1306 def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1309 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1313 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1314 one tax for each tax id in IDS and their childs
1316 res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner)
1318 r['amount'] *= quantity
1321 def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1322 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1326 cur_price_unit=price_unit
1328 # we compute the amount for the current tax object and append it to the result
1330 if tax.type=='percent':
1331 amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
1332 res.append({'id':tax.id,
1335 'account_collected_id':tax.account_collected_id.id,
1336 'account_paid_id':tax.account_paid_id.id,
1337 'base_code_id': tax.base_code_id.id,
1338 'ref_base_code_id': tax.ref_base_code_id.id,
1339 'sequence': tax.sequence,
1340 'base_sign': tax.base_sign,
1341 'tax_sign': tax.tax_sign,
1342 'ref_base_sign': tax.ref_base_sign,
1343 'ref_tax_sign': tax.ref_tax_sign,
1344 'price_unit': cur_price_unit - amount,
1345 'tax_code_id': tax.tax_code_id.id,
1346 'ref_tax_code_id': tax.ref_tax_code_id.id,})
1348 elif tax.type=='fixed':
1349 res.append({'id':tax.id,
1351 'amount':tax.amount,
1352 'account_collected_id':tax.account_collected_id.id,
1353 'account_paid_id':tax.account_paid_id.id,
1354 'base_code_id': tax.base_code_id.id,
1355 'ref_base_code_id': tax.ref_base_code_id.id,
1356 'sequence': tax.sequence,
1357 'base_sign': tax.base_sign,
1358 'tax_sign': tax.tax_sign,
1359 'ref_base_sign': tax.ref_base_sign,
1360 'ref_tax_sign': tax.ref_tax_sign,
1362 'tax_code_id': tax.tax_code_id.id,
1363 'ref_tax_code_id': tax.ref_tax_code_id.id,})
1365 elif tax.type=='code':
1366 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1367 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1368 exec tax.python_compute_inv in localdict
1369 amount = localdict['result']
1374 'account_collected_id': tax.account_collected_id.id,
1375 'account_paid_id': tax.account_paid_id.id,
1376 'base_code_id': tax.base_code_id.id,
1377 'ref_base_code_id': tax.ref_base_code_id.id,
1378 'sequence': tax.sequence,
1379 'base_sign': tax.base_sign,
1380 'tax_sign': tax.tax_sign,
1381 'ref_base_sign': tax.ref_base_sign,
1382 'ref_tax_sign': tax.ref_tax_sign,
1383 'price_unit': cur_price_unit - amount,
1384 'tax_code_id': tax.tax_code_id.id,
1385 'ref_tax_code_id': tax.ref_tax_code_id.id,
1388 amount2 = res[-1]['amount']
1389 if len(tax.child_ids):
1390 if tax.child_depend:
1395 for t in tax.child_ids:
1396 parent_tax = self._unit_compute_inv(cr, uid, [t], amount, address_id, product, partner)
1397 res.extend(parent_tax)
1398 if tax.include_base_amount:
1399 cur_price_unit-=amount
1403 def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1405 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1406 Price Unit is a VAT included price
1410 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1411 one tax for each tax id in IDS and their childs
1413 res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
1415 r['amount'] *= quantity
1419 # ---------------------------------------------------------
1420 # Account Entries Models
1421 # ---------------------------------------------------------
1423 class account_model(osv.osv):
1424 _name = "account.model"
1425 _description = "Account Model"
1427 'name': fields.char('Model Name', size=64, required=True, help="This is a model for recurring accounting entries"),
1428 'ref': fields.char('Ref', size=64),
1429 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
1430 'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'),
1431 'legend' :fields.text('Legend',readonly=True,size=100),
1435 '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''',
1440 class account_model_line(osv.osv):
1441 _name = "account.model.line"
1442 _description = "Account Model Entries"
1444 'name': fields.char('Name', size=64, required=True),
1445 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the resources from the lowest sequences to the higher ones"),
1446 'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity on entries"),
1447 'debit': fields.float('Debit', digits=(16,2)),
1448 'credit': fields.float('Credit', digits=(16,2)),
1450 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"),
1452 'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True),
1454 'ref': fields.char('Ref.', size=16),
1456 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."),
1457 'currency_id': fields.many2one('res.currency', 'Currency'),
1459 'partner_id': fields.many2one('res.partner', 'Partner Ref.'),
1460 '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."),
1461 'date': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Current Date', required=True, help="The date of the generated entries"),
1464 'date': lambda *a: 'today'
1467 _sql_constraints = [
1468 ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in model !'),
1469 ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model !'),
1471 account_model_line()
1473 # ---------------------------------------------------------
1474 # Account Subscription
1475 # ---------------------------------------------------------
1478 class account_subscription(osv.osv):
1479 _name = "account.subscription"
1480 _description = "Account Subscription"
1482 'name': fields.char('Name', size=64, required=True),
1483 'ref': fields.char('Ref.', size=16),
1484 'model_id': fields.many2one('account.model', 'Model', required=True),
1486 'date_start': fields.date('Starting date', required=True),
1487 'period_total': fields.integer('Number of period', required=True),
1488 'period_nbr': fields.integer('Period', required=True),
1489 'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True),
1490 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status', required=True, readonly=True),
1492 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines')
1495 'date_start': lambda *a: time.strftime('%Y-%m-%d'),
1496 'period_type': lambda *a: 'month',
1497 'period_total': lambda *a: 12,
1498 'period_nbr': lambda *a: 1,
1499 'state': lambda *a: 'draft',
1501 def state_draft(self, cr, uid, ids, context={}):
1502 self.write(cr, uid, ids, {'state':'draft'})
1505 def check(self, cr, uid, ids, context={}):
1507 for sub in self.browse(cr, uid, ids, context):
1509 for line in sub.lines_id:
1510 if not line.move_id.id:
1514 todone.append(sub.id)
1516 self.write(cr, uid, todone, {'state':'done'})
1519 def remove_line(self, cr, uid, ids, context={}):
1521 for sub in self.browse(cr, uid, ids, context):
1522 for line in sub.lines_id:
1523 if not line.move_id.id:
1524 toremove.append(line.id)
1526 self.pool.get('account.subscription.line').unlink(cr, uid, toremove)
1527 self.write(cr, uid, ids, {'state':'draft'})
1530 def compute(self, cr, uid, ids, context={}):
1531 for sub in self.browse(cr, uid, ids, context):
1533 for i in range(sub.period_total):
1534 self.pool.get('account.subscription.line').create(cr, uid, {
1536 'subscription_id': sub.id,
1538 if sub.period_type=='day':
1539 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(days=sub.period_nbr)).strftime('%Y-%m-%d')
1540 if sub.period_type=='month':
1541 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(months=sub.period_nbr)).strftime('%Y-%m-%d')
1542 if sub.period_type=='year':
1543 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(years=sub.period_nbr)).strftime('%Y-%m-%d')
1544 self.write(cr, uid, ids, {'state':'running'})
1546 account_subscription()
1548 class account_subscription_line(osv.osv):
1549 _name = "account.subscription.line"
1550 _description = "Account Subscription Line"
1552 'subscription_id': fields.many2one('account.subscription', 'Subscription', required=True, select=True),
1553 'date': fields.date('Date', required=True),
1554 'move_id': fields.many2one('account.move', 'Entry'),
1558 def move_create(self, cr, uid, ids, context={}):
1560 for line in self.browse(cr, uid, ids, context):
1564 ids = self.pool.get('account.model').generate(cr, uid, [line.subscription_id.model_id.id], datas, context)
1565 tocheck[line.subscription_id.id] = True
1566 self.write(cr, uid, [line.id], {'move_id':ids[0]})
1568 self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context)
1571 account_subscription_line()
1574 class account_config_wizard(osv.osv_memory):
1575 _name = 'account.config.wizard'
1577 def _get_charts(self, cr, uid, context):
1578 module_obj=self.pool.get('ir.module.module')
1579 ids=module_obj.search(cr, uid, [('category_id', '=', 'Account Charts'), ('state', '<>', 'installed')])
1580 res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)]
1581 res.append((-1, 'None'))
1582 res.sort(lambda x,y: cmp(x[1],y[1]))
1586 'name':fields.char('Name', required=True, size=64, help="Name of the fiscal year as displayed on screens."),
1587 'code':fields.char('Code', required=True, size=64, help="Name of the fiscal year as displayed in reports."),
1588 'date1': fields.date('Starting Date', required=True),
1589 'date2': fields.date('Ending Date', required=True),
1590 'period':fields.selection([('month','Month'),('3months','3 Months')], 'Periods', required=True),
1591 'charts' : fields.selection(_get_charts, 'Charts of Account',required=True)
1594 'code': lambda *a: time.strftime('%Y'),
1595 'name': lambda *a: time.strftime('%Y'),
1596 'date1': lambda *a: time.strftime('%Y-01-01'),
1597 'date2': lambda *a: time.strftime('%Y-12-31'),
1598 'period':lambda *a:'month',
1600 def action_cancel(self,cr,uid,ids,conect=None):
1602 'view_type': 'form',
1603 "view_mode": 'form',
1604 'res_model': 'ir.actions.configuration.wizard',
1605 'type': 'ir.actions.act_window',
1609 def install_account_chart(self, cr, uid,ids, context=None):
1610 for res in self.read(cr,uid,ids):
1613 mod_obj = self.pool.get('ir.module.module')
1614 mod_obj.write(cr , uid, [id] ,{'state' : 'to install'})
1615 mod_obj.download(cr, uid, [id], context=context)
1617 cr.execute("select m.id as id from ir_module_module_dependency d inner join ir_module_module m on (m.name=d.name) where d.module_id=%d and m.state='uninstalled'",(id,))
1623 mod_obj.write(cr , uid, [id] ,{'state' : 'to install'})
1624 mod_obj.download(cr, uid, [id], context=context)
1629 db, pool = pooler.restart_pool(cr.dbname, update_module=True)
1631 def action_create(self, cr, uid,ids, context=None):
1632 for res in self.read(cr,uid,ids):
1633 if 'date1' in res and 'date2' in res:
1634 res_obj = self.pool.get('account.fiscalyear')
1635 start_date=res['date1']
1636 end_date=res['date2']
1637 name=res['name']#DateTime.strptime(start_date, '%Y-%m-%d').strftime('%m.%Y') + '-' + DateTime.strptime(end_date, '%Y-%m-%d').strftime('%m.%Y')
1641 'date_start':start_date,
1642 'date_stop':end_date,
1644 new_id=res_obj.create(cr, uid, vals, context=context)
1645 if res['period']=='month':
1646 res_obj.create_period(cr,uid,[new_id])
1647 elif res['period']=='3months':
1648 res_obj.create_period3(cr,uid,[new_id])
1649 self.install_account_chart(cr,uid,ids)
1651 'view_type': 'form',
1652 "view_mode": 'form',
1653 'res_model': 'ir.actions.configuration.wizard',
1654 'type': 'ir.actions.act_window',
1660 account_config_wizard()
1663 # ---------------------------------------------------------------
1664 # Account Templates : Account, Tax, Tax Code and chart. + Wizard
1665 # ---------------------------------------------------------------
1667 class account_tax_template(osv.osv):
1668 _name = 'account.tax.template'
1669 account_tax_template()
1671 class account_account_template(osv.osv):
1673 _name = "account.account.template"
1674 _description ='Templates for Accounts'
1677 'name': fields.char('Name', size=128, required=True, select=True),
1678 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
1679 'code': fields.char('Code', size=64),
1680 'type': fields.selection([
1681 ('receivable','Receivable'),
1682 ('payable','Payable'),
1684 ('consolidation','Consolidation'),
1686 ('closed','Closed'),
1687 ], 'Internal Type', required=True,),
1688 'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
1689 'reconcile': fields.boolean('Allow Reconciliation', help="Check this option if the user can make a reconciliation of the entries in this account."),
1690 'shortcut': fields.char('Shortcut', size=12),
1691 'note': fields.text('Note'),
1692 'parent_id': fields.many2one('account.account.template','Parent Account Template', ondelete='cascade'),
1693 'child_parent_ids':fields.one2many('account.account.template','parent_id','Children'),
1694 'tax_ids': fields.many2many('account.tax.template', 'account_account_template_tax_rel','account_id','tax_id', 'Default Taxes'),
1698 'reconcile': lambda *a: False,
1699 'type' : lambda *a :'view',
1702 def _check_recursion(self, cr, uid, ids):
1705 cr.execute('select parent_id from account_account_template where id in ('+','.join(map(str,ids))+')')
1706 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1713 (_check_recursion, 'Error ! You can not create recursive account templates.', ['parent_id'])
1717 def name_get(self, cr, uid, ids, context={}):
1720 reads = self.read(cr, uid, ids, ['name','code'], context)
1722 for record in reads:
1723 name = record['name']
1725 name = record['code']+' '+name
1726 res.append((record['id'],name ))
1729 account_account_template()
1731 class account_tax_code_template(osv.osv):
1733 _name = 'account.tax.code.template'
1734 _description = 'Tax Code Template'
1738 'name': fields.char('Tax Case Name', size=64, required=True),
1739 'code': fields.char('Case Code', size=64),
1740 'info': fields.text('Description'),
1741 'parent_id': fields.many2one('account.tax.code.template', 'Parent Code', select=True),
1742 'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Childs Codes'),
1743 'sign': fields.float('Sign for parent', required=True),
1747 'sign': lambda *args: 1.0,
1750 def name_get(self, cr, uid, ids, context=None):
1753 if isinstance(ids, (int, long)):
1755 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1756 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1759 def _check_recursion(self, cr, uid, ids):
1762 cr.execute('select distinct parent_id from account_tax_code_template where id in ('+','.join(map(str,ids))+')')
1763 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1770 (_check_recursion, 'Error ! You can not create recursive Tax Codes.', ['parent_id'])
1772 _order = 'code,name'
1773 account_tax_code_template()
1776 class account_chart_template(osv.osv):
1777 _name="account.chart.template"
1778 _description= "Templates for Account Chart"
1781 'name': fields.char('Name', size=64, required=True),
1782 'account_root_id': fields.many2one('account.account.template','Root Account',required=True,domain=[('parent_id','=',False)]),
1783 'tax_code_root_id': fields.many2one('account.tax.code.template','Root Tax Code',required=True,domain=[('parent_id','=',False)]),
1784 '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'),
1785 'bank_account_view_id': fields.many2one('account.account.template','Bank Account',required=True),
1786 'property_account_receivable': fields.many2one('account.account.template','Receivable Account'),
1787 'property_account_payable': fields.many2one('account.account.template','Payable Account'),
1788 'property_account_expense_categ': fields.many2one('account.account.template','Expense Category Account'),
1789 'property_account_income_categ': fields.many2one('account.account.template','Income Category Account'),
1790 'property_account_expense': fields.many2one('account.account.template','Expense Account on Product Template'),
1791 'property_account_income': fields.many2one('account.account.template','Income Account on Product Template'),
1794 account_chart_template()
1796 class account_tax_template(osv.osv):
1798 _name = 'account.tax.template'
1799 _description = 'Templates for Taxes'
1802 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
1803 'name': fields.char('Tax Name', size=64, required=True),
1804 '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."),
1805 'amount': fields.float('Amount', required=True, digits=(14,4)),
1806 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1807 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1808 '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."),
1809 'account_collected_id':fields.many2one('account.account.template', 'Invoice Tax Account'),
1810 'account_paid_id':fields.many2one('account.account.template', 'Refund Tax Account'),
1811 'parent_id':fields.many2one('account.tax.template', 'Parent Tax Account', select=True),
1812 '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."),
1813 'python_compute':fields.text('Python Code'),
1814 'python_compute_inv':fields.text('Python Code (reverse)'),
1815 'python_applicable':fields.text('Python Code'),
1816 '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."),
1819 # Fields used for the VAT declaration
1821 'base_code_id': fields.many2one('account.tax.code.template', 'Base Code', help="Use this code for the VAT declaration."),
1822 'tax_code_id': fields.many2one('account.tax.code.template', 'Tax Code', help="Use this code for the VAT declaration."),
1823 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1824 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1826 # Same fields for refund invoices
1828 'ref_base_code_id': fields.many2one('account.tax.code.template', 'Refund Base Code', help="Use this code for the VAT declaration."),
1829 'ref_tax_code_id': fields.many2one('account.tax.code.template', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1830 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1831 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1832 '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."),
1833 'description': fields.char('Internal Name', size=32),
1836 def name_get(self, cr, uid, ids, context={}):
1840 for record in self.read(cr, uid, ids, ['description','name'], context):
1841 name = record['description'] and record['description'] or record['name']
1842 res.append((record['id'],name ))
1845 def _default_company(self, cr, uid, context={}):
1846 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1848 return user.company_id.id
1849 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1852 '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''',
1853 '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''',
1854 'applicable_type': lambda *a: 'true',
1855 'type': lambda *a: 'percent',
1856 'amount': lambda *a: 0,
1857 'sequence': lambda *a: 1,
1858 'tax_group': lambda *a: 'vat',
1859 'ref_tax_sign': lambda *a: 1,
1860 'ref_base_sign': lambda *a: 1,
1861 'tax_sign': lambda *a: 1,
1862 'base_sign': lambda *a: 1,
1863 'include_base_amount': lambda *a: False,
1868 account_tax_template()
1870 # Multi charts of Accounts wizard
1872 class wizard_multi_charts_accounts(osv.osv_memory):
1874 Create a new account chart for a company.
1877 * an account chart template
1878 * a number of digits for formatting code of non-view accounts
1879 * a list of bank account owned by the company
1881 * generates all accounts from the template and assign them to the right company
1882 * generates all taxes and tax codes, changing account assignations
1883 * generates all accounting properties and assign correctly
1885 _name='wizard.multi.charts.accounts'
1888 'company_id':fields.many2one('res.company','Company',required=True),
1889 'chart_template_id': fields.many2one('account.chart.template','Chart Template',required=True),
1890 'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Bank Accounts',required=True),
1891 'code_digits':fields.integer('# of Digits',required=True,help="No. of Digits to use for account code"),
1894 def _get_chart(self, cr, uid, context={}):
1895 ids = self.pool.get('account.chart.template').search(cr, uid, [], context=context)
1900 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr,uid,[uid],c)[0].company_id.id,
1901 'chart_template_id': _get_chart,
1902 'code_digits': lambda *a:6,
1905 def action_create(self, cr, uid, ids, context=None):
1906 obj_multi = self.browse(cr,uid,ids[0])
1907 obj_acc = self.pool.get('account.account')
1908 obj_acc_tax = self.pool.get('account.tax')
1909 obj_journal = self.pool.get('account.journal')
1910 obj_acc_template = self.pool.get('account.account.template')
1913 obj_acc_root = obj_multi.chart_template_id.account_root_id
1914 tax_code_root_id = obj_multi.chart_template_id.tax_code_root_id.id
1915 company_id = obj_multi.company_id.id
1918 acc_template_ref = {}
1919 tax_template_ref = {}
1920 tax_code_template_ref = {}
1923 #create all the tax code
1924 children_tax_code_template = self.pool.get('account.tax.code.template').search(cr, uid, [('parent_id','child_of',[tax_code_root_id])], order='id')
1925 for tax_code_template in self.pool.get('account.tax.code.template').browse(cr, uid, children_tax_code_template):
1927 'name': (tax_code_root_id == tax_code_template.id) and obj_multi.company_id.name or tax_code_template.name,
1928 'code': tax_code_template.code,
1929 'info': tax_code_template.info,
1930 'parent_id': tax_code_template.parent_id and ((tax_code_template.parent_id.id in tax_code_template_ref) and tax_code_template_ref[tax_code_template.parent_id.id]) or False,
1931 'company_id': company_id,
1932 'sign': tax_code_template.sign,
1934 new_tax_code = self.pool.get('account.tax.code').create(cr,uid,vals)
1935 #recording the new tax code to do the mapping
1936 tax_code_template_ref[tax_code_template.id] = new_tax_code
1939 for tax in obj_multi.chart_template_id.tax_template_ids:
1943 'sequence': tax.sequence,
1944 'amount':tax.amount,
1946 'applicable_type': tax.applicable_type,
1947 'domain':tax.domain,
1948 'parent_id': tax.parent_id and ((tax.parent_id.id in tax_template_ref) and tax_template_ref[tax.parent_id.id]) or False,
1949 'child_depend': tax.child_depend,
1950 'python_compute': tax.python_compute,
1951 'python_compute_inv': tax.python_compute_inv,
1952 'python_applicable': tax.python_applicable,
1953 'tax_group':tax.tax_group,
1954 'base_code_id': tax.base_code_id and tax_code_template_ref[tax.base_code_id.id] or False,
1955 'tax_code_id': tax.tax_code_id and tax_code_template_ref[tax.tax_code_id.id] or False,
1956 'base_sign': tax.base_sign,
1957 'tax_sign': tax.tax_sign,
1958 'ref_base_code_id': tax.ref_base_code_id and tax_code_template_ref[tax.ref_base_code_id.id] or False,
1959 'ref_tax_code_id': tax.ref_tax_code_id and tax_code_template_ref[tax.ref_tax_code_id.id] or False,
1960 'ref_base_sign': tax.ref_base_sign,
1961 'ref_tax_sign': tax.ref_tax_sign,
1962 'include_base_amount': tax.include_base_amount,
1963 'description':tax.description,
1964 'company_id': company_id,
1966 new_tax = obj_acc_tax.create(cr,uid,vals_tax)
1967 #as the accounts have not been created yet, we have to wait before filling these fields
1968 todo_dict[new_tax] = {
1969 'account_collected_id': tax.account_collected_id and tax.account_collected_id.id or False,
1970 'account_paid_id': tax.account_paid_id and tax.account_paid_id.id or False,
1972 tax_template_ref[tax.id] = new_tax
1974 #deactivate the parent_store functionnality on account_account for rapidity purpose
1975 self.pool._init = True
1977 children_acc_template = obj_acc_template.search(cr, uid, [('parent_id','child_of',[obj_acc_root.id])])
1978 children_acc_template.sort()
1979 for account_template in obj_acc_template.browse(cr, uid, children_acc_template):
1981 for tax in account_template.tax_ids:
1982 tax_ids.append(tax_template_ref[tax.id])
1983 #create the account_account
1985 dig = obj_multi.code_digits
1986 code_main = len(account_template.code)
1987 code_acc = account_template.code
1988 if code_main<=dig and account_template.type != 'view':
1989 code_acc=str(code_acc) + (str('0'*(dig-code_main)))
1991 'name': (obj_acc_root.id == account_template.id) and obj_multi.company_id.name or account_template.name,
1992 #'sign': account_template.sign,
1993 'currency_id': account_template.currency_id and account_template.currency_id.id or False,
1995 'type': account_template.type,
1996 'user_type': account_template.user_type and account_template.user_type.id or False,
1997 'reconcile': account_template.reconcile,
1998 'shortcut': account_template.shortcut,
1999 'note': account_template.note,
2000 'parent_id': account_template.parent_id and ((account_template.parent_id.id in acc_template_ref) and acc_template_ref[account_template.parent_id.id]) or False,
2001 'tax_ids': [(6,0,tax_ids)],
2002 'company_id': company_id,
2004 new_account = obj_acc.create(cr,uid,vals)
2005 acc_template_ref[account_template.id] = new_account
2006 #reactivate the parent_store functionnality on account_account
2007 self.pool._init = False
2008 self.pool.get('account.account')._parent_store_compute(cr)
2010 for key,value in todo_dict.items():
2011 if value['account_collected_id'] or value['account_paid_id']:
2012 obj_acc_tax.write(cr, uid, [key], vals={
2013 'account_collected_id': acc_template_ref[value['account_collected_id']],
2014 'account_paid_id': acc_template_ref[value['account_paid_id']],
2019 view_id = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Journal View')])[0]
2020 seq_id = self.pool.get('ir.sequence').search(cr,uid,[('code','=','account.journal')])[0]
2021 seq_code = self.pool.get('ir.sequence').get(cr, uid, 'account.journal')
2023 vals_journal['view_id']=view_id
2024 vals_journal['sequence_id']=seq_id
2027 vals_journal['name'] = _('Sales Journal')
2028 vals_journal['type'] = 'sale'
2029 vals_journal['code'] = _('SAJ')
2031 if obj_multi.chart_template_id.property_account_receivable:
2032 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_receivable.id]
2033 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_receivable.id]
2035 obj_journal.create(cr,uid,vals_journal)
2038 vals_journal['name']=_('Purchase Journal')
2039 vals_journal['type']='purchase'
2040 vals_journal['code']=_('EXJ')
2042 if obj_multi.chart_template_id.property_account_payable:
2043 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_payable.id]
2044 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_payable.id]
2046 obj_journal.create(cr,uid,vals_journal)
2049 view_id_cash = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Cash Journal View')])[0]
2050 view_id_cur = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Multi-Currency Cash Journal View')])[0]
2051 ref_acc_bank = obj_multi.chart_template_id.bank_account_view_id
2054 for line in obj_multi.bank_accounts_id:
2055 #create the account_account for this bank journal
2056 tmp = self.pool.get('res.partner.bank').name_get(cr, uid, [line.acc_no.id])[0][1]
2057 dig = obj_multi.code_digits
2059 'name': line.acc_no.bank and line.acc_no.bank.name+' '+tmp or tmp,
2060 'currency_id': line.currency_id and line.currency_id.id or False,
2061 'code': str(int(ref_acc_bank.code.ljust(dig,'0')) + current_num),
2063 'user_type': account_template.user_type and account_template.user_type.id or False,
2065 'parent_id': acc_template_ref[ref_acc_bank.id] or False,
2066 'company_id': company_id,
2068 acc_cash_id = obj_acc.create(cr,uid,vals)
2070 #create the bank journal
2071 vals_journal['name']= vals['name']
2072 vals_journal['code']= _('BNK') + str(current_num)
2073 vals_journal['sequence_id'] = seq_id
2074 vals_journal['type'] = 'cash'
2075 if line.currency_id:
2076 vals_journal['view_id'] = view_id_cur
2077 vals_journal['currency'] = line.currency_id.id
2079 vals_journal['view_id'] = view_id_cash
2080 vals_journal['default_credit_account_id'] = acc_cash_id
2081 vals_journal['default_debit_account_id']= acc_cash_id
2082 obj_journal.create(cr,uid,vals_journal)
2086 #create the properties
2087 property_obj = self.pool.get('ir.property')
2088 fields_obj = self.pool.get('ir.model.fields')
2091 ('property_account_receivable','res.partner','account.account'),
2092 ('property_account_payable','res.partner','account.account'),
2093 ('property_account_expense_categ','product.category','account.account'),
2094 ('property_account_income_categ','product.category','account.account'),
2095 ('property_account_expense','product.template','account.account'),
2096 ('property_account_income','product.template','account.account')
2098 for record in todo_list:
2100 r = property_obj.search(cr, uid, [('name','=', record[0] ),('company_id','=',company_id)])
2101 account = getattr(obj_multi.chart_template_id, record[0])
2102 field = fields_obj.search(cr, uid, [('name','=',record[0]),('model','=',record[1]),('relation','=',record[2])])
2105 'company_id': company_id,
2106 'fields_id': field[0],
2107 'value': account and 'account.account,'+str(acc_template_ref[account.id]) or False,
2110 #the property exist: modify it
2111 property_obj.write(cr, uid, r, vals)
2113 #create the property
2114 property_obj.create(cr, uid, vals)
2117 'view_type': 'form',
2118 "view_mode": 'form',
2119 'res_model': 'ir.actions.configuration.wizard',
2120 'type': 'ir.actions.act_window',
2123 def action_cancel(self,cr,uid,ids,conect=None):
2125 'view_type': 'form',
2126 "view_mode": 'form',
2127 'res_model': 'ir.actions.configuration.wizard',
2128 'type': 'ir.actions.act_window',
2133 wizard_multi_charts_accounts()
2135 class account_bank_accounts_wizard(osv.osv_memory):
2136 _name='account.bank.accounts.wizard'
2139 'acc_no':fields.many2one('res.partner.bank','Account No.',required=True),
2140 'bank_account_id':fields.many2one('wizard.multi.charts.accounts', 'Bank Account', required=True),
2141 'currency_id':fields.many2one('res.currency', 'Currency'),
2144 account_bank_accounts_wizard()
2146 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: