1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 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 beginning 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 if context and context.has_key('consolidate_childs'): #add consolidated childs of accounts
158 ids = super(account_account,self).search(cr, uid, args, offset, limit,
159 order, context=context, count=count)
160 for consolidate_child in self.browse(cr, uid, context['account_id']).child_consol_ids:
161 ids.append(consolidate_child.id)
164 return super(account_account,self).search(cr, uid, args, offset, limit,
165 order, context=context, count=count)
167 def _get_children_and_consol(self, cr, uid, ids, context={}):
168 #this function search for all the children and all consolidated children (recursively) of the given account ids
169 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)], context=context)
171 for rec in self.browse(cr, uid, ids2, context=context):
172 for child in rec.child_consol_ids:
173 ids3.append(child.id)
175 ids3 = self._get_children_and_consol(cr, uid, ids3, context)
178 def __compute(self, cr, uid, ids, field_names, arg, context={}, query=''):
179 #compute the balance/debit/credit accordingly to the value of field_name for the given account ids
181 'balance': "COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance ",
182 'debit': "COALESCE(SUM(l.debit), 0) as debit ",
183 'credit': "COALESCE(SUM(l.credit), 0) as credit "
185 #get all the necessary accounts
186 ids2 = self._get_children_and_consol(cr, uid, ids, context)
187 acc_set = ",".join(map(str, ids2))
188 #compute for each account the balance/debit/credit from the move lines
191 query = self.pool.get('account.move.line')._query_get(cr, uid,
193 cr.execute(("SELECT l.account_id as id, " +\
194 ' , '.join(map(lambda x: mapping[x], field_names)) +
196 "account_move_line l " \
198 "l.account_id IN (%s) " \
199 "AND " + query + " " \
200 "GROUP BY l.account_id") % (acc_set, ))
202 for res in cr.dictfetchall():
203 accounts[res['id']] = res
205 #for the asked accounts, get from the dictionnary 'accounts' the value of it
208 res[id] = self._get_account_values(cr, uid, id, accounts, field_names, context)
211 def _get_account_values(self, cr, uid, id, accounts, field_names, context={}):
212 res = {}.fromkeys(field_names, 0.0)
213 browse_rec = self.browse(cr, uid, id)
214 if browse_rec.type == 'consolidation':
215 ids2 = self.read(cr, uid, [browse_rec.id], ['child_consol_ids'], context)[0]['child_consol_ids']
216 for t in self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])]):
217 if t not in ids2 and t != browse_rec.id:
220 tmp = self._get_account_values(cr, uid, i, accounts, field_names, context)
221 for a in field_names:
224 ids2 = self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])])
226 for a in field_names:
227 res[a] += accounts.get(i, {}).get(a, 0.0)
230 def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}):
232 for rec in self.browse(cr, uid, ids, context):
233 result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code)
236 def _get_child_ids(self, cr, uid, ids, field_name, arg, context={}):
238 for record in self.browse(cr, uid, ids, context):
239 if record.child_parent_ids:
240 result[record.id]=[x.id for x in record.child_parent_ids]
244 if record.child_consol_ids:
245 for acc in record.child_consol_ids:
246 result[record.id].append(acc.id)
251 'name': fields.char('Name', size=128, required=True, select=True),
252 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
253 'code': fields.char('Code', size=64, required=True),
254 'type': fields.selection([
255 ('receivable','Receivable'),
256 ('payable','Payable'),
258 ('consolidation','Consolidation'),
261 ], 'Internal Type', required=True,),
263 'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
264 'parent_id': fields.many2one('account.account','Parent', ondelete='cascade'),
265 'child_parent_ids':fields.one2many('account.account','parent_id','Children'),
266 'child_consol_ids':fields.many2many('account.account', 'account_account_consol_rel', 'child_id', 'parent_id', 'Consolidated Children'),
267 'child_id': fields.function(_get_child_ids, method=True, type='many2many',relation="account.account",string="Children Accounts"),
268 'balance': fields.function(__compute, digits=(16,2), method=True, string='Balance', multi='balance'),
269 'credit': fields.function(__compute, digits=(16,2), method=True, string='Credit', multi='balance'),
270 'debit': fields.function(__compute, digits=(16,2), method=True, string='Debit', multi='balance'),
271 'reconcile': fields.boolean('Reconcile', help="Check this account if the user can make a reconciliation of the entries in this account."),
272 'shortcut': fields.char('Shortcut', size=12),
273 'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel',
274 'account_id','tax_id', 'Default Taxes'),
275 'note': fields.text('Note'),
276 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Company Currency'),
277 'company_id': fields.many2one('res.company', 'Company', required=True),
278 'active': fields.boolean('Active', select=2),
280 'parent_left': fields.integer('Parent Left', select=1),
281 'parent_right': fields.integer('Parent Right', select=1),
282 'currency_mode': fields.selection([('current','At Date'),('average','Average Rate')], 'Outgoing Currencies Rate',
284 'This will select how is computed the current currency rate for outgoing transactions. '\
285 'In most countries the legal method is "average" but only a few softwares are able to '\
286 'manage this. So if you import from another software, you may have to use the rate at date. ' \
287 'Incoming transactions, always use the rate at date.', \
289 'check_history': fields.boolean('Display History',
290 help="Check this box if you want to print all entries when printing the General Ledger, "\
291 "otherwise it will only print its balance."),
294 def _default_company(self, cr, uid, context={}):
295 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
297 return user.company_id.id
298 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
301 'type' : lambda *a :'view',
302 'reconcile': lambda *a: False,
303 'company_id': _default_company,
304 'active': lambda *a: True,
305 'check_history': lambda *a: True,
306 'currency_mode': lambda *a: 'current'
309 def _check_recursion(self, cr, uid, ids):
310 obj_self=self.browse(cr,uid,ids[0])
311 p_id=obj_self.parent_id and obj_self.parent_id.id
312 if (obj_self in obj_self.child_consol_ids) or (p_id and (p_id is obj_self.id)):
315 cr.execute('select distinct child_id from account_account_consol_rel where parent_id in ('+','.join(map(str,ids))+')')
316 child_ids = filter(None, map(lambda x:x[0], cr.fetchall()))
318 if (p_id and (p_id in c_ids)) or (obj_self.id in c_ids):
321 s_ids=self.search(cr,uid,[('parent_id','in',c_ids)])
322 if p_id and (p_id in s_ids):
329 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
331 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
339 if name and str(name).startswith('partner:'):
340 part_id = int(name.split(':')[1])
341 part = self.pool.get('res.partner').browse(cr, user, part_id, context)
342 args += [('id','in', (part.property_account_payable.id, part.property_account_receivable.id))]
344 if name and str(name).startswith('type:'):
345 type = name.split(':')[1]
346 args += [('type','=', type)]
351 ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit)
353 ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit)
355 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
357 ids = self.search(cr, user, args, context=context, limit=limit)
358 return self.name_get(cr, user, ids, context=context)
360 def name_get(self, cr, uid, ids, context={}):
363 reads = self.read(cr, uid, ids, ['name','code'], context)
366 name = record['name']
368 name = record['code']+' '+name
369 res.append((record['id'],name ))
372 def copy(self, cr, uid, id, default={}, context={},done_list=[]):
373 account = self.browse(cr, uid, id, context=context)
377 default=default.copy()
378 default['parent_id'] = False
379 if account.id in done_list:
381 done_list.append(account.id)
383 for child in account.child_id:
384 child_ids=self.copy(cr, uid, child.id, default, context=context,done_list=done_list)
386 new_child_ids.append(child_ids)
387 default['child_parent_ids'] = [(6, 0, new_child_ids)]
389 default['child_parent_ids'] = False
390 return super(account_account, self).copy(cr, uid, id, default, context=context)
392 def write(self, cr, uid, ids, vals, context=None):
395 if 'active' in vals and not vals['active']:
396 line_obj = self.pool.get('account.move.line')
397 account_ids = self.search(cr, uid, [('id', 'child_of', ids)])
398 if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]):
399 raise osv.except_osv(_('Error !'), _('You can not deactivate an account that contains account moves.'))
400 return super(account_account, self).write(cr, uid, ids, vals, context=context)
403 class account_journal_view(osv.osv):
404 _name = "account.journal.view"
405 _description = "Journal View"
407 'name': fields.char('Journal View', size=64, required=True),
408 'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
411 account_journal_view()
414 class account_journal_column(osv.osv):
415 def _col_get(self, cr, user, context={}):
417 cols = self.pool.get('account.move.line')._columns
419 result.append( (col, cols[col].string) )
422 _name = "account.journal.column"
423 _description = "Journal Column"
425 'name': fields.char('Column Name', size=64, required=True),
426 'field': fields.selection(_col_get, 'Field Name', method=True, required=True, size=32),
427 'view_id': fields.many2one('account.journal.view', 'Journal View', select=True),
428 'sequence': fields.integer('Sequence'),
429 'required': fields.boolean('Required'),
430 'readonly': fields.boolean('Readonly'),
433 account_journal_column()
435 class account_journal(osv.osv):
436 _name = "account.journal"
437 _description = "Journal"
439 'name': fields.char('Journal Name', size=64, required=True, translate=True),
440 'code': fields.char('Code', size=16),
441 'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True),
442 'refund_journal': fields.boolean('Refund Journal'),
444 'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]),
445 'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]),
447 'active': fields.boolean('Active'),
448 'view_id': fields.many2one('account.journal.view', 'View', required=True, help="Gives the view used when writing or browsing entries in this journal. The view tell Open ERP which fields should be visible, required or readonly and in which order. You can create your own view for a faster encoding in each journal."),
449 'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account', domain="[('type','!=','view')]"),
450 'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account', domain="[('type','!=','view')]"),
451 'centralisation': fields.boolean('Centralised counterpart', help="Check this box if you want that each entry doesn't create a counterpart but share the same counterpart for each entry of this journal. This is used in fiscal year closing."),
452 'update_posted': fields.boolean('Allow Cancelling Entries'),
453 'group_invoice_lines': fields.boolean('Group invoice lines', help="If this box is cheked, the system will try to group the accouting lines when generating them from invoices."),
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.'),
459 'company_id': fields.related('default_credit_account_id','company_id',type='many2one', relation="res.company", string="Company"),
463 'active': lambda *a: 1,
464 'user_id': lambda self,cr,uid,context: uid,
466 def create(self, cr, uid, vals, context={}):
467 journal_id = super(osv.osv, self).create(cr, uid, vals, context)
468 # journal_name = self.browse(cr, uid, [journal_id])[0].code
469 # periods = self.pool.get('account.period')
470 # ids = periods.search(cr, uid, [('date_stop','>=',time.strftime('%Y-%m-%d'))])
471 # for period in periods.browse(cr, uid, ids):
472 # self.pool.get('account.journal.period').create(cr, uid, {
473 # 'name': (journal_name or '')+':'+(period.code or ''),
474 # 'journal_id': journal_id,
475 # 'period_id': period.id
478 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
485 ids = self.search(cr, user, [('code','ilike',name)]+ args, limit=limit)
487 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
488 return self.name_get(cr, user, ids, context=context)
491 class account_fiscalyear(osv.osv):
492 _name = "account.fiscalyear"
493 _description = "Fiscal Year"
495 'name': fields.char('Fiscal Year', size=64, required=True),
496 'code': fields.char('Code', size=6, required=True),
497 'date_start': fields.date('Start date', required=True),
498 'date_stop': fields.date('End date', required=True),
499 'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'),
500 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', redonly=True),
504 'state': lambda *a: 'draft',
506 _order = "date_start"
508 def _check_duration(self,cr,uid,ids):
509 obj_fy=self.browse(cr,uid,ids[0])
510 if obj_fy.date_stop < obj_fy.date_start:
515 (_check_duration, 'Error ! The date duration of the Fiscal Year is invalid. ', ['date_stop'])
518 def create_period3(self,cr, uid, ids, context={}):
519 return self.create_period(cr, uid, ids, context, 3)
521 def create_period(self,cr, uid, ids, context={}, interval=1):
522 for fy in self.browse(cr, uid, ids, context):
524 ds = mx.DateTime.strptime(fy.date_start, '%Y-%m-%d')
525 while ds.strftime('%Y-%m-%d')<fy.date_stop:
526 de = ds + RelativeDateTime(months=interval, days=-1)
528 if de.strftime('%Y-%m-%d')>fy.date_stop:
529 de=mx.DateTime.strptime(fy.date_stop, '%Y-%m-%d')
531 self.pool.get('account.period').create(cr, uid, {
532 'name': ds.strftime('%m/%Y'),
533 'code': ds.strftime('%m/%Y'),
534 'date_start': ds.strftime('%Y-%m-%d'),
535 'date_stop': de.strftime('%Y-%m-%d'),
536 'fiscalyear_id': fy.id,
538 ds = ds + RelativeDateTime(months=interval)
541 def find(self, cr, uid, dt=None, exception=True, context={}):
543 dt = time.strftime('%Y-%m-%d')
544 ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)])
547 raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.'))
553 class account_period(osv.osv):
554 _name = "account.period"
555 _description = "Account period"
557 'name': fields.char('Period Name', size=64, required=True),
558 'code': fields.char('Code', size=12),
559 'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}),
560 'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}),
561 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True),
562 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', readonly=True)
565 'state': lambda *a: 'draft',
567 _order = "date_start"
569 def _check_duration(self,cr,uid,ids):
570 obj_period=self.browse(cr,uid,ids[0])
571 if obj_period.date_stop < obj_period.date_start:
575 def _check_year_limit(self,cr,uid,ids):
576 obj_period=self.browse(cr,uid,ids[0])
577 if obj_period.fiscalyear_id.date_stop < obj_period.date_stop or obj_period.fiscalyear_id.date_stop < obj_period.date_start or obj_period.fiscalyear_id.date_start > obj_period.date_start or obj_period.fiscalyear_id.date_start > obj_period.date_stop:
582 (_check_duration, 'Error ! The date duration of the Period(s) is invalid. ', ['date_stop']),
583 (_check_year_limit, 'Error ! The date duration of the Period(s) should be within the limit of the Fiscal year. ', ['date_stop'])
586 def next(self, cr, uid, period, step, context={}):
587 ids = self.search(cr, uid, [('date_start','>',period.date_start)])
592 def find(self, cr, uid, dt=None, context={}):
594 dt = time.strftime('%Y-%m-%d')
595 #CHECKME: shouldn't we check the state of the period?
596 ids = self.search(cr, uid, [('date_start','<=',dt),('date_stop','>=',dt)])
598 raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.'))
601 def action_draft(self, cr, uid, ids, *args):
602 users_roles = self.pool.get('res.users').browse(cr, uid, uid).roles_id
603 for role in users_roles:
604 if role.name=='Period':
607 cr.execute('update account_journal_period set state=%s where period_id=%s', (mode, id))
608 cr.execute('update account_period set state=%s where id=%s', (mode, id))
613 class account_journal_period(osv.osv):
614 _name = "account.journal.period"
615 _description = "Journal - Period"
617 def _icon_get(self, cr, uid, ids, field_name, arg=None, context={}):
618 result = {}.fromkeys(ids, 'STOCK_NEW')
619 for r in self.read(cr, uid, ids, ['state']):
621 'draft': 'STOCK_NEW',
622 'printed': 'STOCK_PRINT_PREVIEW',
623 'done': 'STOCK_DIALOG_AUTHENTICATION',
624 }.get(r['state'], 'STOCK_NEW')
628 'name': fields.char('Journal-Period Name', size=64, required=True),
629 'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"),
630 'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"),
631 'icon': fields.function(_icon_get, method=True, string='Icon', type='string'),
632 'active': fields.boolean('Active', required=True),
633 'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'Status', required=True, readonly=True)
636 def _check(self, cr, uid, ids, context={}):
637 for obj in self.browse(cr, uid, ids, context):
638 cr.execute('select * from account_move_line where journal_id=%s and period_id=%s limit 1', (obj.journal_id.id, obj.period_id.id))
641 raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !'))
644 def write(self, cr, uid, ids, vals, context={}):
645 self._check(cr, uid, ids, context)
646 return super(account_journal_period, self).write(cr, uid, ids, vals, context)
648 def create(self, cr, uid, vals, context={}):
649 period_id=vals.get('period_id',False)
651 period = self.pool.get('account.period').browse(cr, uid,period_id)
652 vals['state']=period.state
653 return super(account_journal_period, self).create(cr, uid, vals, context)
655 def unlink(self, cr, uid, ids, context={}):
656 self._check(cr, uid, ids, context)
657 return super(account_journal_period, self).unlink(cr, uid, ids, context)
660 'state': lambda *a: 'draft',
661 'active': lambda *a: True,
665 account_journal_period()
667 class account_fiscalyear(osv.osv):
668 _inherit = "account.fiscalyear"
669 _description = "Fiscal Year"
671 'start_journal_period_id':fields.many2one('account.journal.period','New Entries Journal'),
672 'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True),
676 #----------------------------------------------------------
678 #----------------------------------------------------------
679 class account_move(osv.osv):
680 _name = "account.move"
681 _description = "Account Entry"
684 def name_get(self, cursor, user, ids, context=None):
688 data_move = self.pool.get('account.move').browse(cursor,user,ids)
689 for move in data_move:
690 if move.state=='draft':
691 name = '*' + str(move.id)
694 res.append((move.id, name))
698 def _get_period(self, cr, uid, context):
699 periods = self.pool.get('account.period').find(cr, uid)
705 def _amount_compute(self, cr, uid, ids, name, args, context, where =''):
706 if not ids: return {}
707 cr.execute('select move_id,sum(debit) from account_move_line where move_id in ('+','.join(map(str,ids))+') group by move_id')
708 result = dict(cr.fetchall())
710 result.setdefault(id, 0.0)
714 'name': fields.char('Number', size=64, required=True),
715 'ref': fields.char('Ref', size=64),
716 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}),
717 'journal_id': fields.many2one('account.journal', 'Journal', required=True, states={'posted':[('readonly',True)]}),
718 'state': fields.selection([('draft','Draft'), ('posted','Posted')], 'Status', required=True, readonly=True),
719 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}),
720 'to_check': fields.boolean('To Be Verified'),
721 'partner_id': fields.related('line_id', 'partner_id', type="many2one", relation="res.partner", string="Partner"),
722 'amount': fields.function(_amount_compute, method=True, string='Amount', digits=(16,2)),
723 'date': fields.date('Date', required=True),
724 'type': fields.selection([
725 ('pay_voucher','Cash Payment'),
726 ('bank_pay_voucher','Bank Payment'),
727 ('rec_voucher','Cash Receipt'),
728 ('bank_rec_voucher','Bank Receipt'),
729 ('cont_voucher','Contra'),
730 ('journal_sale_vou','Journal Sale'),
731 ('journal_pur_voucher','Journal Purchase'),
732 ('journal_voucher','Journal Voucher'),
733 ],'Type', readonly=True, select=True, states={'draft':[('readonly',False)]}),
736 'name': lambda *a: '/',
737 'state': lambda *a: 'draft',
738 'period_id': _get_period,
739 'type' : lambda *a : 'journal_voucher',
740 'date': lambda *a:time.strftime('%Y-%m-%d'),
743 def _check_centralisation(self, cursor, user, ids):
744 for move in self.browse(cursor, user, ids):
745 if move.journal_id.centralisation:
746 move_ids = self.search(cursor, user, [
747 ('period_id', '=', move.period_id.id),
748 ('journal_id', '=', move.journal_id.id),
750 if len(move_ids) > 1:
754 def _check_period_journal(self, cursor, user, ids):
755 for move in self.browse(cursor, user, ids):
756 for line in move.line_id:
757 if line.period_id.id != move.period_id.id:
759 if line.journal_id.id != move.journal_id.id:
764 (_check_centralisation,
765 'You can not create more than one move per period on centralized journal',
767 (_check_period_journal,
768 'You can not create entries on different period/journal in the same move',
771 def post(self, cr, uid, ids, context=None):
772 if self.validate(cr, uid, ids, context) and len(ids):
773 for move in self.browse(cr, uid, ids):
776 journal = move.journal_id
777 if journal.sequence_id:
778 new_name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id)
780 raise osv.except_osv(_('Error'), _('No sequence defined in the journal !'))
782 self.write(cr, uid, [move.id], {'name':new_name})
784 cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('posted',))
786 raise osv.except_osv(_('Integrity Error !'), _('You can not validate a non balanced entry !'))
789 def button_validate(self, cursor, user, ids, context=None):
790 return self.post(cursor, user, ids, context=context)
792 def button_cancel(self, cr, uid, ids, context={}):
793 for line in self.browse(cr, uid, ids, context):
794 if not line.journal_id.update_posted:
795 raise osv.except_osv(_('Error !'), _('You can not modify a posted entry of this journal !\nYou should mark the journal to allow canceling entries.'))
797 cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('draft',))
800 def write(self, cr, uid, ids, vals, context={}):
802 c['novalidate'] = True
803 result = super(osv.osv, self).write(cr, uid, ids, vals, c)
804 self.validate(cr, uid, ids, context)
808 # TODO: Check if period is closed !
810 def create(self, cr, uid, vals, context={}):
811 if 'line_id' in vals:
812 if 'journal_id' in vals:
813 for l in vals['line_id']:
815 l[2]['journal_id'] = vals['journal_id']
816 context['journal_id'] = vals['journal_id']
817 if 'period_id' in vals:
818 for l in vals['line_id']:
820 l[2]['period_id'] = vals['period_id']
821 context['period_id'] = vals['period_id']
823 default_period = self._get_period(cr, uid, context)
824 for l in vals['line_id']:
826 l[2]['period_id'] = default_period
827 context['period_id'] = default_period
829 accnt_journal = self.pool.get('account.journal').browse(cr, uid, vals['journal_id'])
830 if 'line_id' in vals:
832 c['novalidate'] = True
833 result = super(account_move, self).create(cr, uid, vals, c)
834 self.validate(cr, uid, [result], context)
836 result = super(account_move, self).create(cr, uid, vals, context)
839 def copy(self, cr, uid, id, default=None, context=None):
842 default = default.copy()
843 default.update({'state':'draft', 'name':'/',})
844 return super(account_move, self).copy(cr, uid, id, default, context)
846 def unlink(self, cr, uid, ids, context={}, check=True):
848 for move in self.browse(cr, uid, ids, context):
849 if move['state'] <> 'draft':
850 raise osv.except_osv(_('UserError'),
851 _('You can not delete posted movement: "%s"!') % \
853 line_ids = map(lambda x: x.id, move.line_id)
854 context['journal_id'] = move.journal_id.id
855 context['period_id'] = move.period_id.id
856 self.pool.get('account.move.line')._update_check(cr, uid, line_ids, context)
857 toremove.append(move.id)
858 result = super(account_move, self).unlink(cr, uid, toremove, context)
861 def _compute_balance(self, cr, uid, id, context={}):
862 move = self.browse(cr, uid, [id])[0]
864 for line in move.line_id:
865 amount+= (line.debit - line.credit)
868 def _centralise(self, cr, uid, move, mode):
870 account_id = move.journal_id.default_debit_account_id.id
873 raise osv.except_osv(_('UserError'),
874 _('There is no default default debit account defined \n' \
875 'on journal "%s"') % move.journal_id.name)
877 account_id = move.journal_id.default_credit_account_id.id
880 raise osv.except_osv(_('UserError'),
881 _('There is no default default credit account defined \n' \
882 'on journal "%s"') % move.journal_id.name)
884 # find the first line of this move with the current mode
885 # or create it if it doesn't exist
886 cr.execute('select id from account_move_line where move_id=%s and centralisation=%s limit 1', (move.id, mode))
891 line_id = self.pool.get('account.move.line').create(cr, uid, {
892 'name': 'Centralisation '+mode,
893 'centralisation': mode,
894 'account_id': account_id,
896 'journal_id': move.journal_id.id,
897 'period_id': move.period_id.id,
898 'date': move.period_id.date_stop,
901 }, {'journal_id': move.journal_id.id, 'period_id': move.period_id.id})
903 # find the first line of this move with the other mode
904 # so that we can exclude it from our calculation
905 cr.execute('select id from account_move_line where move_id=%s and centralisation=%s limit 1', (move.id, mode2))
912 cr.execute('select sum('+mode+') from account_move_line where move_id=%s and id<>%s', (move.id, line_id2))
913 result = cr.fetchone()[0] or 0.0
914 cr.execute('update account_move_line set '+mode2+'=%s where id=%s', (result, line_id))
918 # Validate a balanced move. If it is a centralised journal, create a move.
920 def validate(self, cr, uid, ids, context={}):
922 for move in self.browse(cr, uid, ids, context):
923 #unlink analytic lines on move_lines
924 for obj_line in move.line_id:
925 for obj in obj_line.analytic_lines:
926 self.pool.get('account.analytic.line').unlink(cr,uid,obj.id)
928 journal = move.journal_id
933 for line in move.line_id:
934 amount += line.debit - line.credit
935 line_ids.append(line.id)
936 if line.state=='draft':
937 line_draft_ids.append(line.id)
940 company_id = line.account_id.company_id.id
941 if not company_id == line.account_id.company_id.id:
942 raise osv.except_osv(_('Error'), _("Couldn't create move between different companies"))
944 if line.account_id.currency_id:
945 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):
946 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)))
948 if abs(amount) < 0.0001:
949 if not len(line_draft_ids):
951 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
952 'journal_id': move.journal_id.id,
953 'period_id': move.period_id.id,
955 }, context, check=False)
959 if journal.type not in ('purchase','sale'):
962 for line in move.line_id:
963 if move.journal_id.type == 'sale':
966 key = 'account_paid_id'
969 key = 'account_collected_id'
973 key = 'account_collected_id'
976 key = 'account_paid_id'
977 if line.account_id.tax_ids:
978 code = amount = False
979 for tax in line.account_id.tax_ids:
981 acc = getattr(tax, key).id
982 account[acc] = (getattr(tax,
983 field_base + 'tax_code_id').id,
984 getattr(tax, field_base + 'tax_sign'))
985 account2[(acc,getattr(tax,
986 field_base + 'tax_code_id').id)] = (getattr(tax,
987 field_base + 'tax_code_id').id,
988 getattr(tax, field_base + 'tax_sign'))
989 code = getattr(tax, field_base + 'base_code_id').id
990 amount = getattr(tax, field_base+'base_sign') * \
991 (line.debit + line.credit)
993 if code and not (line.tax_code_id or line.tax_amount):
994 self.pool.get('account.move.line').write(cr, uid,
998 }, context=context, check=False)
1003 key = (line.account_id.id, line.tax_code_id.id)
1005 code = account2[key][0]
1006 amount = account2[key][1] * (line.debit + line.credit)
1007 elif line.account_id.id in account:
1008 code = account[line.account_id.id][0]
1009 amount = account[line.account_id.id][1] * (line.debit + line.credit)
1010 if (code or amount) and not (line.tax_code_id or line.tax_amount):
1011 self.pool.get('account.move.line').write(cr, uid, [line.id], {
1012 'tax_code_id': code,
1013 'tax_amount': amount
1014 }, context, check=False)
1019 if journal.centralisation:
1020 self._centralise(cr, uid, move, 'debit')
1021 self._centralise(cr, uid, move, 'credit')
1022 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
1024 }, context, check=False)
1027 self.pool.get('account.move.line').write(cr, uid, line_ids, {
1028 'journal_id': move.journal_id.id,
1029 'period_id': move.period_id.id,
1030 #'tax_code_id': False,
1031 #'tax_amount': False,
1033 }, context, check=False)
1037 for tmp in move.line_id:
1038 list_ids.append(tmp.id)
1039 self.pool.get('account.move.line').create_analytic_lines(cr, uid, list_ids, context)
1043 class account_move_reconcile(osv.osv):
1044 _name = "account.move.reconcile"
1045 _description = "Account Reconciliation"
1047 'name': fields.char('Name', size=64, required=True),
1048 'type': fields.char('Type', size=16, required=True),
1049 'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry lines'),
1050 'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'),
1051 'create_date': fields.date('Creation date', readonly=True),
1054 'name': lambda self,cr,uid,ctx={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile') or '/',
1056 def reconcile_partial_check(self, cr, uid, ids, type='auto', context={}):
1057 for rec in self.browse(cr, uid, ids, context):
1059 for line in rec.line_partial_ids:
1060 total += (line.debit or 0.0) - (line.credit or 0.0)
1062 self.pool.get('account.move.line').write(cr, uid,
1063 map(lambda x: x.id, rec.line_partial_ids),
1064 {'reconcile_id': rec.id }
1068 def name_get(self, cr, uid, ids, context=None):
1072 for r in self.browse(cr, uid, ids, context):
1073 total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0)
1075 name = '%s (%.2f)' % (r.name, total)
1076 result.append((r.id,name))
1078 result.append((r.id,r.name))
1082 account_move_reconcile()
1084 #----------------------------------------------------------
1086 #----------------------------------------------------------
1089 child_depend: la taxe depend des taxes filles
1091 class account_tax_code(osv.osv):
1093 A code for the tax object.
1095 This code is used for some tax declarations.
1097 def _sum(self, cr, uid, ids, name, args, context, where =''):
1098 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
1099 acc_set = ",".join(map(str, ids2))
1100 if context.get('based_on', 'invoices') == 'payments':
1101 cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1102 FROM account_move_line AS line, \
1103 account_move AS move \
1104 LEFT JOIN account_invoice invoice ON \
1105 (invoice.move_id = move.id) \
1106 WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1107 AND move.id = line.move_id \
1108 AND ((invoice.state = \'paid\') \
1109 OR (invoice.id IS NULL)) \
1110 GROUP BY line.tax_code_id')
1112 cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1113 FROM account_move_line AS line \
1114 WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1115 GROUP BY line.tax_code_id')
1116 res=dict(cr.fetchall())
1117 for record in self.browse(cr, uid, ids, context):
1118 def _rec_get(record):
1119 amount = res.get(record.id, 0.0)
1120 for rec in record.child_ids:
1121 amount += _rec_get(rec) * rec.sign
1123 res[record.id] = round(_rec_get(record), 2)
1126 def _sum_period(self, cr, uid, ids, name, args, context):
1127 if 'period_id' in context and context['period_id']:
1128 period_id = context['period_id']
1130 period_id = self.pool.get('account.period').find(cr, uid)
1131 if not len(period_id):
1132 return dict.fromkeys(ids, 0.0)
1133 period_id = period_id[0]
1134 return self._sum(cr, uid, ids, name, args, context,
1135 where=' and line.period_id='+str(period_id))
1137 _name = 'account.tax.code'
1138 _description = 'Tax Code'
1141 'name': fields.char('Tax Case Name', size=64, required=True),
1142 'code': fields.char('Case Code', size=64),
1143 'info': fields.text('Description'),
1144 'sum': fields.function(_sum, method=True, string="Year Sum"),
1145 'sum_period': fields.function(_sum_period, method=True, string="Period Sum"),
1146 'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True),
1147 'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'),
1148 'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'),
1149 'company_id': fields.many2one('res.company', 'Company', required=True),
1150 'sign': fields.float('Sign for parent', required=True),
1153 def name_get(self, cr, uid, ids, context=None):
1156 if isinstance(ids, (int, long)):
1158 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1159 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1162 def _default_company(self, cr, uid, context={}):
1163 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1165 return user.company_id.id
1166 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1168 'company_id': _default_company,
1169 'sign': lambda *args: 1.0,
1171 def _check_recursion(self, cr, uid, ids):
1174 cr.execute('select distinct parent_id from account_tax_code where id in ('+','.join(map(str,ids))+')')
1175 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1182 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
1184 _order = 'code,name'
1187 class account_tax(osv.osv):
1191 Type: percent, fixed, none, code
1192 PERCENT: tax = price * amount
1193 FIXED: tax = price + amount
1195 CODE: execute python code. localcontext = {'price_unit':pu, 'address':address_object}
1196 return result in the context
1197 Ex: result=round(price_unit*0.21,4)
1199 _name = 'account.tax'
1200 _description = 'Tax'
1202 'name': fields.char('Tax Name', size=64, required=True, help="This name will be used to be displayed on reports"),
1203 '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."),
1204 'amount': fields.float('Amount', required=True, digits=(14,4)),
1205 'active': fields.boolean('Active'),
1206 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1207 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1208 '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."),
1209 'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'),
1210 'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'),
1211 'parent_id':fields.many2one('account.tax', 'Parent Tax Account', select=True),
1212 'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'),
1213 '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."),
1214 'python_compute':fields.text('Python Code'),
1215 'python_compute_inv':fields.text('Python Code (reverse)'),
1216 'python_applicable':fields.text('Python Code'),
1217 '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."),
1220 # Fields used for the VAT declaration
1222 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."),
1223 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."),
1224 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1225 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1227 # Same fields for refund invoices
1229 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the VAT declaration."),
1230 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1231 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1232 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1233 '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"),
1234 'company_id': fields.many2one('res.company', 'Company', required=True),
1235 'description': fields.char('Internal Name',size=32),
1236 'price_include': fields.boolean('Tax Included in Price', help="Check this is the price you use on the product and invoices is including this tax.")
1239 def name_get(self, cr, uid, ids, context={}):
1243 for record in self.read(cr, uid, ids, ['description','name'], context):
1244 name = record['description'] and record['description'] or record['name']
1245 res.append((record['id'],name ))
1248 def _default_company(self, cr, uid, context={}):
1249 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1251 return user.company_id.id
1252 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1254 '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''',
1255 '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''',
1256 'applicable_type': lambda *a: 'true',
1257 'type': lambda *a: 'percent',
1258 'amount': lambda *a: 0,
1259 'price_include': lambda *a: 0,
1260 'active': lambda *a: 1,
1261 'sequence': lambda *a: 1,
1262 'tax_group': lambda *a: 'vat',
1263 'ref_tax_sign': lambda *a: 1,
1264 'ref_base_sign': lambda *a: 1,
1265 'tax_sign': lambda *a: 1,
1266 'base_sign': lambda *a: 1,
1267 'include_base_amount': lambda *a: False,
1268 'company_id': _default_company,
1272 def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1275 if tax.applicable_type=='code':
1276 localdict = {'price_unit':price_unit, 'address':self.pool.get('res.partner.address').browse(cr, uid, address_id), 'product':product, 'partner':partner}
1277 exec tax.python_applicable in localdict
1278 if localdict.get('result', False):
1284 def _unit_compute(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1285 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1288 cur_price_unit=price_unit
1290 # we compute the amount for the current tax object and append it to the result
1292 data = {'id':tax.id,
1294 'account_collected_id':tax.account_collected_id.id,
1295 'account_paid_id':tax.account_paid_id.id,
1296 'base_code_id': tax.base_code_id.id,
1297 'ref_base_code_id': tax.ref_base_code_id.id,
1298 'sequence': tax.sequence,
1299 'base_sign': tax.base_sign,
1300 'tax_sign': tax.tax_sign,
1301 'ref_base_sign': tax.ref_base_sign,
1302 'ref_tax_sign': tax.ref_tax_sign,
1303 'price_unit': cur_price_unit,
1304 'tax_code_id': tax.tax_code_id.id,
1305 'ref_tax_code_id': tax.ref_tax_code_id.id,
1308 if tax.type=='percent':
1309 amount = cur_price_unit * tax.amount
1310 data['amount'] = amount
1312 elif tax.type=='fixed':
1313 data['amount'] = tax.amount
1314 elif tax.type=='code':
1315 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1316 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1317 exec tax.python_compute in localdict
1318 amount = localdict['result']
1319 data['amount'] = amount
1320 amount2 = data['amount']
1321 if len(tax.child_ids):
1322 if tax.child_depend:
1325 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner)
1326 res.extend(child_tax)
1327 if tax.include_base_amount:
1328 cur_price_unit+=amount2
1331 def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1334 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1338 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1339 one tax for each tax id in IDS and their childs
1341 res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner)
1343 r['amount'] *= quantity
1346 def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1347 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1351 cur_price_unit=price_unit
1353 tax_parent_tot = 0.0
1355 if (tax.type=='percent') and not tax.include_base_amount:
1356 tax_parent_tot+=tax.amount
1359 if tax.type=='percent':
1360 if tax.include_base_amount:
1361 amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
1363 amount = (cur_price_unit / (1 + tax_parent_tot)) * tax.amount
1365 elif tax.type=='fixed':
1368 elif tax.type=='code':
1369 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1370 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1371 exec tax.python_compute_inv in localdict
1372 amount = localdict['result']
1374 if tax.include_base_amount:
1375 cur_price_unit -= amount
1384 'account_collected_id': tax.account_collected_id.id,
1385 'account_paid_id': tax.account_paid_id.id,
1386 'base_code_id': tax.base_code_id.id,
1387 'ref_base_code_id': tax.ref_base_code_id.id,
1388 'sequence': tax.sequence,
1389 'base_sign': tax.base_sign,
1390 'tax_sign': tax.tax_sign,
1391 'ref_base_sign': tax.ref_base_sign,
1392 'ref_tax_sign': tax.ref_tax_sign,
1393 'price_unit': cur_price_unit,
1394 'tax_code_id': tax.tax_code_id.id,
1395 'ref_tax_code_id': tax.ref_tax_code_id.id,
1397 if len(tax.child_ids):
1398 if tax.child_depend:
1402 parent_tax = self._unit_compute_inv(cr, uid, tax.child_ids, amount, address_id, product, partner)
1403 res.extend(parent_tax)
1408 total += r['amount']
1410 r['price_unit'] -= total
1414 def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1416 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1417 Price Unit is a VAT included price
1421 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1422 one tax for each tax id in IDS and their childs
1424 res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
1426 r['amount'] *= quantity
1430 # ---------------------------------------------------------
1431 # Account Entries Models
1432 # ---------------------------------------------------------
1434 class account_model(osv.osv):
1435 _name = "account.model"
1436 _description = "Account Model"
1438 'name': fields.char('Model Name', size=64, required=True, help="This is a model for recurring accounting entries"),
1439 'ref': fields.char('Ref', size=64),
1440 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
1441 'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'),
1442 'legend' :fields.text('Legend',readonly=True,size=100),
1446 'legend': lambda self, cr, uid, context:_('You can specify year, month and date in the name of the model using the following labels:\n\n%(year)s : To Specify Year \n%(month)s : To Specify Month \n%(date)s : Current Date\n\ne.g. My model on %(date)s'),
1452 class account_model_line(osv.osv):
1453 _name = "account.model.line"
1454 _description = "Account Model Entries"
1456 'name': fields.char('Name', size=64, required=True),
1457 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the resources from the lowest sequences to the higher ones"),
1458 'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity on entries"),
1459 'debit': fields.float('Debit', digits=(16,2)),
1460 'credit': fields.float('Credit', digits=(16,2)),
1462 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"),
1464 'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True),
1466 'ref': fields.char('Ref.', size=16),
1468 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."),
1469 'currency_id': fields.many2one('res.currency', 'Currency'),
1471 'partner_id': fields.many2one('res.partner', 'Partner Ref.'),
1472 '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."),
1473 'date': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Current Date', required=True, help="The date of the generated entries"),
1476 'date': lambda *a: 'today'
1479 _sql_constraints = [
1480 ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in model !'),
1481 ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model !'),
1483 account_model_line()
1485 # ---------------------------------------------------------
1486 # Account Subscription
1487 # ---------------------------------------------------------
1490 class account_subscription(osv.osv):
1491 _name = "account.subscription"
1492 _description = "Account Subscription"
1494 'name': fields.char('Name', size=64, required=True),
1495 'ref': fields.char('Ref.', size=16),
1496 'model_id': fields.many2one('account.model', 'Model', required=True),
1498 'date_start': fields.date('Starting date', required=True),
1499 'period_total': fields.integer('Number of period', required=True),
1500 'period_nbr': fields.integer('Period', required=True),
1501 'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True),
1502 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status', required=True, readonly=True),
1504 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines')
1507 'date_start': lambda *a: time.strftime('%Y-%m-%d'),
1508 'period_type': lambda *a: 'month',
1509 'period_total': lambda *a: 12,
1510 'period_nbr': lambda *a: 1,
1511 'state': lambda *a: 'draft',
1513 def state_draft(self, cr, uid, ids, context={}):
1514 self.write(cr, uid, ids, {'state':'draft'})
1517 def check(self, cr, uid, ids, context={}):
1519 for sub in self.browse(cr, uid, ids, context):
1521 for line in sub.lines_id:
1522 if not line.move_id.id:
1526 todone.append(sub.id)
1528 self.write(cr, uid, todone, {'state':'done'})
1531 def remove_line(self, cr, uid, ids, context={}):
1533 for sub in self.browse(cr, uid, ids, context):
1534 for line in sub.lines_id:
1535 if not line.move_id.id:
1536 toremove.append(line.id)
1538 self.pool.get('account.subscription.line').unlink(cr, uid, toremove)
1539 self.write(cr, uid, ids, {'state':'draft'})
1542 def compute(self, cr, uid, ids, context={}):
1543 for sub in self.browse(cr, uid, ids, context):
1545 for i in range(sub.period_total):
1546 self.pool.get('account.subscription.line').create(cr, uid, {
1548 'subscription_id': sub.id,
1550 if sub.period_type=='day':
1551 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(days=sub.period_nbr)).strftime('%Y-%m-%d')
1552 if sub.period_type=='month':
1553 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(months=sub.period_nbr)).strftime('%Y-%m-%d')
1554 if sub.period_type=='year':
1555 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(years=sub.period_nbr)).strftime('%Y-%m-%d')
1556 self.write(cr, uid, ids, {'state':'running'})
1558 account_subscription()
1560 class account_subscription_line(osv.osv):
1561 _name = "account.subscription.line"
1562 _description = "Account Subscription Line"
1564 'subscription_id': fields.many2one('account.subscription', 'Subscription', required=True, select=True),
1565 'date': fields.date('Date', required=True),
1566 'move_id': fields.many2one('account.move', 'Entry'),
1570 def move_create(self, cr, uid, ids, context={}):
1572 for line in self.browse(cr, uid, ids, context):
1576 ids = self.pool.get('account.model').generate(cr, uid, [line.subscription_id.model_id.id], datas, context)
1577 tocheck[line.subscription_id.id] = True
1578 self.write(cr, uid, [line.id], {'move_id':ids[0]})
1580 self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context)
1583 account_subscription_line()
1586 class account_config_wizard(osv.osv_memory):
1587 _name = 'account.config.wizard'
1589 def _get_charts(self, cr, uid, context):
1590 module_obj=self.pool.get('ir.module.module')
1591 ids=module_obj.search(cr, uid, [('category_id', '=', 'Account Charts'), ('state', '<>', 'installed')])
1592 res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)]
1593 res.append((-1, 'None'))
1594 res.sort(key=lambda x: x[1])
1598 'name':fields.char('Name', required=True, size=64, help="Name of the fiscal year as displayed on screens."),
1599 'code':fields.char('Code', required=True, size=64, help="Name of the fiscal year as displayed in reports."),
1600 'date1': fields.date('Starting Date', required=True),
1601 'date2': fields.date('Ending Date', required=True),
1602 'period':fields.selection([('month','Month'),('3months','3 Months')], 'Periods', required=True),
1603 'charts' : fields.selection(_get_charts, 'Charts of Account',required=True)
1606 'code': lambda *a: time.strftime('%Y'),
1607 'name': lambda *a: time.strftime('%Y'),
1608 'date1': lambda *a: time.strftime('%Y-01-01'),
1609 'date2': lambda *a: time.strftime('%Y-12-31'),
1610 'period':lambda *a:'month',
1612 def action_cancel(self,cr,uid,ids,conect=None):
1614 'view_type': 'form',
1615 "view_mode": 'form',
1616 'res_model': 'ir.actions.configuration.wizard',
1617 'type': 'ir.actions.act_window',
1621 def install_account_chart(self, cr, uid, ids, context=None):
1622 for res in self.read(cr,uid,ids):
1623 chart_id = res['charts']
1625 mod_obj = self.pool.get('ir.module.module')
1626 mod_obj.button_install(cr, uid, [chart_id], context=context)
1628 db, pool = pooler.restart_pool(cr.dbname, update_module=True)
1630 def action_create(self, cr, uid,ids, context=None):
1631 for res in self.read(cr,uid,ids):
1632 if 'date1' in res and 'date2' in res:
1633 res_obj = self.pool.get('account.fiscalyear')
1634 start_date=res['date1']
1635 end_date=res['date2']
1636 name=res['name']#DateTime.strptime(start_date, '%Y-%m-%d').strftime('%m.%Y') + '-' + DateTime.strptime(end_date, '%Y-%m-%d').strftime('%m.%Y')
1640 'date_start':start_date,
1641 'date_stop':end_date,
1643 new_id=res_obj.create(cr, uid, vals, context=context)
1644 if res['period']=='month':
1645 res_obj.create_period(cr,uid,[new_id])
1646 elif res['period']=='3months':
1647 res_obj.create_period3(cr,uid,[new_id])
1648 self.install_account_chart(cr,uid,ids)
1650 'view_type': 'form',
1651 "view_mode": 'form',
1652 'res_model': 'ir.actions.configuration.wizard',
1653 'type': 'ir.actions.act_window',
1659 account_config_wizard()
1662 # ---------------------------------------------------------------
1663 # Account Templates : Account, Tax, Tax Code and chart. + Wizard
1664 # ---------------------------------------------------------------
1666 class account_tax_template(osv.osv):
1667 _name = 'account.tax.template'
1668 account_tax_template()
1670 class account_account_template(osv.osv):
1672 _name = "account.account.template"
1673 _description ='Templates for Accounts'
1676 'name': fields.char('Name', size=128, required=True, select=True),
1677 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
1678 'code': fields.char('Code', size=64),
1679 'type': fields.selection([
1680 ('receivable','Receivable'),
1681 ('payable','Payable'),
1683 ('consolidation','Consolidation'),
1685 ('closed','Closed'),
1686 ], 'Internal Type', required=True,),
1687 'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
1688 'reconcile': fields.boolean('Allow Reconciliation', help="Check this option if the user can make a reconciliation of the entries in this account."),
1689 'shortcut': fields.char('Shortcut', size=12),
1690 'note': fields.text('Note'),
1691 'parent_id': fields.many2one('account.account.template','Parent Account Template', ondelete='cascade'),
1692 'child_parent_ids':fields.one2many('account.account.template','parent_id','Children'),
1693 'tax_ids': fields.many2many('account.tax.template', 'account_account_template_tax_rel','account_id','tax_id', 'Default Taxes'),
1697 'reconcile': lambda *a: False,
1698 'type' : lambda *a :'view',
1701 def _check_recursion(self, cr, uid, ids):
1704 cr.execute('select parent_id from account_account_template where id in ('+','.join(map(str,ids))+')')
1705 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1712 (_check_recursion, 'Error ! You can not create recursive account templates.', ['parent_id'])
1716 def name_get(self, cr, uid, ids, context={}):
1719 reads = self.read(cr, uid, ids, ['name','code'], context)
1721 for record in reads:
1722 name = record['name']
1724 name = record['code']+' '+name
1725 res.append((record['id'],name ))
1728 account_account_template()
1730 class account_tax_code_template(osv.osv):
1732 _name = 'account.tax.code.template'
1733 _description = 'Tax Code Template'
1737 'name': fields.char('Tax Case Name', size=64, required=True),
1738 'code': fields.char('Case Code', size=64),
1739 'info': fields.text('Description'),
1740 'parent_id': fields.many2one('account.tax.code.template', 'Parent Code', select=True),
1741 'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Childs Codes'),
1742 'sign': fields.float('Sign for parent', required=True),
1746 'sign': lambda *args: 1.0,
1749 def name_get(self, cr, uid, ids, context=None):
1752 if isinstance(ids, (int, long)):
1754 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1755 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1758 def _check_recursion(self, cr, uid, ids):
1761 cr.execute('select distinct parent_id from account_tax_code_template where id in ('+','.join(map(str,ids))+')')
1762 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1769 (_check_recursion, 'Error ! You can not create recursive Tax Codes.', ['parent_id'])
1771 _order = 'code,name'
1772 account_tax_code_template()
1775 class account_chart_template(osv.osv):
1776 _name="account.chart.template"
1777 _description= "Templates for Account Chart"
1780 'name': fields.char('Name', size=64, required=True),
1781 'account_root_id': fields.many2one('account.account.template','Root Account',required=True,domain=[('parent_id','=',False)]),
1782 'tax_code_root_id': fields.many2one('account.tax.code.template','Root Tax Code',required=True,domain=[('parent_id','=',False)]),
1783 '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'),
1784 'bank_account_view_id': fields.many2one('account.account.template','Bank Account',required=True),
1785 'property_account_receivable': fields.many2one('account.account.template','Receivable Account'),
1786 'property_account_payable': fields.many2one('account.account.template','Payable Account'),
1787 'property_account_expense_categ': fields.many2one('account.account.template','Expense Category Account'),
1788 'property_account_income_categ': fields.many2one('account.account.template','Income Category Account'),
1789 'property_account_expense': fields.many2one('account.account.template','Expense Account on Product Template'),
1790 'property_account_income': fields.many2one('account.account.template','Income Account on Product Template'),
1793 account_chart_template()
1795 class account_tax_template(osv.osv):
1797 _name = 'account.tax.template'
1798 _description = 'Templates for Taxes'
1801 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
1802 'name': fields.char('Tax Name', size=64, required=True),
1803 '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."),
1804 'amount': fields.float('Amount', required=True, digits=(14,4)),
1805 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1806 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1807 '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."),
1808 'account_collected_id':fields.many2one('account.account.template', 'Invoice Tax Account'),
1809 'account_paid_id':fields.many2one('account.account.template', 'Refund Tax Account'),
1810 'parent_id':fields.many2one('account.tax.template', 'Parent Tax Account', select=True),
1811 '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."),
1812 'python_compute':fields.text('Python Code'),
1813 'python_compute_inv':fields.text('Python Code (reverse)'),
1814 'python_applicable':fields.text('Python Code'),
1815 '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."),
1818 # Fields used for the VAT declaration
1820 'base_code_id': fields.many2one('account.tax.code.template', 'Base Code', help="Use this code for the VAT declaration."),
1821 'tax_code_id': fields.many2one('account.tax.code.template', 'Tax Code', help="Use this code for the VAT declaration."),
1822 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1823 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1825 # Same fields for refund invoices
1827 'ref_base_code_id': fields.many2one('account.tax.code.template', 'Refund Base Code', help="Use this code for the VAT declaration."),
1828 'ref_tax_code_id': fields.many2one('account.tax.code.template', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1829 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1830 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1831 '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."),
1832 'description': fields.char('Internal Name', size=32),
1835 def name_get(self, cr, uid, ids, context={}):
1839 for record in self.read(cr, uid, ids, ['description','name'], context):
1840 name = record['description'] and record['description'] or record['name']
1841 res.append((record['id'],name ))
1844 def _default_company(self, cr, uid, context={}):
1845 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1847 return user.company_id.id
1848 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1851 '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''',
1852 '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''',
1853 'applicable_type': lambda *a: 'true',
1854 'type': lambda *a: 'percent',
1855 'amount': lambda *a: 0,
1856 'sequence': lambda *a: 1,
1857 'tax_group': lambda *a: 'vat',
1858 'ref_tax_sign': lambda *a: 1,
1859 'ref_base_sign': lambda *a: 1,
1860 'tax_sign': lambda *a: 1,
1861 'base_sign': lambda *a: 1,
1862 'include_base_amount': lambda *a: False,
1867 account_tax_template()
1869 # Fiscal Position Templates
1871 class account_fiscal_position_template(osv.osv):
1872 _name = 'account.fiscal.position.template'
1873 _description = 'Template for Fiscal Position'
1876 'name': fields.char('Fiscal Position Template', size=64, translate=True, required=True),
1877 'account_ids': fields.one2many('account.fiscal.position.account.template', 'position_id', 'Accounts Mapping'),
1878 'tax_ids': fields.one2many('account.fiscal.position.tax.template', 'position_id', 'Taxes Mapping')
1881 account_fiscal_position_template()
1883 class account_fiscal_position_tax_template(osv.osv):
1884 _name = 'account.fiscal.position.tax.template'
1885 _description = 'Fiscal Position Template Taxes Mapping'
1886 _rec_name = 'position_id'
1889 'position_id': fields.many2one('account.fiscal.position.template', 'Fiscal Position', required=True, ondelete='cascade'),
1890 'tax_src_id': fields.many2one('account.tax.template', 'Tax Source', required=True),
1891 'tax_dest_id': fields.many2one('account.tax.template', 'Replacement Tax')
1894 account_fiscal_position_tax_template()
1896 class account_fiscal_position_account_template(osv.osv):
1897 _name = 'account.fiscal.position.account.template'
1898 _description = 'Fiscal Position Template Accounts Mapping'
1899 _rec_name = 'position_id'
1901 'position_id': fields.many2one('account.fiscal.position.template', 'Fiscal Position', required=True, ondelete='cascade'),
1902 'account_src_id': fields.many2one('account.account.template', 'Account Source', domain=[('type','<>','view')], required=True),
1903 'account_dest_id': fields.many2one('account.account.template', 'Account Destination', domain=[('type','<>','view')], required=True)
1906 account_fiscal_position_account_template()
1908 # Multi charts of Accounts wizard
1910 class wizard_multi_charts_accounts(osv.osv_memory):
1912 Create a new account chart for a company.
1915 * an account chart template
1916 * a number of digits for formatting code of non-view accounts
1917 * a list of bank account owned by the company
1919 * generates all accounts from the template and assign them to the right company
1920 * generates all taxes and tax codes, changing account assignations
1921 * generates all accounting properties and assign correctly
1923 _name='wizard.multi.charts.accounts'
1926 'company_id':fields.many2one('res.company','Company',required=True),
1927 'chart_template_id': fields.many2one('account.chart.template','Chart Template',required=True),
1928 'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Bank Accounts',required=True),
1929 'code_digits':fields.integer('# of Digits',required=True,help="No. of Digits to use for account code"),
1932 def _get_chart(self, cr, uid, context={}):
1933 ids = self.pool.get('account.chart.template').search(cr, uid, [], context=context)
1938 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr,uid,[uid],c)[0].company_id.id,
1939 'chart_template_id': _get_chart,
1940 'code_digits': lambda *a:6,
1943 def action_create(self, cr, uid, ids, context=None):
1944 obj_multi = self.browse(cr,uid,ids[0])
1945 obj_acc = self.pool.get('account.account')
1946 obj_acc_tax = self.pool.get('account.tax')
1947 obj_journal = self.pool.get('account.journal')
1948 obj_acc_template = self.pool.get('account.account.template')
1949 obj_fiscal_position_template = self.pool.get('account.fiscal.position.template')
1950 obj_fiscal_position = self.pool.get('account.fiscal.position')
1953 obj_acc_root = obj_multi.chart_template_id.account_root_id
1954 tax_code_root_id = obj_multi.chart_template_id.tax_code_root_id.id
1955 company_id = obj_multi.company_id.id
1958 acc_template_ref = {}
1959 tax_template_ref = {}
1960 tax_code_template_ref = {}
1963 #create all the tax code
1964 children_tax_code_template = self.pool.get('account.tax.code.template').search(cr, uid, [('parent_id','child_of',[tax_code_root_id])], order='id')
1965 for tax_code_template in self.pool.get('account.tax.code.template').browse(cr, uid, children_tax_code_template):
1967 'name': (tax_code_root_id == tax_code_template.id) and obj_multi.company_id.name or tax_code_template.name,
1968 'code': tax_code_template.code,
1969 'info': tax_code_template.info,
1970 '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,
1971 'company_id': company_id,
1972 'sign': tax_code_template.sign,
1974 new_tax_code = self.pool.get('account.tax.code').create(cr,uid,vals)
1975 #recording the new tax code to do the mapping
1976 tax_code_template_ref[tax_code_template.id] = new_tax_code
1979 for tax in obj_multi.chart_template_id.tax_template_ids:
1983 'sequence': tax.sequence,
1984 'amount':tax.amount,
1986 'applicable_type': tax.applicable_type,
1987 'domain':tax.domain,
1988 'parent_id': tax.parent_id and ((tax.parent_id.id in tax_template_ref) and tax_template_ref[tax.parent_id.id]) or False,
1989 'child_depend': tax.child_depend,
1990 'python_compute': tax.python_compute,
1991 'python_compute_inv': tax.python_compute_inv,
1992 'python_applicable': tax.python_applicable,
1993 'tax_group':tax.tax_group,
1994 'base_code_id': tax.base_code_id and ((tax.base_code_id.id in tax_code_template_ref) and tax_code_template_ref[tax.base_code_id.id]) or False,
1995 'tax_code_id': tax.tax_code_id and ((tax.tax_code_id.id in tax_code_template_ref) and tax_code_template_ref[tax.tax_code_id.id]) or False,
1996 'base_sign': tax.base_sign,
1997 'tax_sign': tax.tax_sign,
1998 'ref_base_code_id': tax.ref_base_code_id and ((tax.ref_base_code_id.id in tax_code_template_ref) and tax_code_template_ref[tax.ref_base_code_id.id]) or False,
1999 'ref_tax_code_id': tax.ref_tax_code_id and ((tax.ref_tax_code_id.id in tax_code_template_ref) and tax_code_template_ref[tax.ref_tax_code_id.id]) or False,
2000 'ref_base_sign': tax.ref_base_sign,
2001 'ref_tax_sign': tax.ref_tax_sign,
2002 'include_base_amount': tax.include_base_amount,
2003 'description':tax.description,
2004 'company_id': company_id,
2006 new_tax = obj_acc_tax.create(cr,uid,vals_tax)
2007 #as the accounts have not been created yet, we have to wait before filling these fields
2008 todo_dict[new_tax] = {
2009 'account_collected_id': tax.account_collected_id and tax.account_collected_id.id or False,
2010 'account_paid_id': tax.account_paid_id and tax.account_paid_id.id or False,
2012 tax_template_ref[tax.id] = new_tax
2014 #deactivate the parent_store functionnality on account_account for rapidity purpose
2015 self.pool._init = True
2017 children_acc_template = obj_acc_template.search(cr, uid, [('parent_id','child_of',[obj_acc_root.id])])
2018 children_acc_template.sort()
2019 for account_template in obj_acc_template.browse(cr, uid, children_acc_template):
2021 for tax in account_template.tax_ids:
2022 tax_ids.append(tax_template_ref[tax.id])
2023 #create the account_account
2025 dig = obj_multi.code_digits
2026 code_main = account_template.code and len(account_template.code) or 0
2027 code_acc = account_template.code or ''
2028 if code_main>0 and code_main<=dig and account_template.type != 'view':
2029 code_acc=str(code_acc) + (str('0'*(dig-code_main)))
2031 'name': (obj_acc_root.id == account_template.id) and obj_multi.company_id.name or account_template.name,
2032 #'sign': account_template.sign,
2033 'currency_id': account_template.currency_id and account_template.currency_id.id or False,
2035 'type': account_template.type,
2036 'user_type': account_template.user_type and account_template.user_type.id or False,
2037 'reconcile': account_template.reconcile,
2038 'shortcut': account_template.shortcut,
2039 'note': account_template.note,
2040 '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,
2041 'tax_ids': [(6,0,tax_ids)],
2042 'company_id': company_id,
2044 new_account = obj_acc.create(cr,uid,vals)
2045 acc_template_ref[account_template.id] = new_account
2046 #reactivate the parent_store functionnality on account_account
2047 self.pool._init = False
2048 self.pool.get('account.account')._parent_store_compute(cr)
2050 for key,value in todo_dict.items():
2051 if value['account_collected_id'] or value['account_paid_id']:
2052 obj_acc_tax.write(cr, uid, [key], vals={
2053 'account_collected_id': acc_template_ref[value['account_collected_id']],
2054 'account_paid_id': acc_template_ref[value['account_paid_id']],
2059 view_id = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Journal View')])[0]
2060 seq_id = self.pool.get('ir.sequence').search(cr,uid,[('code','=','account.journal')])[0]
2061 seq_code = self.pool.get('ir.sequence').get(cr, uid, 'account.journal')
2063 vals_journal['view_id']=view_id
2064 vals_journal['sequence_id']=seq_id
2067 vals_journal['name'] = _('Sales Journal')
2068 vals_journal['type'] = 'sale'
2069 vals_journal['code'] = _('SAJ')
2071 if obj_multi.chart_template_id.property_account_receivable:
2072 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_receivable.id]
2073 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_receivable.id]
2075 obj_journal.create(cr,uid,vals_journal)
2078 vals_journal['name']=_('Purchase Journal')
2079 vals_journal['type']='purchase'
2080 vals_journal['code']=_('EXJ')
2082 if obj_multi.chart_template_id.property_account_payable:
2083 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_payable.id]
2084 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_payable.id]
2086 obj_journal.create(cr,uid,vals_journal)
2089 view_id_cash = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Cash Journal View')])[0]
2090 view_id_cur = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Multi-Currency Cash Journal View')])[0]
2091 ref_acc_bank = obj_multi.chart_template_id.bank_account_view_id
2094 for line in obj_multi.bank_accounts_id:
2095 #create the account_account for this bank journal
2096 tmp = self.pool.get('res.partner.bank').name_get(cr, uid, [line.acc_no.id])[0][1]
2097 dig = obj_multi.code_digits
2099 'name': line.acc_no.bank and line.acc_no.bank.name+' '+tmp or tmp,
2100 'currency_id': line.currency_id and line.currency_id.id or False,
2101 'code': str(ref_acc_bank.code.ljust(dig,'0') + str(current_num)),
2103 'user_type': account_template.user_type and account_template.user_type.id or False,
2105 'parent_id': acc_template_ref[ref_acc_bank.id] or False,
2106 'company_id': company_id,
2108 acc_cash_id = obj_acc.create(cr,uid,vals)
2110 #create the bank journal
2111 vals_journal['name']= vals['name']
2112 vals_journal['code']= _('BNK') + str(current_num)
2113 vals_journal['sequence_id'] = seq_id
2114 vals_journal['type'] = 'cash'
2115 if line.currency_id:
2116 vals_journal['view_id'] = view_id_cur
2117 vals_journal['currency'] = line.currency_id.id
2119 vals_journal['view_id'] = view_id_cash
2120 vals_journal['default_credit_account_id'] = acc_cash_id
2121 vals_journal['default_debit_account_id']= acc_cash_id
2122 obj_journal.create(cr,uid,vals_journal)
2126 #create the properties
2127 property_obj = self.pool.get('ir.property')
2128 fields_obj = self.pool.get('ir.model.fields')
2131 ('property_account_receivable','res.partner','account.account'),
2132 ('property_account_payable','res.partner','account.account'),
2133 ('property_account_expense_categ','product.category','account.account'),
2134 ('property_account_income_categ','product.category','account.account'),
2135 ('property_account_expense','product.template','account.account'),
2136 ('property_account_income','product.template','account.account')
2138 for record in todo_list:
2140 r = property_obj.search(cr, uid, [('name','=', record[0] ),('company_id','=',company_id)])
2141 account = getattr(obj_multi.chart_template_id, record[0])
2142 field = fields_obj.search(cr, uid, [('name','=',record[0]),('model','=',record[1]),('relation','=',record[2])])
2145 'company_id': company_id,
2146 'fields_id': field[0],
2147 'value': account and 'account.account,'+str(acc_template_ref[account.id]) or False,
2150 #the property exist: modify it
2151 property_obj.write(cr, uid, r, vals)
2153 #create the property
2154 property_obj.create(cr, uid, vals)
2156 fp_ids = obj_fiscal_position_template.search(cr, uid,[])
2159 for position in obj_fiscal_position_template.browse(cr, uid, fp_ids):
2162 'company_id' : company_id,
2163 'name' : position.name,
2165 new_fp = obj_fiscal_position.create(cr, uid, vals_fp)
2167 obj_tax_fp = self.pool.get('account.fiscal.position.tax')
2168 obj_ac_fp = self.pool.get('account.fiscal.position.account')
2170 for tax in position.tax_ids:
2172 'tax_src_id' : tax_template_ref[tax.tax_src_id.id],
2173 'tax_dest_id' : tax_template_ref[tax.tax_dest_id.id],
2174 'position_id' : new_fp,
2176 obj_tax_fp.create(cr, uid, vals_tax)
2178 for acc in position.account_ids:
2180 'account_src_id' : acc_template_ref[acc.account_src_id.id],
2181 'account_dest_id' : acc_template_ref[acc.account_dest_id.id],
2182 'position_id' : new_fp,
2184 obj_ac_fp.create(cr, uid, vals_acc)
2187 'view_type': 'form',
2188 "view_mode": 'form',
2189 'res_model': 'ir.actions.configuration.wizard',
2190 'type': 'ir.actions.act_window',
2193 def action_cancel(self,cr,uid,ids,conect=None):
2195 'view_type': 'form',
2196 "view_mode": 'form',
2197 'res_model': 'ir.actions.configuration.wizard',
2198 'type': 'ir.actions.act_window',
2203 wizard_multi_charts_accounts()
2205 class account_bank_accounts_wizard(osv.osv_memory):
2206 _name='account.bank.accounts.wizard'
2209 'acc_no':fields.many2one('res.partner.bank','Account No.',required=True),
2210 'bank_account_id':fields.many2one('wizard.multi.charts.accounts', 'Bank Account', required=True),
2211 'currency_id':fields.many2one('res.currency', 'Currency'),
2214 account_bank_accounts_wizard()
2216 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: