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,
55 def compute(self, cr, uid, id, value, date_ref=False, context={}):
57 date_ref = now().strftime('%Y-%m-%d')
58 pt = self.browse(cr, uid, id, context)
61 for line in pt.line_ids:
62 if line.value=='fixed':
63 amt = round(line.value_amount, 2)
64 elif line.value=='procent':
65 amt = round(value * line.value_amount, 2)
66 elif line.value=='balance':
67 amt = round(amount, 2)
69 next_date = mx.DateTime.strptime(date_ref, '%Y-%m-%d') + RelativeDateTime(days=line.days)
71 next_date += RelativeDateTime(day=line.days2)
73 next_date += RelativeDateTime(day=line.days2, months=1)
74 result.append( (next_date.strftime('%Y-%m-%d'), amt) )
78 account_payment_term()
80 class account_payment_term_line(osv.osv):
81 _name = "account.payment.term.line"
82 _description = "Payment Term Line"
84 'name': fields.char('Line Name', size=32,required=True),
85 '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"),
86 'value': fields.selection([('procent','Percent'),('balance','Balance'),('fixed','Fixed Amount')], 'Value',required=True),
87 'value_amount': fields.float('Value Amount'),
88 'days': fields.integer('Number of Days',required=True, help="Number of days to add before computation of the day of month."),
89 '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."),
90 'payment_id': fields.many2one('account.payment.term','Payment Term', required=True, select=True),
93 'value': lambda *a: 'balance',
94 'sequence': lambda *a: 5,
95 'days2': lambda *a: 0,
98 account_payment_term_line()
101 class account_account_type(osv.osv):
102 _name = "account.account.type"
103 _description = "Account Type"
105 'name': fields.char('Acc. Type Name', size=64, required=True, translate=True),
106 'code': fields.char('Code', size=32, required=True),
107 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of account types."),
108 'partner_account': fields.boolean('Partner account'),
109 'close_method': fields.selection([('none','None'), ('balance','Balance'), ('detail','Detail'),('unreconciled','Unreconciled')], 'Deferral Method', required=True),
110 '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.'),
113 'close_method': lambda *a: 'none',
114 'sequence': lambda *a: 5,
115 'sign': lambda *a: 1,
118 account_account_type()
120 def _code_get(self, cr, uid, context={}):
121 acc_type_obj = self.pool.get('account.account.type')
122 ids = acc_type_obj.search(cr, uid, [])
123 res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context)
124 return [(r['code'], r['name']) for r in res]
126 #----------------------------------------------------------
128 #----------------------------------------------------------
130 class account_tax(osv.osv):
131 _name = 'account.tax'
134 class account_account(osv.osv):
135 _order = "parent_left"
136 _parent_order = "code"
137 _name = "account.account"
138 _description = "Account"
141 def search(self, cr, uid, args, offset=0, limit=None, order=None,
142 context=None, count=False):
149 if args[pos][0]=='code' and args[pos][1] in ('like','ilike') and args[pos][2]:
150 args[pos] = ('code', '=like', str(args[pos][2].replace('%',''))+'%')
151 if args[pos][0]=='journal_id':
155 jour = self.pool.get('account.journal').browse(cr, uid, args[pos][2])
156 if (not (jour.account_control_ids or jour.type_control_ids)) or not args[pos][2]:
159 ids3 = map(lambda x: x.code, jour.type_control_ids)
160 ids1 = super(account_account,self).search(cr, uid, [('type','in',ids3)])
161 ids1 += map(lambda x: x.id, jour.account_control_ids)
162 args[pos] = ('id','in',ids1)
165 return super(account_account,self).search(cr, uid, args, offset, limit,
166 order, context=context, count=count)
168 def _get_children_and_consol(self, cr, uid, ids, context={}):
169 #this function search for all the children and all consolidated children (recursively) of the given account ids
170 res = self.search(cr, uid, [('parent_id', 'child_of', ids)])
172 this = self.browse(cr, uid, id, context)
173 for child in this.child_consol_ids:
174 if child.id not in res:
176 if len(res) != len(ids):
177 return self._get_children_and_consol(cr, uid, res, context)
180 def __compute(self, cr, uid, ids, field_names, arg, context={}, query=''):
181 #compute the balance/debit/credit accordingly to the value of field_name for the given account ids
183 'balance': "COALESCE(SUM(l.debit) - SUM(l.credit), 0) as balance ",
184 'debit': "COALESCE(SUM(l.debit), 0) as debit ",
185 'credit': "COALESCE(SUM(l.credit), 0) as credit "
187 #get all the necessary accounts
188 ids2 = self._get_children_and_consol(cr, uid, ids, context)
189 acc_set = ",".join(map(str, ids2))
190 #compute for each account the balance/debit/credit from the move lines
193 query = self.pool.get('account.move.line')._query_get(cr, uid,
195 cr.execute(("SELECT l.account_id as id, " +\
196 ' , '.join(map(lambda x: mapping[x], field_names)) +
198 "account_move_line l " \
200 "l.account_id IN (%s) " \
201 "AND " + query + " " \
202 "GROUP BY l.account_id") % (acc_set, ))
204 for res in cr.dictfetchall():
205 accounts[res['id']] = res
207 #for the asked accounts, get from the dictionnary 'accounts' the value of it
210 res[id] = self._get_account_values(cr, uid, id, accounts, field_names, context)
213 def _get_account_values(self, cr, uid, id, accounts, field_names, context={}):
214 res = {}.fromkeys(field_names, 0.0)
215 browse_rec = self.browse(cr, uid, id)
216 if browse_rec.type == 'consolidation':
217 ids2 = self.read(cr, uid, [browse_rec.id], ['child_consol_ids'], context)[0]['child_consol_ids']
218 for t in self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])]):
219 if t not in ids2 and t != browse_rec.id:
222 tmp = self._get_account_values(cr, uid, i, accounts, field_names, context)
223 for a in field_names:
226 ids2 = self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])])
228 for a in field_names:
229 res[a] += accounts.get(i, {}).get(a, 0.0)
232 def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}):
234 for rec in self.browse(cr, uid, ids, context):
235 result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code)
238 def _get_child_ids(self, cr, uid, ids, field_name, arg, context={}):
240 for record in self.browse(cr, uid, ids, context):
241 if record.child_parent_ids:
242 result[record.id]=[x.id for x in record.child_parent_ids]
246 if record.child_consol_ids:
247 for acc in record.child_consol_ids:
248 result[record.id].append(acc.id)
253 'name': fields.char('Name', size=128, required=True, select=True),
254 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
255 'code': fields.char('Code', size=64, required=True),
256 'type': fields.selection([
257 ('receivable','Receivable'),
258 ('payable','Payable'),
260 ('consolidation','Consolidation'),
263 ], 'Internal Type', required=True,),
265 'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
266 'parent_id': fields.many2one('account.account','Parent', ondelete='cascade'),
267 'child_parent_ids':fields.one2many('account.account','parent_id','Children'),
268 'child_consol_ids':fields.many2many('account.account', 'account_account_consol_rel', 'child_id', 'parent_id', 'Consolidated Children'),
269 'child_id': fields.function(_get_child_ids, method=True, type='many2many',relation="account.account",string="Children Accounts"),
270 'balance': fields.function(__compute, digits=(16,2), method=True, string='Balance', multi='balance'),
271 'credit': fields.function(__compute, digits=(16,2), method=True, string='Credit', multi='balance'),
272 'debit': fields.function(__compute, digits=(16,2), method=True, string='Debit', multi='balance'),
273 'reconcile': fields.boolean('Reconcile', help="Check this account if the user can make a reconciliation of the entries in this account."),
274 'shortcut': fields.char('Shortcut', size=12),
275 'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel',
276 'account_id','tax_id', 'Default Taxes'),
277 'note': fields.text('Note'),
278 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Company Currency'),
279 'company_id': fields.many2one('res.company', 'Company', required=True),
280 'active': fields.boolean('Active', select=2),
282 'parent_left': fields.integer('Parent Left', select=1),
283 'parent_right': fields.integer('Parent Right', select=1),
284 'currency_mode': fields.selection([('current','At Date'),('average','Average Rate')], 'Outgoing Currencies Rate',
286 'This will select how is computed the current currency rate for outgoing transactions. '\
287 'In most countries the legal method is "average" but only a few softwares are able to '\
288 'manage this. So if you import from another software, you may have to use the rate at date. ' \
289 'Incoming transactions, always use the rate at date.', \
291 'check_history': fields.boolean('Display History',
292 help="Check this box if you want to print all entries when printing the General Ledger, "\
293 "otherwise it will only print its balance.")
296 def _default_company(self, cr, uid, context={}):
297 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
299 return user.company_id.id
300 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
303 'type' : lambda *a :'view',
304 'reconcile': lambda *a: False,
305 'company_id': _default_company,
306 'active': lambda *a: True,
307 'check_history': lambda *a: True,
308 'currency_mode': lambda *a: 'current'
311 def _check_recursion(self, cr, uid, ids):
312 obj_self=self.browse(cr,uid,ids[0])
313 p_id=obj_self.parent_id and obj_self.parent_id.id
314 if (obj_self in obj_self.child_consol_ids) or (p_id and (p_id is obj_self.id)):
317 cr.execute('select distinct child_id from account_account_consol_rel where parent_id in ('+','.join(map(str,ids))+')')
318 child_ids = filter(None, map(lambda x:x[0], cr.fetchall()))
320 if (p_id and (p_id in c_ids)) or (obj_self.id in c_ids):
323 s_ids=self.search(cr,uid,[('parent_id','in',c_ids)])
324 if p_id and (p_id in s_ids):
331 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
333 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
341 if name and str(name).startswith('partner:'):
342 part_id = int(name.split(':')[1])
343 part = self.pool.get('res.partner').browse(cr, user, part_id, context)
344 args += [('id','in', (part.property_account_payable.id, part.property_account_receivable.id))]
346 if name and str(name).startswith('type:'):
347 type = name.split(':')[1]
348 args += [('type','=', type)]
353 ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit)
355 ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit)
357 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
359 ids = self.search(cr, user, args, context=context, limit=limit)
360 return self.name_get(cr, user, ids, context=context)
362 def name_get(self, cr, uid, ids, context={}):
365 reads = self.read(cr, uid, ids, ['name','code'], context)
368 name = record['name']
370 name = record['code']+' '+name
371 res.append((record['id'],name ))
374 def copy(self, cr, uid, id, default={}, context={},done_list=[]):
375 account = self.browse(cr, uid, id, context=context)
379 default=default.copy()
380 default['parent_id'] = False
381 if account.id in done_list:
383 done_list.append(account.id)
385 for child in account.child_id:
386 child_ids=self.copy(cr, uid, child.id, default, context=context,done_list=done_list)
388 new_child_ids.append(child_ids)
389 default['child_parent_ids'] = [(6, 0, new_child_ids)]
391 default['child_parent_ids'] = False
392 return super(account_account, self).copy(cr, uid, id, default, context=context)
394 def write(self, cr, uid, ids, vals, context=None):
397 if 'active' in vals and not vals['active']:
398 line_obj = self.pool.get('account.move.line')
399 account_ids = self.search(cr, uid, [('id', 'child_of', ids)])
400 if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]):
401 raise osv.except_osv(_('Error !'), _('You can not deactivate an account that contains account moves.'))
402 return super(account_account, self).write(cr, uid, ids, vals, context=context)
405 class account_journal_view(osv.osv):
406 _name = "account.journal.view"
407 _description = "Journal View"
409 'name': fields.char('Journal View', size=64, required=True),
410 'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
413 account_journal_view()
416 class account_journal_column(osv.osv):
417 def _col_get(self, cr, user, context={}):
419 cols = self.pool.get('account.move.line')._columns
421 result.append( (col, cols[col].string) )
424 _name = "account.journal.column"
425 _description = "Journal Column"
427 'name': fields.char('Column Name', size=64, required=True),
428 'field': fields.selection(_col_get, 'Field Name', method=True, required=True, size=32),
429 'view_id': fields.many2one('account.journal.view', 'Journal View', select=True),
430 'sequence': fields.integer('Sequence'),
431 'required': fields.boolean('Required'),
432 'readonly': fields.boolean('Readonly'),
435 account_journal_column()
437 class account_journal(osv.osv):
438 _name = "account.journal"
439 _description = "Journal"
441 'name': fields.char('Journal Name', size=64, required=True, translate=True),
442 'code': fields.char('Code', size=16),
443 'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True),
445 'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]),
446 'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]),
448 'active': fields.boolean('Active'),
449 '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."),
450 'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account'),
451 'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account'),
452 '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."),
453 'update_posted': fields.boolean('Allow Cancelling Entries'),
454 'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="The sequence gives the display order for a list of journals", required=True),
455 'user_id': fields.many2one('res.users', 'User', help="The responsible user of this journal"),
456 'groups_id': fields.many2many('res.groups', 'account_journal_group_rel', 'journal_id', 'group_id', 'Groups'),
457 'currency': fields.many2one('res.currency', 'Currency', help='The currency used to enter statement'),
458 'entry_posted': fields.boolean('Skip \'Draft\' State for Created Entries', help='Check this box if you don\'t want that new account moves pass through the \'draft\' state and goes direclty to the \'posted state\' without any manual validation.'),
461 'active': lambda *a: 1,
462 'user_id': lambda self,cr,uid,context: uid,
464 def create(self, cr, uid, vals, context={}):
465 journal_id = super(osv.osv, self).create(cr, uid, vals, context)
466 # journal_name = self.browse(cr, uid, [journal_id])[0].code
467 # periods = self.pool.get('account.period')
468 # ids = periods.search(cr, uid, [('date_stop','>=',time.strftime('%Y-%m-%d'))])
469 # for period in periods.browse(cr, uid, ids):
470 # self.pool.get('account.journal.period').create(cr, uid, {
471 # 'name': (journal_name or '')+':'+(period.code or ''),
472 # 'journal_id': journal_id,
473 # 'period_id': period.id
476 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
483 ids = self.search(cr, user, [('code','ilike',name)]+ args, limit=limit)
485 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
486 return self.name_get(cr, user, ids, context=context)
489 class account_fiscalyear(osv.osv):
490 _name = "account.fiscalyear"
491 _description = "Fiscal Year"
493 'name': fields.char('Fiscal Year', size=64, required=True),
494 'code': fields.char('Code', size=6, required=True),
495 'date_start': fields.date('Start date', required=True),
496 'date_stop': fields.date('End date', required=True),
497 'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'),
498 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', redonly=True),
502 'state': lambda *a: 'draft',
504 _order = "date_start"
505 def create_period3(self,cr, uid, ids, context={}):
506 return self.create_period(cr, uid, ids, context, 3)
508 def create_period(self,cr, uid, ids, context={}, interval=1):
509 for fy in self.browse(cr, uid, ids, context):
511 ds = mx.DateTime.strptime(fy.date_start, '%Y-%m-%d')
512 while ds.strftime('%Y-%m-%d')<fy.date_stop:
513 de = ds + RelativeDateTime(months=interval, days=-1)
514 self.pool.get('account.period').create(cr, uid, {
515 'name': ds.strftime('%m/%Y'),
516 'code': ds.strftime('%m/%Y'),
517 'date_start': ds.strftime('%Y-%m-%d'),
518 'date_stop': de.strftime('%Y-%m-%d'),
519 'fiscalyear_id': fy.id,
521 ds = ds + RelativeDateTime(months=interval)
524 def find(self, cr, uid, dt=None, exception=True, context={}):
526 dt = time.strftime('%Y-%m-%d')
527 ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)])
530 raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.'))
536 class account_period(osv.osv):
537 _name = "account.period"
538 _description = "Account period"
540 'name': fields.char('Period Name', size=64, required=True),
541 'code': fields.char('Code', size=12),
542 'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}),
543 'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}),
544 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True),
545 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', readonly=True)
548 'state': lambda *a: 'draft',
550 _order = "date_start"
551 def next(self, cr, uid, period, step, context={}):
552 ids = self.search(cr, uid, [('date_start','>',period.date_start)])
557 def find(self, cr, uid, dt=None, context={}):
559 dt = time.strftime('%Y-%m-%d')
560 #CHECKME: shouldn't we check the state of the period?
561 ids = self.search(cr, uid, [('date_start','<=',dt),('date_stop','>=',dt)])
563 raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.'))
566 def action_draft(self, cr, uid, ids, *args):
567 users_roles = self.pool.get('res.users').browse(cr, uid, uid).roles_id
568 for role in users_roles:
569 if role.name=='Period':
572 cr.execute('update account_journal_period set state=%s where period_id=%d', (mode, id))
573 cr.execute('update account_period set state=%s where id=%d', (mode, id))
578 class account_journal_period(osv.osv):
579 _name = "account.journal.period"
580 _description = "Journal - Period"
582 def _icon_get(self, cr, uid, ids, field_name, arg=None, context={}):
583 result = {}.fromkeys(ids, 'STOCK_NEW')
584 for r in self.read(cr, uid, ids, ['state']):
586 'draft': 'STOCK_NEW',
587 'printed': 'STOCK_PRINT_PREVIEW',
588 'done': 'STOCK_DIALOG_AUTHENTICATION',
589 }.get(r['state'], 'STOCK_NEW')
593 'name': fields.char('Journal-Period Name', size=64, required=True),
594 'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"),
595 'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"),
596 'icon': fields.function(_icon_get, method=True, string='Icon', type='string'),
597 'active': fields.boolean('Active', required=True),
598 'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'Status', required=True, readonly=True)
601 def _check(self, cr, uid, ids, context={}):
602 for obj in self.browse(cr, uid, ids, context):
603 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))
606 raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !'))
609 def write(self, cr, uid, ids, vals, context={}):
610 self._check(cr, uid, ids, context)
611 return super(account_journal_period, self).write(cr, uid, ids, vals, context)
613 def create(self, cr, uid, vals, context={}):
614 period_id=vals.get('period_id',False)
616 period = self.pool.get('account.period').browse(cr, uid,period_id)
617 vals['state']=period.state
618 return super(account_journal_period, self).create(cr, uid, vals, context)
620 def unlink(self, cr, uid, ids, context={}):
621 self._check(cr, uid, ids, context)
622 return super(account_journal_period, self).unlink(cr, uid, ids, context)
625 'state': lambda *a: 'draft',
626 'active': lambda *a: True,
630 account_journal_period()
632 class account_fiscalyear(osv.osv):
633 _inherit = "account.fiscalyear"
634 _description = "Fiscal Year"
636 'start_journal_period_id':fields.many2one('account.journal.period','New Entries Journal'),
637 'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True),
641 #----------------------------------------------------------
643 #----------------------------------------------------------
644 class account_move(osv.osv):
645 _name = "account.move"
646 _description = "Account Entry"
648 def name_get(self, cursor, user, ids, context=None):
652 data_move = self.pool.get('account.move').browse(cursor,user,ids)
653 for move in data_move:
654 if move.state=='draft':
655 name = '*' + move.name
658 res.append((move.id, name))
662 def _get_period(self, cr, uid, context):
663 periods = self.pool.get('account.period').find(cr, uid)
669 def _amount_compute(self, cr, uid, ids, name, args, context, where =''):
670 if not ids: return {}
671 cr.execute('select move_id,sum(debit) from account_move_line where move_id in ('+','.join(map(str,ids))+') group by move_id')
672 result = dict(cr.fetchall())
674 result.setdefault(id, 0.0)
678 'name': fields.char('Entry Name', size=64, required=True),
679 'ref': fields.char('Ref', size=64),
680 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}),
681 'journal_id': fields.many2one('account.journal', 'Journal', required=True, states={'posted':[('readonly',True)]}),
682 'state': fields.selection([('draft','Draft'), ('posted','Posted')], 'Status', required=True, readonly=True),
683 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}),
684 'to_check': fields.boolean('To Be Verified'),
685 'partner_id': fields.related('line_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store=True),
686 'amount': fields.function(_amount_compute, method=True, string='Amount', digits=(16,2), store=True),
689 'state': lambda *a: 'draft',
690 'period_id': _get_period,
693 def _check_centralisation(self, cursor, user, ids):
694 for move in self.browse(cursor, user, ids):
695 if move.journal_id.centralisation:
696 move_ids = self.search(cursor, user, [
697 ('period_id', '=', move.period_id.id),
698 ('journal_id', '=', move.journal_id.id),
700 if len(move_ids) > 1:
704 def _check_period_journal(self, cursor, user, ids):
705 for move in self.browse(cursor, user, ids):
706 for line in move.line_id:
707 if line.period_id.id != move.period_id.id:
709 if line.journal_id.id != move.journal_id.id:
714 (_check_centralisation,
715 'You can not create more than one move per period on centralized journal',
717 (_check_period_journal,
718 'You can not create entries on different period/journal in the same move',
721 def post(self, cr, uid, ids, context=None):
722 if self.validate(cr, uid, ids, context) and len(ids):
723 cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('posted',))
725 raise osv.except_osv(_('Integrity Error !'), _('You can not validate a non balanced entry !'))
728 def button_validate(self, cursor, user, ids, context=None):
729 return self.post(cursor, user, ids, context=context)
731 def button_cancel(self, cr, uid, ids, context={}):
732 for line in self.browse(cr, uid, ids, context):
733 if not line.journal_id.update_posted:
734 raise osv.except_osv(_('Error !'), _('You can not modify a posted entry of this journal !'))
736 cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('draft',))
739 def write(self, cr, uid, ids, vals, context={}):
741 c['novalidate'] = True
742 result = super(osv.osv, self).write(cr, uid, ids, vals, c)
743 self.validate(cr, uid, ids, context)
747 # TODO: Check if period is closed !
749 def create(self, cr, uid, vals, context={}):
750 if 'line_id' in vals:
751 if 'journal_id' in vals:
752 for l in vals['line_id']:
754 l[2]['journal_id'] = vals['journal_id']
755 context['journal_id'] = vals['journal_id']
756 if 'period_id' in vals:
757 for l in vals['line_id']:
759 l[2]['period_id'] = vals['period_id']
760 context['period_id'] = vals['period_id']
762 default_period = self._get_period(cr, uid, context)
763 for l in vals['line_id']:
765 l[2]['period_id'] = default_period
766 context['period_id'] = default_period
768 if not 'name' in vals:
769 journal = self.pool.get('account.journal').browse(cr, uid, context.get('journal_id', vals.get('journal_id', False)))
770 if journal.sequence_id:
771 vals['name'] = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id)
773 raise osv.except_osv(_('Error'), _('No sequence defined in the journal !'))
774 accnt_journal = self.pool.get('account.journal').browse(cr, uid, vals['journal_id'])
775 if 'line_id' in vals:
777 c['novalidate'] = True
778 result = super(account_move, self).create(cr, uid, vals, c)
779 self.validate(cr, uid, [result], context)
781 result = super(account_move, self).create(cr, uid, vals, context)
784 def unlink(self, cr, uid, ids, context={}, check=True):
786 for move in self.browse(cr, uid, ids, context):
787 if move['state'] <> 'draft':
788 raise osv.except_osv(_('UserError'),
789 _('You can not delete posted movement: "%s"!') % \
791 line_ids = map(lambda x: x.id, move.line_id)
792 context['journal_id'] = move.journal_id.id
793 context['period_id'] = move.period_id.id
794 self.pool.get('account.move.line')._update_check(cr, uid, line_ids, context)
795 toremove.append(move.id)
796 result = super(account_move, self).unlink(cr, uid, toremove, context)
799 def _compute_balance(self, cr, uid, id, context={}):
800 move = self.browse(cr, uid, [id])[0]
802 for line in move.line_id:
803 amount+= (line.debit - line.credit)
806 def _centralise(self, cr, uid, move, mode):
808 account_id = move.journal_id.default_debit_account_id.id
811 raise osv.except_osv(_('UserError'),
812 _('There is no default default debit account defined \n' \
813 'on journal "%s"') % move.journal_id.name)
815 account_id = move.journal_id.default_credit_account_id.id
818 raise osv.except_osv(_('UserError'),
819 _('There is no default default credit account defined \n' \
820 'on journal "%s"') % move.journal_id.name)
822 # find the first line of this move with the current mode
823 # or create it if it doesn't exist
824 cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode))
829 line_id = self.pool.get('account.move.line').create(cr, uid, {
830 'name': 'Centralisation '+mode,
831 'centralisation': mode,
832 'account_id': account_id,
834 'journal_id': move.journal_id.id,
835 'period_id': move.period_id.id,
836 'date': move.period_id.date_stop,
839 }, {'journal_id': move.journal_id.id, 'period_id': move.period_id.id})
841 # find the first line of this move with the other mode
842 # so that we can exclude it from our calculation
843 cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode2))
850 cr.execute('select sum('+mode+') from account_move_line where move_id=%d and id<>%d', (move.id, line_id2))
851 result = cr.fetchone()[0] or 0.0
852 cr.execute('update account_move_line set '+mode2+'=%f where id=%d', (result, line_id))
856 # Validate a balanced move. If it is a centralised journal, create a move.
858 def validate(self, cr, uid, ids, context={}):
860 for move in self.browse(cr, uid, ids, context):
861 journal = move.journal_id
866 for line in move.line_id:
867 amount += line.debit - line.credit
868 line_ids.append(line.id)
869 if line.state=='draft':
870 line_draft_ids.append(line.id)
873 company_id = line.account_id.company_id.id
874 if not company_id == line.account_id.company_id.id:
875 raise osv.except_osv(_('Error'), _('Couldn\'t create move between different companies'))
877 if line.account_id.currency_id:
878 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):
879 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)))
881 if abs(amount) < 0.0001:
882 if not len(line_draft_ids):
884 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
885 'journal_id': move.journal_id.id,
886 'period_id': move.period_id.id,
888 }, context, check=False)
892 if journal.type not in ('purchase','sale'):
895 for line in move.line_id:
896 if move.journal_id.type == 'sale':
899 key = 'account_paid_id'
902 key = 'account_collected_id'
906 key = 'account_collected_id'
909 key = 'account_paid_id'
910 if line.account_id.tax_ids:
911 code = amount = False
912 for tax in line.account_id.tax_ids:
914 acc = getattr(tax, key).id
915 account[acc] = (getattr(tax,
916 field_base + 'tax_code_id').id,
917 getattr(tax, field_base + 'tax_sign'))
918 account2[(acc,getattr(tax,
919 field_base + 'tax_code_id').id)] = (getattr(tax,
920 field_base + 'tax_code_id').id,
921 getattr(tax, field_base + 'tax_sign'))
922 code = getattr(tax, field_base + 'base_code_id').id
923 amount = getattr(tax, field_base+'base_sign') * \
924 (line.debit + line.credit)
926 if code and not (line.tax_code_id or line.tax_amount):
927 self.pool.get('account.move.line').write(cr, uid,
931 }, context=context, check=False)
936 key = (line.account_id.id, line.tax_code_id.id)
938 code = account2[key][0]
939 amount = account2[key][1] * (line.debit + line.credit)
940 elif line.account_id.id in account:
941 code = account[line.account_id.id][0]
942 amount = account[line.account_id.id][1] * (line.debit + line.credit)
943 if (code or amount) and not (line.tax_code_id or line.tax_amount):
944 self.pool.get('account.move.line').write(cr, uid, [line.id], {
947 }, context, check=False)
952 if journal.centralisation:
953 self._centralise(cr, uid, move, 'debit')
954 self._centralise(cr, uid, move, 'credit')
955 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
957 }, context, check=False)
960 self.pool.get('account.move.line').write(cr, uid, line_ids, {
961 'journal_id': move.journal_id.id,
962 'period_id': move.period_id.id,
963 #'tax_code_id': False,
964 #'tax_amount': False,
966 }, context, check=False)
971 class account_move_reconcile(osv.osv):
972 _name = "account.move.reconcile"
973 _description = "Account Reconciliation"
975 'name': fields.char('Name', size=64, required=True),
976 'type': fields.char('Type', size=16, required=True),
977 'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry lines'),
978 'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'),
979 'create_date': fields.date('Creation date', readonly=True),
982 'name': lambda self,cr,uid,ctx={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile') or '/',
984 def reconcile_partial_check(self, cr, uid, ids, type='auto', context={}):
985 for rec in self.browse(cr, uid, ids, context):
987 for line in rec.line_partial_ids:
988 total += (line.debit or 0.0) - (line.credit or 0.0)
990 self.pool.get('account.move.line').write(cr, uid,
991 map(lambda x: x.id, rec.line_partial_ids),
992 {'reconcile_id': rec.id }
996 def name_get(self, cr, uid, ids, context=None):
1000 for r in self.browse(cr, uid, ids, context):
1001 total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0)
1003 name = '%s (%.2f)' % (r.name, total)
1004 result.append((r.id,name))
1006 result.append((r.id,r.name))
1010 account_move_reconcile()
1012 #----------------------------------------------------------
1014 #----------------------------------------------------------
1017 child_depend: la taxe depend des taxes filles
1019 class account_tax_code(osv.osv):
1021 A code for the tax object.
1023 This code is used for some tax declarations.
1025 def _sum(self, cr, uid, ids, name, args, context, where =''):
1026 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
1027 acc_set = ",".join(map(str, ids2))
1028 if context.get('based_on', 'invoices') == 'payments':
1029 cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1030 FROM account_move_line AS line, \
1031 account_move AS move \
1032 LEFT JOIN account_invoice invoice ON \
1033 (invoice.move_id = move.id) \
1034 WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1035 AND move.id = line.move_id \
1036 AND ((invoice.state = \'paid\') \
1037 OR (invoice.id IS NULL)) \
1038 GROUP BY line.tax_code_id')
1040 cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1041 FROM account_move_line AS line \
1042 WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1043 GROUP BY line.tax_code_id')
1044 res=dict(cr.fetchall())
1045 for record in self.browse(cr, uid, ids, context):
1046 def _rec_get(record):
1047 amount = res.get(record.id, 0.0)
1048 for rec in record.child_ids:
1049 amount += _rec_get(rec) * rec.sign
1051 res[record.id] = round(_rec_get(record), 2)
1054 def _sum_period(self, cr, uid, ids, name, args, context):
1055 if not 'period_id' in context:
1056 period_id = self.pool.get('account.period').find(cr, uid)
1057 if not len(period_id):
1058 return dict.fromkeys(ids, 0.0)
1059 period_id = period_id[0]
1061 period_id = context['period_id']
1062 return self._sum(cr, uid, ids, name, args, context,
1063 where=' and line.period_id='+str(period_id))
1065 _name = 'account.tax.code'
1066 _description = 'Tax Code'
1069 'name': fields.char('Tax Case Name', size=64, required=True),
1070 'code': fields.char('Case Code', size=64),
1071 'info': fields.text('Description'),
1072 'sum': fields.function(_sum, method=True, string="Year Sum"),
1073 'sum_period': fields.function(_sum_period, method=True, string="Period Sum"),
1074 'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True),
1075 'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'),
1076 'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'),
1077 'company_id': fields.many2one('res.company', 'Company', required=True),
1078 'sign': fields.float('Sign for parent', required=True),
1081 def name_get(self, cr, uid, ids, context=None):
1084 if isinstance(ids, (int, long)):
1086 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1087 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1090 def _default_company(self, cr, uid, context={}):
1091 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1093 return user.company_id.id
1094 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1096 'company_id': _default_company,
1097 'sign': lambda *args: 1.0,
1099 def _check_recursion(self, cr, uid, ids):
1102 cr.execute('select distinct parent_id from account_tax_code where id in ('+','.join(map(str,ids))+')')
1103 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1110 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
1112 _order = 'code,name'
1115 class account_tax(osv.osv):
1119 Type: percent, fixed, none, code
1120 PERCENT: tax = price * amount
1121 FIXED: tax = price + amount
1123 CODE: execute python code. localcontext = {'price_unit':pu, 'address':address_object}
1124 return result in the context
1125 Ex: result=round(price_unit*0.21,4)
1127 _name = 'account.tax'
1128 _description = 'Tax'
1130 'name': fields.char('Tax Name', size=64, required=True, help="This name will be used to be displayed on reports"),
1131 '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."),
1132 'amount': fields.float('Amount', required=True, digits=(14,4)),
1133 'active': fields.boolean('Active'),
1134 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1135 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1136 '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."),
1137 'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'),
1138 'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'),
1139 'parent_id':fields.many2one('account.tax', 'Parent Tax Account', select=True),
1140 'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'),
1141 '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."),
1142 'python_compute':fields.text('Python Code'),
1143 'python_compute_inv':fields.text('Python Code (reverse)'),
1144 'python_applicable':fields.text('Python Code'),
1145 '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."),
1148 # Fields used for the VAT declaration
1150 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."),
1151 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."),
1152 'base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."),
1153 'tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."),
1155 # Same fields for refund invoices
1157 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the VAT declaration."),
1158 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1159 'ref_base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."),
1160 'ref_tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."),
1161 '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"),
1162 'company_id': fields.many2one('res.company', 'Company', required=True),
1163 'description': fields.char('Internal Name',size=32),
1166 def name_get(self, cr, uid, ids, context={}):
1170 for record in self.read(cr, uid, ids, ['description','name'], context):
1171 name = record['description'] and record['description'] or record['name']
1172 res.append((record['id'],name ))
1175 def _default_company(self, cr, uid, context={}):
1176 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1178 return user.company_id.id
1179 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1181 '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''',
1182 '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''',
1183 'applicable_type': lambda *a: 'true',
1184 'type': lambda *a: 'percent',
1185 'amount': lambda *a: 0,
1186 'active': lambda *a: 1,
1187 'sequence': lambda *a: 1,
1188 'tax_group': lambda *a: 'vat',
1189 'ref_tax_sign': lambda *a: 1,
1190 'ref_base_sign': lambda *a: 1,
1191 'tax_sign': lambda *a: 1,
1192 'base_sign': lambda *a: 1,
1193 'include_base_amount': lambda *a: False,
1194 'company_id': _default_company,
1198 def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1201 if tax.applicable_type=='code':
1202 localdict = {'price_unit':price_unit, 'address':self.pool.get('res.partner.address').browse(cr, uid, address_id), 'product':product, 'partner':partner}
1203 exec tax.python_applicable in localdict
1204 if localdict.get('result', False):
1210 def _unit_compute(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1211 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1214 cur_price_unit=price_unit
1216 # we compute the amount for the current tax object and append it to the result
1218 if tax.type=='percent':
1219 amount = cur_price_unit * tax.amount
1220 res.append({'id':tax.id,
1223 'account_collected_id':tax.account_collected_id.id,
1224 'account_paid_id':tax.account_paid_id.id,
1225 'base_code_id': tax.base_code_id.id,
1226 'ref_base_code_id': tax.ref_base_code_id.id,
1227 'sequence': tax.sequence,
1228 'base_sign': tax.base_sign,
1229 'tax_sign': tax.tax_sign,
1230 'ref_base_sign': tax.ref_base_sign,
1231 'ref_tax_sign': tax.ref_tax_sign,
1232 'price_unit': cur_price_unit,
1233 'tax_code_id': tax.tax_code_id.id,
1234 'ref_tax_code_id': tax.ref_tax_code_id.id,
1237 elif tax.type=='fixed':
1238 res.append({'id':tax.id,
1240 'amount':tax.amount,
1241 'account_collected_id':tax.account_collected_id.id,
1242 'account_paid_id':tax.account_paid_id.id,
1243 'base_code_id': tax.base_code_id.id,
1244 'ref_base_code_id': tax.ref_base_code_id.id,
1245 'sequence': tax.sequence,
1246 'base_sign': tax.base_sign,
1247 'tax_sign': tax.tax_sign,
1248 'ref_base_sign': tax.ref_base_sign,
1249 'ref_tax_sign': tax.ref_tax_sign,
1251 'tax_code_id': tax.tax_code_id.id,
1252 'ref_tax_code_id': tax.ref_tax_code_id.id,})
1253 elif tax.type=='code':
1254 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1255 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1256 exec tax.python_compute in localdict
1257 amount = localdict['result']
1262 'account_collected_id': tax.account_collected_id.id,
1263 'account_paid_id': tax.account_paid_id.id,
1264 'base_code_id': tax.base_code_id.id,
1265 'ref_base_code_id': tax.ref_base_code_id.id,
1266 'sequence': tax.sequence,
1267 'base_sign': tax.base_sign,
1268 'tax_sign': tax.tax_sign,
1269 'ref_base_sign': tax.ref_base_sign,
1270 'ref_tax_sign': tax.ref_tax_sign,
1271 'price_unit': cur_price_unit,
1272 'tax_code_id': tax.tax_code_id.id,
1273 'ref_tax_code_id': tax.ref_tax_code_id.id,
1275 amount2 = res[-1]['amount']
1276 if len(tax.child_ids):
1277 if tax.child_depend:
1280 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner)
1281 res.extend(child_tax)
1282 if tax.include_base_amount:
1283 cur_price_unit+=amount2
1286 def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1289 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1293 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1294 one tax for each tax id in IDS and their childs
1296 res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner)
1298 r['amount'] *= quantity
1301 def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1302 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1306 cur_price_unit=price_unit
1308 # we compute the amount for the current tax object and append it to the result
1310 if tax.type=='percent':
1311 amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
1312 res.append({'id':tax.id,
1315 'account_collected_id':tax.account_collected_id.id,
1316 'account_paid_id':tax.account_paid_id.id,
1317 'base_code_id': tax.base_code_id.id,
1318 'ref_base_code_id': tax.ref_base_code_id.id,
1319 'sequence': tax.sequence,
1320 'base_sign': tax.base_sign,
1321 'tax_sign': tax.tax_sign,
1322 'ref_base_sign': tax.ref_base_sign,
1323 'ref_tax_sign': tax.ref_tax_sign,
1324 'price_unit': cur_price_unit - amount,
1325 'tax_code_id': tax.tax_code_id.id,
1326 'ref_tax_code_id': tax.ref_tax_code_id.id,})
1328 elif tax.type=='fixed':
1329 res.append({'id':tax.id,
1331 'amount':tax.amount,
1332 'account_collected_id':tax.account_collected_id.id,
1333 'account_paid_id':tax.account_paid_id.id,
1334 'base_code_id': tax.base_code_id.id,
1335 'ref_base_code_id': tax.ref_base_code_id.id,
1336 'sequence': tax.sequence,
1337 'base_sign': tax.base_sign,
1338 'tax_sign': tax.tax_sign,
1339 'ref_base_sign': tax.ref_base_sign,
1340 'ref_tax_sign': tax.ref_tax_sign,
1342 'tax_code_id': tax.tax_code_id.id,
1343 'ref_tax_code_id': tax.ref_tax_code_id.id,})
1345 elif tax.type=='code':
1346 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1347 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1348 exec tax.python_compute_inv in localdict
1349 amount = localdict['result']
1354 'account_collected_id': tax.account_collected_id.id,
1355 'account_paid_id': tax.account_paid_id.id,
1356 'base_code_id': tax.base_code_id.id,
1357 'ref_base_code_id': tax.ref_base_code_id.id,
1358 'sequence': tax.sequence,
1359 'base_sign': tax.base_sign,
1360 'tax_sign': tax.tax_sign,
1361 'ref_base_sign': tax.ref_base_sign,
1362 'ref_tax_sign': tax.ref_tax_sign,
1363 'price_unit': cur_price_unit - amount,
1364 'tax_code_id': tax.tax_code_id.id,
1365 'ref_tax_code_id': tax.ref_tax_code_id.id,
1368 amount2 = res[-1]['amount']
1369 if len(tax.child_ids):
1370 if tax.child_depend:
1375 for t in tax.child_ids:
1376 parent_tax = self._unit_compute_inv(cr, uid, [t], amount, address_id, product, partner)
1377 res.extend(parent_tax)
1378 if tax.include_base_amount:
1379 cur_price_unit-=amount
1383 def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1385 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1386 Price Unit is a VAT included price
1390 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1391 one tax for each tax id in IDS and their childs
1393 res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
1395 r['amount'] *= quantity
1399 # ---------------------------------------------------------
1400 # Account Entries Models
1401 # ---------------------------------------------------------
1403 class account_model(osv.osv):
1404 _name = "account.model"
1405 _description = "Account Model"
1407 'name': fields.char('Model Name', size=64, required=True, help="This is a model for recurring accounting entries"),
1408 'ref': fields.char('Ref', size=64),
1409 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
1410 'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'),
1411 'legend' :fields.text('Legend',readonly=True,size=100),
1415 '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''',
1420 class account_model_line(osv.osv):
1421 _name = "account.model.line"
1422 _description = "Account Model Entries"
1424 'name': fields.char('Name', size=64, required=True),
1425 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the resources from the lowest sequences to the higher ones"),
1426 'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity on entries"),
1427 'debit': fields.float('Debit', digits=(16,2)),
1428 'credit': fields.float('Credit', digits=(16,2)),
1430 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"),
1432 'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True),
1434 'ref': fields.char('Ref.', size=16),
1436 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."),
1437 'currency_id': fields.many2one('res.currency', 'Currency'),
1439 'partner_id': fields.many2one('res.partner', 'Partner Ref.'),
1440 '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."),
1441 'date': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Current Date', required=True, help="The date of the generated entries"),
1444 'date': lambda *a: 'today'
1447 _sql_constraints = [
1448 ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in model !'),
1449 ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model !'),
1451 account_model_line()
1453 # ---------------------------------------------------------
1454 # Account Subscription
1455 # ---------------------------------------------------------
1458 class account_subscription(osv.osv):
1459 _name = "account.subscription"
1460 _description = "Account Subscription"
1462 'name': fields.char('Name', size=64, required=True),
1463 'ref': fields.char('Ref.', size=16),
1464 'model_id': fields.many2one('account.model', 'Model', required=True),
1466 'date_start': fields.date('Starting date', required=True),
1467 'period_total': fields.integer('Number of period', required=True),
1468 'period_nbr': fields.integer('Period', required=True),
1469 'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True),
1470 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status', required=True, readonly=True),
1472 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines')
1475 'date_start': lambda *a: time.strftime('%Y-%m-%d'),
1476 'period_type': lambda *a: 'month',
1477 'period_total': lambda *a: 12,
1478 'period_nbr': lambda *a: 1,
1479 'state': lambda *a: 'draft',
1481 def state_draft(self, cr, uid, ids, context={}):
1482 self.write(cr, uid, ids, {'state':'draft'})
1485 def check(self, cr, uid, ids, context={}):
1487 for sub in self.browse(cr, uid, ids, context):
1489 for line in sub.lines_id:
1490 if not line.move_id.id:
1494 todone.append(sub.id)
1496 self.write(cr, uid, todone, {'state':'done'})
1499 def remove_line(self, cr, uid, ids, context={}):
1501 for sub in self.browse(cr, uid, ids, context):
1502 for line in sub.lines_id:
1503 if not line.move_id.id:
1504 toremove.append(line.id)
1506 self.pool.get('account.subscription.line').unlink(cr, uid, toremove)
1507 self.write(cr, uid, ids, {'state':'draft'})
1510 def compute(self, cr, uid, ids, context={}):
1511 for sub in self.browse(cr, uid, ids, context):
1513 for i in range(sub.period_total):
1514 self.pool.get('account.subscription.line').create(cr, uid, {
1516 'subscription_id': sub.id,
1518 if sub.period_type=='day':
1519 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(days=sub.period_nbr)).strftime('%Y-%m-%d')
1520 if sub.period_type=='month':
1521 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(months=sub.period_nbr)).strftime('%Y-%m-%d')
1522 if sub.period_type=='year':
1523 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(years=sub.period_nbr)).strftime('%Y-%m-%d')
1524 self.write(cr, uid, ids, {'state':'running'})
1526 account_subscription()
1528 class account_subscription_line(osv.osv):
1529 _name = "account.subscription.line"
1530 _description = "Account Subscription Line"
1532 'subscription_id': fields.many2one('account.subscription', 'Subscription', required=True, select=True),
1533 'date': fields.date('Date', required=True),
1534 'move_id': fields.many2one('account.move', 'Entry'),
1538 def move_create(self, cr, uid, ids, context={}):
1540 for line in self.browse(cr, uid, ids, context):
1544 ids = self.pool.get('account.model').generate(cr, uid, [line.subscription_id.model_id.id], datas, context)
1545 tocheck[line.subscription_id.id] = True
1546 self.write(cr, uid, [line.id], {'move_id':ids[0]})
1548 self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context)
1551 account_subscription_line()
1554 class account_config_wizard(osv.osv_memory):
1555 _name = 'account.config.wizard'
1557 def _get_charts(self, cr, uid, context):
1558 module_obj=self.pool.get('ir.module.module')
1559 ids=module_obj.search(cr, uid, [('category_id', '=', 'Account Charts'), ('state', '<>', 'installed')])
1560 res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)]
1561 res.append((-1, 'None'))
1562 res.sort(lambda x,y: cmp(x[1],y[1]))
1566 'name':fields.char('Name', required=True, size=64, help="Name of the fiscal year as displayed on screens."),
1567 'code':fields.char('Code', required=True, size=64, help="Name of the fiscal year as displayed in reports."),
1568 'date1': fields.date('Starting Date', required=True),
1569 'date2': fields.date('Ending Date', required=True),
1570 'period':fields.selection([('month','Month'),('3months','3 Months')], 'Periods', required=True),
1571 'charts' : fields.selection(_get_charts, 'Charts of Account',required=True)
1574 'code': lambda *a: time.strftime('%Y'),
1575 'name': lambda *a: time.strftime('%Y'),
1576 'date1': lambda *a: time.strftime('%Y-01-01'),
1577 'date2': lambda *a: time.strftime('%Y-12-31'),
1578 'period':lambda *a:'month',
1579 'charts': lambda *a: -1,
1581 def action_cancel(self,cr,uid,ids,conect=None):
1583 'view_type': 'form',
1584 "view_mode": 'form',
1585 'res_model': 'ir.actions.configuration.wizard',
1586 'type': 'ir.actions.act_window',
1590 def install_account_chart(self, cr, uid,ids, context=None):
1591 for res in self.read(cr,uid,ids):
1594 mod_obj = self.pool.get('ir.module.module')
1595 mod_obj.write(cr , uid, [id] ,{'state' : 'to install'})
1596 mod_obj.download(cr, uid, [id], context=context)
1598 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,))
1604 mod_obj.write(cr , uid, [id] ,{'state' : 'to install'})
1605 mod_obj.download(cr, uid, [id], context=context)
1610 db, pool = pooler.restart_pool(cr.dbname, update_module=True)
1612 def action_create(self, cr, uid,ids, context=None):
1613 for res in self.read(cr,uid,ids):
1614 if 'date1' in res and 'date2' in res:
1615 res_obj = self.pool.get('account.fiscalyear')
1616 start_date=res['date1']
1617 end_date=res['date2']
1618 name=res['name']#DateTime.strptime(start_date, '%Y-%m-%d').strftime('%m.%Y') + '-' + DateTime.strptime(end_date, '%Y-%m-%d').strftime('%m.%Y')
1622 'date_start':start_date,
1623 'date_stop':end_date,
1625 new_id=res_obj.create(cr, uid, vals, context=context)
1626 if res['period']=='month':
1627 res_obj.create_period(cr,uid,[new_id])
1628 elif res['period']=='3months':
1629 res_obj.create_period3(cr,uid,[new_id])
1630 self.install_account_chart(cr,uid,ids)
1632 'view_type': 'form',
1633 "view_mode": 'form',
1634 'res_model': 'ir.actions.configuration.wizard',
1635 'type': 'ir.actions.act_window',
1641 account_config_wizard()
1644 # ---------------------------------------------------------------
1645 # Account Templates : Account, Tax, Tax Code and chart. + Wizard
1646 # ---------------------------------------------------------------
1648 class account_tax_template(osv.osv):
1649 _name = 'account.tax.template'
1650 account_tax_template()
1652 class account_account_template(osv.osv):
1654 _name = "account.account.template"
1655 _description ='Templates for Accounts'
1658 'name': fields.char('Name', size=128, required=True, select=True),
1659 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
1660 'code': fields.char('Code', size=64),
1661 'type': fields.selection([
1662 ('receivable','Receivable'),
1663 ('payable','Payable'),
1665 ('consolidation','Consolidation'),
1667 ('closed','Closed'),
1668 ], 'Internal Type', required=True,),
1669 'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
1670 'reconcile': fields.boolean('Allow Reconciliation', help="Check this option if the user can make a reconciliation of the entries in this account."),
1671 'shortcut': fields.char('Shortcut', size=12),
1672 'note': fields.text('Note'),
1673 'parent_id': fields.many2one('account.account.template','Parent Account Template', ondelete='cascade'),
1674 'child_parent_ids':fields.one2many('account.account.template','parent_id','Children'),
1675 'tax_ids': fields.many2many('account.tax.template', 'account_account_template_tax_rel','account_id','tax_id', 'Default Taxes'),
1679 'reconcile': lambda *a: False,
1680 'type' : lambda *a :'view',
1683 def _check_recursion(self, cr, uid, ids):
1686 cr.execute('select parent_id from account_account_template where id in ('+','.join(map(str,ids))+')')
1687 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1694 (_check_recursion, 'Error ! You can not create recursive account templates.', ['parent_id'])
1698 def name_get(self, cr, uid, ids, context={}):
1701 reads = self.read(cr, uid, ids, ['name','code'], context)
1703 for record in reads:
1704 name = record['name']
1706 name = record['code']+' '+name
1707 res.append((record['id'],name ))
1710 account_account_template()
1712 class account_tax_code_template(osv.osv):
1714 _name = 'account.tax.code.template'
1715 _description = 'Tax Code Template'
1719 'name': fields.char('Tax Case Name', size=64, required=True),
1720 'code': fields.char('Case Code', size=64),
1721 'info': fields.text('Description'),
1722 'parent_id': fields.many2one('account.tax.code.template', 'Parent Code', select=True),
1723 'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Childs Codes'),
1724 'sign': fields.float('Sign for parent', required=True),
1728 'sign': lambda *args: 1.0,
1731 def name_get(self, cr, uid, ids, context=None):
1734 if isinstance(ids, (int, long)):
1736 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1737 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1740 def _check_recursion(self, cr, uid, ids):
1743 cr.execute('select distinct parent_id from account_tax_code_template where id in ('+','.join(map(str,ids))+')')
1744 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1751 (_check_recursion, 'Error ! You can not create recursive Tax Codes.', ['parent_id'])
1753 _order = 'code,name'
1754 account_tax_code_template()
1757 class account_chart_template(osv.osv):
1758 _name="account.chart.template"
1759 _description= "Templates for Account Chart"
1762 'name': fields.char('Name', size=64, required=True),
1763 'account_root_id': fields.many2one('account.account.template','Root Account',required=True,domain=[('parent_id','=',False)]),
1764 'tax_code_root_id': fields.many2one('account.tax.code.template','Root Tax Code',required=True,domain=[('parent_id','=',False)]),
1765 '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'),
1766 'bank_account_view_id': fields.many2one('account.account.template','Bank Account',required=True),
1767 'property_account_receivable': fields.many2one('account.account.template','Receivable Account'),
1768 'property_account_payable': fields.many2one('account.account.template','Payable Account'),
1769 'property_account_expense_categ': fields.many2one('account.account.template','Expense Category Account'),
1770 'property_account_income_categ': fields.many2one('account.account.template','Income Category Account'),
1771 'property_account_expense': fields.many2one('account.account.template','Expense Account on Product Template'),
1772 'property_account_income': fields.many2one('account.account.template','Income Account on Product Template'),
1775 account_chart_template()
1777 class account_tax_template(osv.osv):
1779 _name = 'account.tax.template'
1780 _description = 'Templates for Taxes'
1783 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
1784 'name': fields.char('Tax Name', size=64, required=True),
1785 '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."),
1786 'amount': fields.float('Amount', required=True, digits=(14,4)),
1787 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1788 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1789 '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."),
1790 'account_collected_id':fields.many2one('account.account.template', 'Invoice Tax Account'),
1791 'account_paid_id':fields.many2one('account.account.template', 'Refund Tax Account'),
1792 'parent_id':fields.many2one('account.tax.template', 'Parent Tax Account', select=True),
1793 '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."),
1794 'python_compute':fields.text('Python Code'),
1795 'python_compute_inv':fields.text('Python Code (reverse)'),
1796 'python_applicable':fields.text('Python Code'),
1797 '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."),
1800 # Fields used for the VAT declaration
1802 'base_code_id': fields.many2one('account.tax.code.template', 'Base Code', help="Use this code for the VAT declaration."),
1803 'tax_code_id': fields.many2one('account.tax.code.template', 'Tax Code', help="Use this code for the VAT declaration."),
1804 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1805 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1807 # Same fields for refund invoices
1809 'ref_base_code_id': fields.many2one('account.tax.code.template', 'Refund Base Code', help="Use this code for the VAT declaration."),
1810 'ref_tax_code_id': fields.many2one('account.tax.code.template', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1811 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1812 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1813 '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."),
1814 'description': fields.char('Internal Name', size=32),
1817 def name_get(self, cr, uid, ids, context={}):
1821 for record in self.read(cr, uid, ids, ['description','name'], context):
1822 name = record['description'] and record['description'] or record['name']
1823 res.append((record['id'],name ))
1826 def _default_company(self, cr, uid, context={}):
1827 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1829 return user.company_id.id
1830 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1833 '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''',
1834 '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''',
1835 'applicable_type': lambda *a: 'true',
1836 'type': lambda *a: 'percent',
1837 'amount': lambda *a: 0,
1838 'sequence': lambda *a: 1,
1839 'tax_group': lambda *a: 'vat',
1840 'ref_tax_sign': lambda *a: 1,
1841 'ref_base_sign': lambda *a: 1,
1842 'tax_sign': lambda *a: 1,
1843 'base_sign': lambda *a: 1,
1844 'include_base_amount': lambda *a: False,
1849 account_tax_template()
1851 # Multi charts of Accounts wizard
1853 class wizard_multi_charts_accounts(osv.osv_memory):
1855 Create a new account chart for a company.
1858 * an account chart template
1859 * a number of digits for formatting code of non-view accounts
1860 * a list of bank account owned by the company
1862 * generates all accounts from the template and assign them to the right company
1863 * generates all taxes and tax codes, changing account assignations
1864 * generates all accounting properties and assign correctly
1866 _name='wizard.multi.charts.accounts'
1869 'company_id':fields.many2one('res.company','Company',required=True),
1870 'chart_template_id': fields.many2one('account.chart.template','Chart Template',required=True),
1871 'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Bank Accounts',required=True),
1872 'code_digits':fields.integer('# of Digits',required=True,help="No. of Digits to use for account code"),
1876 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr,uid,[uid],c)[0].company_id.id,
1877 'code_digits': lambda *a:6,
1880 def action_create(self, cr, uid, ids, context=None):
1881 obj_multi = self.browse(cr,uid,ids[0])
1882 obj_acc = self.pool.get('account.account')
1883 obj_acc_tax = self.pool.get('account.tax')
1884 obj_journal = self.pool.get('account.journal')
1885 obj_acc_template = self.pool.get('account.account.template')
1888 obj_acc_root = obj_multi.chart_template_id.account_root_id
1889 tax_code_root_id = obj_multi.chart_template_id.tax_code_root_id.id
1890 company_id = obj_multi.company_id.id
1893 acc_template_ref = {}
1894 tax_template_ref = {}
1895 tax_code_template_ref = {}
1898 #create all the tax code
1899 children_tax_code_template = self.pool.get('account.tax.code.template').search(cr, uid, [('parent_id','child_of',[tax_code_root_id])], order='id')
1900 for tax_code_template in self.pool.get('account.tax.code.template').browse(cr, uid, children_tax_code_template):
1902 'name': (tax_code_root_id == tax_code_template.id) and obj_multi.company_id.name or tax_code_template.name,
1903 'code': tax_code_template.code,
1904 'info': tax_code_template.info,
1905 '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,
1906 'company_id': company_id,
1907 'sign': tax_code_template.sign,
1909 new_tax_code = self.pool.get('account.tax.code').create(cr,uid,vals)
1910 #recording the new tax code to do the mapping
1911 tax_code_template_ref[tax_code_template.id] = new_tax_code
1914 for tax in obj_multi.chart_template_id.tax_template_ids:
1918 'sequence': tax.sequence,
1919 'amount':tax.amount,
1921 'applicable_type': tax.applicable_type,
1922 'domain':tax.domain,
1923 'parent_id': tax.parent_id and ((tax.parent_id.id in tax_template_ref) and tax_template_ref[tax.parent_id.id]) or False,
1924 'child_depend': tax.child_depend,
1925 'python_compute': tax.python_compute,
1926 'python_compute_inv': tax.python_compute_inv,
1927 'python_applicable': tax.python_applicable,
1928 'tax_group':tax.tax_group,
1929 'base_code_id': tax.base_code_id and tax_code_template_ref[tax.base_code_id.id] or False,
1930 'tax_code_id': tax.tax_code_id and tax_code_template_ref[tax.tax_code_id.id] or False,
1931 'base_sign': tax.base_sign,
1932 'tax_sign': tax.tax_sign,
1933 'ref_base_code_id': tax.ref_base_code_id and tax_code_template_ref[tax.ref_base_code_id.id] or False,
1934 'ref_tax_code_id': tax.ref_tax_code_id and tax_code_template_ref[tax.ref_tax_code_id.id] or False,
1935 'ref_base_sign': tax.ref_base_sign,
1936 'ref_tax_sign': tax.ref_tax_sign,
1937 'include_base_amount': tax.include_base_amount,
1938 'description':tax.description,
1939 'company_id': company_id,
1941 new_tax = obj_acc_tax.create(cr,uid,vals_tax)
1942 #as the accounts have not been created yet, we have to wait before filling these fields
1943 todo_dict[new_tax] = {
1944 'account_collected_id': tax.account_collected_id and tax.account_collected_id.id or False,
1945 'account_paid_id': tax.account_paid_id and tax.account_paid_id.id or False,
1947 tax_template_ref[tax.id] = new_tax
1949 #deactivate the parent_store functionnality on account_account for rapidity purpose
1950 self.pool._init = True
1952 children_acc_template = obj_acc_template.search(cr, uid, [('parent_id','child_of',[obj_acc_root.id])])
1953 children_acc_template.sort()
1954 for account_template in obj_acc_template.browse(cr, uid, children_acc_template):
1956 for tax in account_template.tax_ids:
1957 tax_ids.append(tax_template_ref[tax.id])
1958 #create the account_account
1960 dig = obj_multi.code_digits
1961 code_main = len(account_template.code)
1962 code_acc = account_template.code
1963 if code_main<=dig and account_template.type != 'view':
1964 code_acc=str(code_acc) + (str('0'*(dig-code_main)))
1966 'name': (obj_acc_root.id == account_template.id) and obj_multi.company_id.name or account_template.name,
1967 #'sign': account_template.sign,
1968 'currency_id': account_template.currency_id and account_template.currency_id.id or False,
1970 'type': account_template.type,
1971 'user_type': account_template.user_type and account_template.user_type.id or False,
1972 'reconcile': account_template.reconcile,
1973 'shortcut': account_template.shortcut,
1974 'note': account_template.note,
1975 '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,
1976 'tax_ids': [(6,0,tax_ids)],
1977 'company_id': company_id,
1979 new_account = obj_acc.create(cr,uid,vals)
1980 acc_template_ref[account_template.id] = new_account
1981 #reactivate the parent_store functionnality on account_account
1982 self.pool._init = False
1983 self.pool.get('account.account')._parent_store_compute(cr)
1985 for key,value in todo_dict.items():
1986 if value['account_collected_id'] or value['account_paid_id']:
1987 obj_acc_tax.write(cr, uid, [key], vals={
1988 'account_collected_id': acc_template_ref[value['account_collected_id']],
1989 'account_paid_id': acc_template_ref[value['account_paid_id']],
1994 view_id = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Journal View')])[0]
1995 seq_id = self.pool.get('ir.sequence').search(cr,uid,[('code','=','account.journal')])[0]
1996 seq_code = self.pool.get('ir.sequence').get(cr, uid, 'account.journal')
1998 vals_journal['view_id']=view_id
1999 vals_journal['sequence_id']=seq_id
2002 vals_journal['name'] = _('Sales Journal')
2003 vals_journal['type'] = 'sale'
2004 vals_journal['code'] = _('SAJ')
2006 if obj_multi.chart_template_id.property_account_receivable:
2007 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_receivable.id]
2008 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_receivable.id]
2010 obj_journal.create(cr,uid,vals_journal)
2013 vals_journal['name']=_('Purchase Journal')
2014 vals_journal['type']='purchase'
2015 vals_journal['code']=_('EXJ')
2017 if obj_multi.chart_template_id.property_account_payable:
2018 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_payable.id]
2019 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_payable.id]
2021 obj_journal.create(cr,uid,vals_journal)
2024 view_id_cash = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Cash Journal View')])[0]
2025 view_id_cur = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Multi-Currency Cash Journal View')])[0]
2026 ref_acc_bank = obj_multi.chart_template_id.bank_account_view_id
2029 for line in obj_multi.bank_accounts_id:
2030 #create the account_account for this bank journal
2031 tmp = self.pool.get('res.partner.bank').name_get(cr, uid, [line.acc_no.id])[0][1]
2032 dig = obj_multi.code_digits
2034 'name': line.acc_no.bank and line.acc_no.bank.name+' '+tmp or tmp,
2035 'currency_id': line.currency_id and line.currency_id.id or False,
2036 'code': str(int(ref_acc_bank.code.ljust(dig,'0')) + current_num),
2038 'user_type': account_template.user_type and account_template.user_type.id or False,
2040 'parent_id': acc_template_ref[ref_acc_bank.id] or False,
2041 'company_id': company_id,
2043 acc_cash_id = obj_acc.create(cr,uid,vals)
2045 #create the bank journal
2046 vals_journal['name']= vals['name']
2047 vals_journal['code']= _('BNK') + str(current_num)
2048 vals_journal['sequence_id'] = seq_id
2049 vals_journal['type'] = 'cash'
2050 if line.currency_id:
2051 vals_journal['view_id'] = view_id_cur
2052 vals_journal['currency'] = line.currency_id.id
2054 vals_journal['view_id'] = view_id_cash
2055 vals_journal['default_credit_account_id'] = acc_cash_id
2056 vals_journal['default_debit_account_id']= acc_cash_id
2057 obj_journal.create(cr,uid,vals_journal)
2061 #create the properties
2062 property_obj = self.pool.get('ir.property')
2063 fields_obj = self.pool.get('ir.model.fields')
2066 ('property_account_receivable','res.partner','account.account'),
2067 ('property_account_payable','res.partner','account.account'),
2068 ('property_account_expense_categ','product.category','account.account'),
2069 ('property_account_income_categ','product.category','account.account'),
2070 ('property_account_expense','product.template','account.account'),
2071 ('property_account_income','product.template','account.account')
2073 for record in todo_list:
2075 r = property_obj.search(cr, uid, [('name','=', record[0] ),('company_id','=',company_id)])
2076 account = getattr(obj_multi.chart_template_id, record[0])
2077 field = fields_obj.search(cr, uid, [('name','=',record[0]),('model','=',record[1]),('relation','=',record[2])])
2080 'company_id': company_id,
2081 'fields_id': field[0],
2082 'value': account and 'account.account,'+str(acc_template_ref[account.id]) or False,
2085 #the property exist: modify it
2086 property_obj.write(cr, uid, r, vals)
2088 #create the property
2089 property_obj.create(cr, uid, vals)
2092 'view_type': 'form',
2093 "view_mode": 'form',
2094 'res_model': 'ir.actions.configuration.wizard',
2095 'type': 'ir.actions.act_window',
2098 def action_cancel(self,cr,uid,ids,conect=None):
2100 'view_type': 'form',
2101 "view_mode": 'form',
2102 'res_model': 'ir.actions.configuration.wizard',
2103 'type': 'ir.actions.act_window',
2108 wizard_multi_charts_accounts()
2110 class account_bank_accounts_wizard(osv.osv_memory):
2111 _name='account.bank.accounts.wizard'
2114 'acc_no':fields.many2one('res.partner.bank','Account No.',required=True),
2115 'bank_account_id':fields.many2one('wizard.multi.charts.accounts', 'Bank Account', required=True),
2116 'currency_id':fields.many2one('res.currency', 'Currency'),
2119 account_bank_accounts_wizard()
2121 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: