1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
24 from osv import fields, osv
26 from tools.misc import currency
27 from tools.translate import _
30 from mx.DateTime import RelativeDateTime, now, DateTime, localtime
33 class account_payment_term(osv.osv):
34 _name = "account.payment.term"
35 _description = "Payment Term"
37 'name': fields.char('Payment Term', size=32, translate=True, required=True),
38 'active': fields.boolean('Active'),
39 'note': fields.text('Description', translate=True),
40 'line_ids': fields.one2many('account.payment.term.line', 'payment_id', 'Terms'),
43 'active': lambda *a: 1,
47 def compute(self, cr, uid, id, value, date_ref=False, context={}):
49 date_ref = now().strftime('%Y-%m-%d')
50 pt = self.browse(cr, uid, id, context)
53 for line in pt.line_ids:
54 if line.value=='fixed':
55 amt = round(line.value_amount, 2)
56 elif line.value=='procent':
57 amt = round(value * line.value_amount, 2)
58 elif line.value=='balance':
59 amt = round(amount, 2)
61 next_date = mx.DateTime.strptime(date_ref, '%Y-%m-%d') + RelativeDateTime(days=line.days)
63 next_date += RelativeDateTime(day=line.days2)
65 next_date += RelativeDateTime(day=line.days2, months=1)
66 result.append( (next_date.strftime('%Y-%m-%d'), amt) )
70 account_payment_term()
72 class account_payment_term_line(osv.osv):
73 _name = "account.payment.term.line"
74 _description = "Payment Term Line"
76 'name': fields.char('Line Name', size=32,required=True),
77 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the payment term lines from the lowest sequences to the higher ones"),
78 'value': fields.selection([('procent','Percent'),('balance','Balance'),('fixed','Fixed Amount')], 'Value',required=True),
79 'value_amount': fields.float('Value Amount'),
80 'days': fields.integer('Number of Days',required=True, help="Number of days to add before computation of the day of month."),
81 'days2': fields.integer('Day of the Month',required=True, help="Day of the month, set -1 for the last day of the current month. If it's positive, it gives the day of the next month. Set 0 for net days (otherwise it's based on the end of the month)."),
82 'payment_id': fields.many2one('account.payment.term','Payment Term', required=True, select=True),
85 'value': lambda *a: 'balance',
86 'sequence': lambda *a: 5,
87 'days2': lambda *a: 0,
90 account_payment_term_line()
93 class account_account_type(osv.osv):
94 _name = "account.account.type"
95 _description = "Account Type"
97 'name': fields.char('Acc. Type Name', size=64, required=True, translate=True),
98 'code': fields.char('Code', size=32, required=True),
99 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of account types."),
100 'partner_account': fields.boolean('Partner account'),
101 'close_method': fields.selection([('none','None'), ('balance','Balance'), ('detail','Detail'),('unreconciled','Unreconciled')], 'Deferral Method', required=True),
102 'sign': fields.selection([(-1, 'Negative'), (1, 'Positive')], 'Sign on Reports', required=True, help='Allows to change the displayed amount of the balance in the reports, in order to see positive results instead of negative ones in expenses accounts.'),
105 'close_method': lambda *a: 'none',
106 'sequence': lambda *a: 5,
107 'sign': lambda *a: 1,
110 account_account_type()
112 def _code_get(self, cr, uid, context={}):
113 acc_type_obj = self.pool.get('account.account.type')
114 ids = acc_type_obj.search(cr, uid, [])
115 res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context)
116 return [(r['code'], r['name']) for r in res]
118 #----------------------------------------------------------
120 #----------------------------------------------------------
122 class account_tax(osv.osv):
123 _name = 'account.tax'
126 class account_account(osv.osv):
127 _order = "parent_left"
128 _parent_order = "code"
129 _name = "account.account"
130 _description = "Account"
133 def search(self, cr, uid, args, offset=0, limit=None, order=None,
134 context=None, count=False):
141 if args[pos][0]=='code' and args[pos][1] in ('like','ilike') and args[pos][2]:
142 args[pos] = ('code', '=like', str(args[pos][2].replace('%',''))+'%')
143 if args[pos][0]=='journal_id':
147 jour = self.pool.get('account.journal').browse(cr, uid, args[pos][2])
148 if (not (jour.account_control_ids or jour.type_control_ids)) or not args[pos][2]:
151 ids3 = map(lambda x: x.code, jour.type_control_ids)
152 ids1 = super(account_account,self).search(cr, uid, [('type','in',ids3)])
153 ids1 += map(lambda x: x.id, jour.account_control_ids)
154 args[pos] = ('id','in',ids1)
157 if context and context.has_key('consolidate_childs'): #add concolidated 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 res = self.search(cr, uid, [('parent_id', 'child_of', ids)])
171 this = self.browse(cr, uid, id, context)
172 for child in this.child_consol_ids:
173 if child.id not in res:
175 if len(res) != len(ids):
176 return self._get_children_and_consol(cr, uid, res, context)
179 def __compute(self, cr, uid, ids, field_names, arg, context={}, query=''):
180 #compute the balance/debit/credit accordingly to the value of field_name for the given account ids
182 'balance': "COALESCE(SUM(l.debit) - SUM(l.credit), 0) as balance ",
183 'debit': "COALESCE(SUM(l.debit), 0) as debit ",
184 'credit': "COALESCE(SUM(l.credit), 0) as credit "
186 #get all the necessary accounts
187 ids2 = self._get_children_and_consol(cr, uid, ids, context)
188 acc_set = ",".join(map(str, ids2))
189 #compute for each account the balance/debit/credit from the move lines
192 query = self.pool.get('account.move.line')._query_get(cr, uid,
194 cr.execute(("SELECT l.account_id as id, " +\
195 ' , '.join(map(lambda x: mapping[x], field_names)) +
197 "account_move_line l " \
199 "l.account_id IN (%s) " \
200 "AND " + query + " " \
201 "GROUP BY l.account_id") % (acc_set, ))
203 for res in cr.dictfetchall():
204 accounts[res['id']] = res
206 #for the asked accounts, get from the dictionnary 'accounts' the value of it
209 res[id] = self._get_account_values(cr, uid, id, accounts, field_names, context)
212 def _get_account_values(self, cr, uid, id, accounts, field_names, context={}):
213 res = {}.fromkeys(field_names, 0.0)
214 browse_rec = self.browse(cr, uid, id)
215 if browse_rec.type == 'consolidation':
216 ids2 = self.read(cr, uid, [browse_rec.id], ['child_consol_ids'], context)[0]['child_consol_ids']
217 for t in self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])]):
218 if t not in ids2 and t != browse_rec.id:
221 tmp = self._get_account_values(cr, uid, i, accounts, field_names, context)
222 for a in field_names:
225 ids2 = self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])])
227 for a in field_names:
228 res[a] += accounts.get(i, {}).get(a, 0.0)
231 def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}):
233 for rec in self.browse(cr, uid, ids, context):
234 result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code)
237 def _get_child_ids(self, cr, uid, ids, field_name, arg, context={}):
239 for record in self.browse(cr, uid, ids, context):
240 if record.child_parent_ids:
241 result[record.id]=[x.id for x in record.child_parent_ids]
245 if record.child_consol_ids:
246 for acc in record.child_consol_ids:
247 result[record.id].append(acc.id)
252 'name': fields.char('Name', size=128, required=True, select=True),
253 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
254 'code': fields.char('Code', size=64, required=True),
255 'type': fields.selection([
256 ('receivable','Receivable'),
257 ('payable','Payable'),
259 ('consolidation','Consolidation'),
262 ], 'Internal Type', required=True,),
264 'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
265 'parent_id': fields.many2one('account.account','Parent', ondelete='cascade'),
266 'child_parent_ids':fields.one2many('account.account','parent_id','Children'),
267 'child_consol_ids':fields.many2many('account.account', 'account_account_consol_rel', 'child_id', 'parent_id', 'Consolidated Children'),
268 'child_id': fields.function(_get_child_ids, method=True, type='many2many',relation="account.account",string="Children Accounts"),
269 'balance': fields.function(__compute, digits=(16,2), method=True, string='Balance', multi='balance'),
270 'credit': fields.function(__compute, digits=(16,2), method=True, string='Credit', multi='balance'),
271 'debit': fields.function(__compute, digits=(16,2), method=True, string='Debit', multi='balance'),
272 'reconcile': fields.boolean('Reconcile', help="Check this account if the user can make a reconciliation of the entries in this account."),
273 'shortcut': fields.char('Shortcut', size=12),
274 'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel',
275 'account_id','tax_id', 'Default Taxes'),
276 'note': fields.text('Note'),
277 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Company Currency'),
278 'company_id': fields.many2one('res.company', 'Company', required=True),
279 'active': fields.boolean('Active', select=2),
281 'parent_left': fields.integer('Parent Left', select=1),
282 'parent_right': fields.integer('Parent Right', select=1),
283 'currency_mode': fields.selection([('current','At Date'),('average','Average Rate')], 'Outgoing Currencies Rate',
285 'This will select how is computed the current currency rate for outgoing transactions. '\
286 'In most countries the legal method is "average" but only a few softwares are able to '\
287 'manage this. So if you import from another software, you may have to use the rate at date. ' \
288 'Incoming transactions, always use the rate at date.', \
290 'check_history': fields.boolean('Display History',
291 help="Check this box if you want to print all entries when printing the General Ledger, "\
292 "otherwise it will only print its balance."),
293 'merge_invoice': fields.boolean('Merge Invoice Entries',help="Check this box if you want that all lines of "\
294 "a customer or supplier invoice using this account are created in one line only"),
297 def _default_company(self, cr, uid, context={}):
298 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
300 return user.company_id.id
301 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
304 'type' : lambda *a :'view',
305 'reconcile': lambda *a: False,
306 'company_id': _default_company,
307 'active': lambda *a: True,
308 'check_history': lambda *a: True,
309 'currency_mode': lambda *a: 'current'
312 def _check_recursion(self, cr, uid, ids):
313 obj_self=self.browse(cr,uid,ids[0])
314 p_id=obj_self.parent_id and obj_self.parent_id.id
315 if (obj_self in obj_self.child_consol_ids) or (p_id and (p_id is obj_self.id)):
318 cr.execute('select distinct child_id from account_account_consol_rel where parent_id in ('+','.join(map(str,ids))+')')
319 child_ids = filter(None, map(lambda x:x[0], cr.fetchall()))
321 if (p_id and (p_id in c_ids)) or (obj_self.id in c_ids):
324 s_ids=self.search(cr,uid,[('parent_id','in',c_ids)])
325 if p_id and (p_id in s_ids):
332 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
334 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
342 if name and str(name).startswith('partner:'):
343 part_id = int(name.split(':')[1])
344 part = self.pool.get('res.partner').browse(cr, user, part_id, context)
345 args += [('id','in', (part.property_account_payable.id, part.property_account_receivable.id))]
347 if name and str(name).startswith('type:'):
348 type = name.split(':')[1]
349 args += [('type','=', type)]
354 ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit)
356 ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit)
358 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
360 ids = self.search(cr, user, args, context=context, limit=limit)
361 return self.name_get(cr, user, ids, context=context)
363 def name_get(self, cr, uid, ids, context={}):
366 reads = self.read(cr, uid, ids, ['name','code'], context)
369 name = record['name']
371 name = record['code']+' '+name
372 res.append((record['id'],name ))
375 def copy(self, cr, uid, id, default={}, context={},done_list=[]):
376 account = self.browse(cr, uid, id, context=context)
380 default=default.copy()
381 default['parent_id'] = False
382 if account.id in done_list:
384 done_list.append(account.id)
386 for child in account.child_id:
387 child_ids=self.copy(cr, uid, child.id, default, context=context,done_list=done_list)
389 new_child_ids.append(child_ids)
390 default['child_parent_ids'] = [(6, 0, new_child_ids)]
392 default['child_parent_ids'] = False
393 return super(account_account, self).copy(cr, uid, id, default, context=context)
395 def write(self, cr, uid, ids, vals, context=None):
398 if 'active' in vals and not vals['active']:
399 line_obj = self.pool.get('account.move.line')
400 account_ids = self.search(cr, uid, [('id', 'child_of', ids)])
401 if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]):
402 raise osv.except_osv(_('Error !'), _('You can not deactivate an account that contains account moves.'))
403 return super(account_account, self).write(cr, uid, ids, vals, context=context)
406 class account_journal_view(osv.osv):
407 _name = "account.journal.view"
408 _description = "Journal View"
410 'name': fields.char('Journal View', size=64, required=True),
411 'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
414 account_journal_view()
417 class account_journal_column(osv.osv):
418 def _col_get(self, cr, user, context={}):
420 cols = self.pool.get('account.move.line')._columns
422 result.append( (col, cols[col].string) )
425 _name = "account.journal.column"
426 _description = "Journal Column"
428 'name': fields.char('Column Name', size=64, required=True),
429 'field': fields.selection(_col_get, 'Field Name', method=True, required=True, size=32),
430 'view_id': fields.many2one('account.journal.view', 'Journal View', select=True),
431 'sequence': fields.integer('Sequence'),
432 'required': fields.boolean('Required'),
433 'readonly': fields.boolean('Readonly'),
436 account_journal_column()
438 class account_journal(osv.osv):
439 _name = "account.journal"
440 _description = "Journal"
442 'name': fields.char('Journal Name', size=64, required=True, translate=True),
443 'code': fields.char('Code', size=16),
444 'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True),
445 'refund_journal': fields.boolean('Refund Journal'),
447 'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]),
448 'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]),
450 'active': fields.boolean('Active'),
451 '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."),
452 'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account'),
453 'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account'),
454 '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."),
455 'update_posted': fields.boolean('Allow Cancelling Entries'),
456 'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="The sequence gives the display order for a list of journals", required=True),
457 'user_id': fields.many2one('res.users', 'User', help="The responsible user of this journal"),
458 'groups_id': fields.many2many('res.groups', 'account_journal_group_rel', 'journal_id', 'group_id', 'Groups'),
459 'currency': fields.many2one('res.currency', 'Currency', help='The currency used to enter statement'),
460 '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 'company_id': fields.related('default_credit_account_id','company_id',type='many2one', relation="res.company", string="Company"),
465 'active': lambda *a: 1,
466 'user_id': lambda self,cr,uid,context: uid,
468 def create(self, cr, uid, vals, context={}):
469 journal_id = super(osv.osv, self).create(cr, uid, vals, context)
470 # journal_name = self.browse(cr, uid, [journal_id])[0].code
471 # periods = self.pool.get('account.period')
472 # ids = periods.search(cr, uid, [('date_stop','>=',time.strftime('%Y-%m-%d'))])
473 # for period in periods.browse(cr, uid, ids):
474 # self.pool.get('account.journal.period').create(cr, uid, {
475 # 'name': (journal_name or '')+':'+(period.code or ''),
476 # 'journal_id': journal_id,
477 # 'period_id': period.id
480 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
487 ids = self.search(cr, user, [('code','ilike',name)]+ args, limit=limit)
489 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
490 return self.name_get(cr, user, ids, context=context)
493 class account_fiscalyear(osv.osv):
494 _name = "account.fiscalyear"
495 _description = "Fiscal Year"
497 'name': fields.char('Fiscal Year', size=64, required=True),
498 'code': fields.char('Code', size=6, required=True),
499 'date_start': fields.date('Start date', required=True),
500 'date_stop': fields.date('End date', required=True),
501 'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'),
502 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', redonly=True),
506 'state': lambda *a: 'draft',
508 _order = "date_start"
510 def _check_duration(self,cr,uid,ids):
511 obj_fy=self.browse(cr,uid,ids[0])
512 if obj_fy.date_stop < obj_fy.date_start:
517 (_check_duration, 'Error ! The date duration of the Fiscal Year is invalid. ', ['date_stop'])
520 def create_period3(self,cr, uid, ids, context={}):
521 return self.create_period(cr, uid, ids, context, 3)
523 def create_period(self,cr, uid, ids, context={}, interval=1):
524 for fy in self.browse(cr, uid, ids, context):
526 ds = mx.DateTime.strptime(fy.date_start, '%Y-%m-%d')
527 while ds.strftime('%Y-%m-%d')<fy.date_stop:
528 de = ds + RelativeDateTime(months=interval, days=-1)
530 if de.strftime('%Y-%m-%d')>fy.date_stop:
531 de=mx.DateTime.strptime(fy.date_stop, '%Y-%m-%d')
533 self.pool.get('account.period').create(cr, uid, {
534 'name': ds.strftime('%m/%Y'),
535 'code': ds.strftime('%m/%Y'),
536 'date_start': ds.strftime('%Y-%m-%d'),
537 'date_stop': de.strftime('%Y-%m-%d'),
538 'fiscalyear_id': fy.id,
540 ds = ds + RelativeDateTime(months=interval)
543 def find(self, cr, uid, dt=None, exception=True, context={}):
545 dt = time.strftime('%Y-%m-%d')
546 ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)])
549 raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.'))
555 class account_period(osv.osv):
556 _name = "account.period"
557 _description = "Account period"
559 'name': fields.char('Period Name', size=64, required=True),
560 'code': fields.char('Code', size=12),
561 'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}),
562 'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}),
563 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True),
564 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', readonly=True)
567 'state': lambda *a: 'draft',
569 _order = "date_start"
571 def _check_duration(self,cr,uid,ids):
572 obj_period=self.browse(cr,uid,ids[0])
573 if obj_period.date_stop < obj_period.date_start:
577 def _check_year_limit(self,cr,uid,ids):
578 obj_period=self.browse(cr,uid,ids[0])
579 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:
584 (_check_duration, 'Error ! The date duration of the Period(s) is invalid. ', ['date_stop']),
585 (_check_year_limit, 'Error ! The date duration of the Period(s) should be within the limit of the Fiscal year. ', ['date_stop'])
588 def next(self, cr, uid, period, step, context={}):
589 ids = self.search(cr, uid, [('date_start','>',period.date_start)])
594 def find(self, cr, uid, dt=None, context={}):
596 dt = time.strftime('%Y-%m-%d')
597 #CHECKME: shouldn't we check the state of the period?
598 ids = self.search(cr, uid, [('date_start','<=',dt),('date_stop','>=',dt)])
600 raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.'))
603 def action_draft(self, cr, uid, ids, *args):
604 users_roles = self.pool.get('res.users').browse(cr, uid, uid).roles_id
605 for role in users_roles:
606 if role.name=='Period':
609 cr.execute('update account_journal_period set state=%s where period_id=%d', (mode, id))
610 cr.execute('update account_period set state=%s where id=%d', (mode, id))
615 class account_journal_period(osv.osv):
616 _name = "account.journal.period"
617 _description = "Journal - Period"
619 def _icon_get(self, cr, uid, ids, field_name, arg=None, context={}):
620 result = {}.fromkeys(ids, 'STOCK_NEW')
621 for r in self.read(cr, uid, ids, ['state']):
623 'draft': 'STOCK_NEW',
624 'printed': 'STOCK_PRINT_PREVIEW',
625 'done': 'STOCK_DIALOG_AUTHENTICATION',
626 }.get(r['state'], 'STOCK_NEW')
630 'name': fields.char('Journal-Period Name', size=64, required=True),
631 'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"),
632 'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"),
633 'icon': fields.function(_icon_get, method=True, string='Icon', type='string'),
634 'active': fields.boolean('Active', required=True),
635 'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'Status', required=True, readonly=True)
638 def _check(self, cr, uid, ids, context={}):
639 for obj in self.browse(cr, uid, ids, context):
640 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))
643 raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !'))
646 def write(self, cr, uid, ids, vals, context={}):
647 self._check(cr, uid, ids, context)
648 return super(account_journal_period, self).write(cr, uid, ids, vals, context)
650 def create(self, cr, uid, vals, context={}):
651 period_id=vals.get('period_id',False)
653 period = self.pool.get('account.period').browse(cr, uid,period_id)
654 vals['state']=period.state
655 return super(account_journal_period, self).create(cr, uid, vals, context)
657 def unlink(self, cr, uid, ids, context={}):
658 self._check(cr, uid, ids, context)
659 return super(account_journal_period, self).unlink(cr, uid, ids, context)
662 'state': lambda *a: 'draft',
663 'active': lambda *a: True,
667 account_journal_period()
669 class account_fiscalyear(osv.osv):
670 _inherit = "account.fiscalyear"
671 _description = "Fiscal Year"
673 'start_journal_period_id':fields.many2one('account.journal.period','New Entries Journal'),
674 'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True),
678 #----------------------------------------------------------
680 #----------------------------------------------------------
681 class account_move(osv.osv):
682 _name = "account.move"
683 _description = "Account Entry"
685 def name_get(self, cursor, user, ids, context=None):
689 data_move = self.pool.get('account.move').browse(cursor,user,ids)
690 for move in data_move:
691 if move.state=='draft':
692 name = '*' + str(move.id)
695 res.append((move.id, name))
699 def _get_period(self, cr, uid, context):
700 periods = self.pool.get('account.period').find(cr, uid)
706 def _amount_compute(self, cr, uid, ids, name, args, context, where =''):
707 if not ids: return {}
708 cr.execute('select move_id,sum(debit) from account_move_line where move_id in ('+','.join(map(str,ids))+') group by move_id')
709 result = dict(cr.fetchall())
711 result.setdefault(id, 0.0)
715 'name': fields.char('Entry Number', size=64, required=True),
716 'ref': fields.char('Ref', size=64),
717 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}),
718 'journal_id': fields.many2one('account.journal', 'Journal', required=True, states={'posted':[('readonly',True)]}),
719 'state': fields.selection([('draft','Draft'), ('posted','Posted')], 'Status', required=True, readonly=True),
720 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}),
721 'to_check': fields.boolean('To Be Verified'),
722 'partner_id': fields.related('line_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store=True),
723 'amount': fields.function(_amount_compute, method=True, string='Amount', digits=(16,2), store=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',
742 def _check_centralisation(self, cursor, user, ids):
743 for move in self.browse(cursor, user, ids):
744 if move.journal_id.centralisation:
745 move_ids = self.search(cursor, user, [
746 ('period_id', '=', move.period_id.id),
747 ('journal_id', '=', move.journal_id.id),
749 if len(move_ids) > 1:
753 def _check_period_journal(self, cursor, user, ids):
754 for move in self.browse(cursor, user, ids):
755 for line in move.line_id:
756 if line.period_id.id != move.period_id.id:
758 if line.journal_id.id != move.journal_id.id:
763 (_check_centralisation,
764 'You can not create more than one move per period on centralized journal',
766 (_check_period_journal,
767 'You can not create entries on different period/journal in the same move',
770 def post(self, cr, uid, ids, context=None):
771 if self.validate(cr, uid, ids, context) and len(ids):
772 for move in self.browse(cr, uid, ids):
775 journal = move.journal_id
776 if journal.sequence_id:
777 new_name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id)
779 raise osv.except_osv(_('Error'), _('No sequence defined in the journal !'))
781 self.write(cr, uid, [move.id], {'name':new_name})
783 cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('posted',))
785 raise osv.except_osv(_('Integrity Error !'), _('You can not validate a non balanced entry !'))
788 def button_validate(self, cursor, user, ids, context=None):
789 return self.post(cursor, user, ids, context=context)
791 def button_cancel(self, cr, uid, ids, context={}):
792 for line in self.browse(cr, uid, ids, context):
793 if not line.journal_id.update_posted:
794 raise osv.except_osv(_('Error !'), _('You can not modify a posted entry of this journal !'))
796 cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('draft',))
799 def write(self, cr, uid, ids, vals, context={}):
801 c['novalidate'] = True
802 result = super(osv.osv, self).write(cr, uid, ids, vals, c)
803 self.validate(cr, uid, ids, context)
807 # TODO: Check if period is closed !
809 def create(self, cr, uid, vals, context={}):
810 if 'line_id' in vals:
811 if 'journal_id' in vals:
812 for l in vals['line_id']:
814 l[2]['journal_id'] = vals['journal_id']
815 context['journal_id'] = vals['journal_id']
816 if 'period_id' in vals:
817 for l in vals['line_id']:
819 l[2]['period_id'] = vals['period_id']
820 context['period_id'] = vals['period_id']
822 default_period = self._get_period(cr, uid, context)
823 for l in vals['line_id']:
825 l[2]['period_id'] = default_period
826 context['period_id'] = default_period
828 accnt_journal = self.pool.get('account.journal').browse(cr, uid, vals['journal_id'])
829 if 'line_id' in vals:
831 c['novalidate'] = True
832 result = super(account_move, self).create(cr, uid, vals, c)
833 self.validate(cr, uid, [result], context)
835 result = super(account_move, self).create(cr, uid, vals, context)
838 def copy(self, cr, uid, id, default=None, context=None):
841 default = default.copy()
842 default.update({'state':'draft', 'name':'/',})
843 return super(account_move, self).copy(cr, uid, id, default, context)
845 def unlink(self, cr, uid, ids, context={}, check=True):
847 for move in self.browse(cr, uid, ids, context):
848 if move['state'] <> 'draft':
849 raise osv.except_osv(_('UserError'),
850 _('You can not delete posted movement: "%s"!') % \
852 line_ids = map(lambda x: x.id, move.line_id)
853 context['journal_id'] = move.journal_id.id
854 context['period_id'] = move.period_id.id
855 self.pool.get('account.move.line')._update_check(cr, uid, line_ids, context)
856 toremove.append(move.id)
857 result = super(account_move, self).unlink(cr, uid, toremove, context)
860 def _compute_balance(self, cr, uid, id, context={}):
861 move = self.browse(cr, uid, [id])[0]
863 for line in move.line_id:
864 amount+= (line.debit - line.credit)
867 def _centralise(self, cr, uid, move, mode):
869 account_id = move.journal_id.default_debit_account_id.id
872 raise osv.except_osv(_('UserError'),
873 _('There is no default default debit account defined \n' \
874 'on journal "%s"') % move.journal_id.name)
876 account_id = move.journal_id.default_credit_account_id.id
879 raise osv.except_osv(_('UserError'),
880 _('There is no default default credit account defined \n' \
881 'on journal "%s"') % move.journal_id.name)
883 # find the first line of this move with the current mode
884 # or create it if it doesn't exist
885 cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode))
890 line_id = self.pool.get('account.move.line').create(cr, uid, {
891 'name': 'Centralisation '+mode,
892 'centralisation': mode,
893 'account_id': account_id,
895 'journal_id': move.journal_id.id,
896 'period_id': move.period_id.id,
897 'date': move.period_id.date_stop,
900 }, {'journal_id': move.journal_id.id, 'period_id': move.period_id.id})
902 # find the first line of this move with the other mode
903 # so that we can exclude it from our calculation
904 cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode2))
911 cr.execute('select sum('+mode+') from account_move_line where move_id=%d and id<>%d', (move.id, line_id2))
912 result = cr.fetchone()[0] or 0.0
913 cr.execute('update account_move_line set '+mode2+'=%f where id=%d', (result, line_id))
917 # Validate a balanced move. If it is a centralised journal, create a move.
919 def validate(self, cr, uid, ids, context={}):
921 for move in self.browse(cr, uid, ids, context):
922 journal = move.journal_id
927 for line in move.line_id:
928 amount += line.debit - line.credit
929 line_ids.append(line.id)
930 if line.state=='draft':
931 line_draft_ids.append(line.id)
934 company_id = line.account_id.company_id.id
935 if not company_id == line.account_id.company_id.id:
936 raise osv.except_osv(_('Error'), _("Couldn't create move between different companies"))
938 if line.account_id.currency_id:
939 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):
940 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)))
942 if abs(amount) < 0.0001:
943 if not len(line_draft_ids):
945 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
946 'journal_id': move.journal_id.id,
947 'period_id': move.period_id.id,
949 }, context, check=False)
953 if journal.type not in ('purchase','sale'):
956 for line in move.line_id:
957 if move.journal_id.type == 'sale':
960 key = 'account_paid_id'
963 key = 'account_collected_id'
967 key = 'account_collected_id'
970 key = 'account_paid_id'
971 if line.account_id.tax_ids:
972 code = amount = False
973 for tax in line.account_id.tax_ids:
975 acc = getattr(tax, key).id
976 account[acc] = (getattr(tax,
977 field_base + 'tax_code_id').id,
978 getattr(tax, field_base + 'tax_sign'))
979 account2[(acc,getattr(tax,
980 field_base + 'tax_code_id').id)] = (getattr(tax,
981 field_base + 'tax_code_id').id,
982 getattr(tax, field_base + 'tax_sign'))
983 code = getattr(tax, field_base + 'base_code_id').id
984 amount = getattr(tax, field_base+'base_sign') * \
985 (line.debit + line.credit)
987 if code and not (line.tax_code_id or line.tax_amount):
988 self.pool.get('account.move.line').write(cr, uid,
992 }, context=context, check=False)
997 key = (line.account_id.id, line.tax_code_id.id)
999 code = account2[key][0]
1000 amount = account2[key][1] * (line.debit + line.credit)
1001 elif line.account_id.id in account:
1002 code = account[line.account_id.id][0]
1003 amount = account[line.account_id.id][1] * (line.debit + line.credit)
1004 if (code or amount) and not (line.tax_code_id or line.tax_amount):
1005 self.pool.get('account.move.line').write(cr, uid, [line.id], {
1006 'tax_code_id': code,
1007 'tax_amount': amount
1008 }, context, check=False)
1013 if journal.centralisation:
1014 self._centralise(cr, uid, move, 'debit')
1015 self._centralise(cr, uid, move, 'credit')
1016 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
1018 }, context, check=False)
1021 self.pool.get('account.move.line').write(cr, uid, line_ids, {
1022 'journal_id': move.journal_id.id,
1023 'period_id': move.period_id.id,
1024 #'tax_code_id': False,
1025 #'tax_amount': False,
1027 }, context, check=False)
1032 class account_move_reconcile(osv.osv):
1033 _name = "account.move.reconcile"
1034 _description = "Account Reconciliation"
1036 'name': fields.char('Name', size=64, required=True),
1037 'type': fields.char('Type', size=16, required=True),
1038 'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry lines'),
1039 'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'),
1040 'create_date': fields.date('Creation date', readonly=True),
1043 'name': lambda self,cr,uid,ctx={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile') or '/',
1045 def reconcile_partial_check(self, cr, uid, ids, type='auto', context={}):
1046 for rec in self.browse(cr, uid, ids, context):
1048 for line in rec.line_partial_ids:
1049 total += (line.debit or 0.0) - (line.credit or 0.0)
1051 self.pool.get('account.move.line').write(cr, uid,
1052 map(lambda x: x.id, rec.line_partial_ids),
1053 {'reconcile_id': rec.id }
1057 def name_get(self, cr, uid, ids, context=None):
1061 for r in self.browse(cr, uid, ids, context):
1062 total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0)
1064 name = '%s (%.2f)' % (r.name, total)
1065 result.append((r.id,name))
1067 result.append((r.id,r.name))
1071 account_move_reconcile()
1073 #----------------------------------------------------------
1075 #----------------------------------------------------------
1078 child_depend: la taxe depend des taxes filles
1080 class account_tax_code(osv.osv):
1082 A code for the tax object.
1084 This code is used for some tax declarations.
1086 def _sum(self, cr, uid, ids, name, args, context, where =''):
1087 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
1088 acc_set = ",".join(map(str, ids2))
1089 if context.get('based_on', 'invoices') == 'payments':
1090 cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1091 FROM account_move_line AS line, \
1092 account_move AS move \
1093 LEFT JOIN account_invoice invoice ON \
1094 (invoice.move_id = move.id) \
1095 WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1096 AND move.id = line.move_id \
1097 AND ((invoice.state = \'paid\') \
1098 OR (invoice.id IS NULL)) \
1099 GROUP BY line.tax_code_id')
1101 cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1102 FROM account_move_line AS line \
1103 WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1104 GROUP BY line.tax_code_id')
1105 res=dict(cr.fetchall())
1106 for record in self.browse(cr, uid, ids, context):
1107 def _rec_get(record):
1108 amount = res.get(record.id, 0.0)
1109 for rec in record.child_ids:
1110 amount += _rec_get(rec) * rec.sign
1112 res[record.id] = round(_rec_get(record), 2)
1115 def _sum_period(self, cr, uid, ids, name, args, context):
1116 if 'period_id' in context and context['period_id']:
1117 period_id = context['period_id']
1119 period_id = self.pool.get('account.period').find(cr, uid)
1120 if not len(period_id):
1121 return dict.fromkeys(ids, 0.0)
1122 period_id = period_id[0]
1123 return self._sum(cr, uid, ids, name, args, context,
1124 where=' and line.period_id='+str(period_id))
1126 _name = 'account.tax.code'
1127 _description = 'Tax Code'
1130 'name': fields.char('Tax Case Name', size=64, required=True),
1131 'code': fields.char('Case Code', size=64),
1132 'info': fields.text('Description'),
1133 'sum': fields.function(_sum, method=True, string="Year Sum"),
1134 'sum_period': fields.function(_sum_period, method=True, string="Period Sum"),
1135 'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True),
1136 'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'),
1137 'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'),
1138 'company_id': fields.many2one('res.company', 'Company', required=True),
1139 'sign': fields.float('Sign for parent', required=True),
1142 def name_get(self, cr, uid, ids, context=None):
1145 if isinstance(ids, (int, long)):
1147 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1148 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1151 def _default_company(self, cr, uid, context={}):
1152 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1154 return user.company_id.id
1155 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1157 'company_id': _default_company,
1158 'sign': lambda *args: 1.0,
1160 def _check_recursion(self, cr, uid, ids):
1163 cr.execute('select distinct parent_id from account_tax_code where id in ('+','.join(map(str,ids))+')')
1164 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1171 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
1173 _order = 'code,name'
1176 class account_tax(osv.osv):
1180 Type: percent, fixed, none, code
1181 PERCENT: tax = price * amount
1182 FIXED: tax = price + amount
1184 CODE: execute python code. localcontext = {'price_unit':pu, 'address':address_object}
1185 return result in the context
1186 Ex: result=round(price_unit*0.21,4)
1188 _name = 'account.tax'
1189 _description = 'Tax'
1191 'name': fields.char('Tax Name', size=64, required=True, help="This name will be used to be displayed on reports"),
1192 '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."),
1193 'amount': fields.float('Amount', required=True, digits=(14,4)),
1194 'active': fields.boolean('Active'),
1195 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1196 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1197 '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."),
1198 'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'),
1199 'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'),
1200 'parent_id':fields.many2one('account.tax', 'Parent Tax Account', select=True),
1201 'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'),
1202 '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."),
1203 'python_compute':fields.text('Python Code'),
1204 'python_compute_inv':fields.text('Python Code (reverse)'),
1205 'python_applicable':fields.text('Python Code'),
1206 '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."),
1209 # Fields used for the VAT declaration
1211 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."),
1212 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."),
1213 'base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."),
1214 'tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."),
1216 # Same fields for refund invoices
1218 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the VAT declaration."),
1219 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1220 'ref_base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."),
1221 'ref_tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."),
1222 '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"),
1223 'company_id': fields.many2one('res.company', 'Company', required=True),
1224 'description': fields.char('Internal Name',size=32),
1227 def name_get(self, cr, uid, ids, context={}):
1231 for record in self.read(cr, uid, ids, ['description','name'], context):
1232 name = record['description'] and record['description'] or record['name']
1233 res.append((record['id'],name ))
1236 def _default_company(self, cr, uid, context={}):
1237 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1239 return user.company_id.id
1240 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1242 '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''',
1243 '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''',
1244 'applicable_type': lambda *a: 'true',
1245 'type': lambda *a: 'percent',
1246 'amount': lambda *a: 0,
1247 'active': lambda *a: 1,
1248 'sequence': lambda *a: 1,
1249 'tax_group': lambda *a: 'vat',
1250 'ref_tax_sign': lambda *a: 1,
1251 'ref_base_sign': lambda *a: 1,
1252 'tax_sign': lambda *a: 1,
1253 'base_sign': lambda *a: 1,
1254 'include_base_amount': lambda *a: False,
1255 'company_id': _default_company,
1259 def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1262 if tax.applicable_type=='code':
1263 localdict = {'price_unit':price_unit, 'address':self.pool.get('res.partner.address').browse(cr, uid, address_id), 'product':product, 'partner':partner}
1264 exec tax.python_applicable in localdict
1265 if localdict.get('result', False):
1271 def _unit_compute(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1272 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1275 cur_price_unit=price_unit
1277 # we compute the amount for the current tax object and append it to the result
1279 if tax.type=='percent':
1280 amount = cur_price_unit * tax.amount
1281 res.append({'id':tax.id,
1284 'account_collected_id':tax.account_collected_id.id,
1285 'account_paid_id':tax.account_paid_id.id,
1286 'base_code_id': tax.base_code_id.id,
1287 'ref_base_code_id': tax.ref_base_code_id.id,
1288 'sequence': tax.sequence,
1289 'base_sign': tax.base_sign,
1290 'tax_sign': tax.tax_sign,
1291 'ref_base_sign': tax.ref_base_sign,
1292 'ref_tax_sign': tax.ref_tax_sign,
1293 'price_unit': cur_price_unit,
1294 'tax_code_id': tax.tax_code_id.id,
1295 'ref_tax_code_id': tax.ref_tax_code_id.id,
1298 elif tax.type=='fixed':
1299 res.append({'id':tax.id,
1301 'amount':tax.amount,
1302 'account_collected_id':tax.account_collected_id.id,
1303 'account_paid_id':tax.account_paid_id.id,
1304 'base_code_id': tax.base_code_id.id,
1305 'ref_base_code_id': tax.ref_base_code_id.id,
1306 'sequence': tax.sequence,
1307 'base_sign': tax.base_sign,
1308 'tax_sign': tax.tax_sign,
1309 'ref_base_sign': tax.ref_base_sign,
1310 'ref_tax_sign': tax.ref_tax_sign,
1312 'tax_code_id': tax.tax_code_id.id,
1313 'ref_tax_code_id': tax.ref_tax_code_id.id,})
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']
1323 'account_collected_id': tax.account_collected_id.id,
1324 'account_paid_id': tax.account_paid_id.id,
1325 'base_code_id': tax.base_code_id.id,
1326 'ref_base_code_id': tax.ref_base_code_id.id,
1327 'sequence': tax.sequence,
1328 'base_sign': tax.base_sign,
1329 'tax_sign': tax.tax_sign,
1330 'ref_base_sign': tax.ref_base_sign,
1331 'ref_tax_sign': tax.ref_tax_sign,
1332 'price_unit': cur_price_unit,
1333 'tax_code_id': tax.tax_code_id.id,
1334 'ref_tax_code_id': tax.ref_tax_code_id.id,
1336 amount2 = res[-1]['amount']
1337 if len(tax.child_ids):
1338 if tax.child_depend:
1341 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner)
1342 res.extend(child_tax)
1343 if tax.include_base_amount:
1344 cur_price_unit+=amount2
1347 def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1350 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1354 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1355 one tax for each tax id in IDS and their childs
1357 res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner)
1359 r['amount'] *= quantity
1362 def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1363 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1367 cur_price_unit=price_unit
1369 # we compute the amount for the current tax object and append it to the result
1371 if tax.type=='percent':
1372 amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
1373 res.append({'id':tax.id,
1376 'account_collected_id':tax.account_collected_id.id,
1377 'account_paid_id':tax.account_paid_id.id,
1378 'base_code_id': tax.base_code_id.id,
1379 'ref_base_code_id': tax.ref_base_code_id.id,
1380 'sequence': tax.sequence,
1381 'base_sign': tax.base_sign,
1382 'tax_sign': tax.tax_sign,
1383 'ref_base_sign': tax.ref_base_sign,
1384 'ref_tax_sign': tax.ref_tax_sign,
1385 'price_unit': cur_price_unit - amount,
1386 'tax_code_id': tax.tax_code_id.id,
1387 'ref_tax_code_id': tax.ref_tax_code_id.id,})
1389 elif tax.type=='fixed':
1390 res.append({'id':tax.id,
1392 'amount':tax.amount,
1393 'account_collected_id':tax.account_collected_id.id,
1394 'account_paid_id':tax.account_paid_id.id,
1395 'base_code_id': tax.base_code_id.id,
1396 'ref_base_code_id': tax.ref_base_code_id.id,
1397 'sequence': tax.sequence,
1398 'base_sign': tax.base_sign,
1399 'tax_sign': tax.tax_sign,
1400 'ref_base_sign': tax.ref_base_sign,
1401 'ref_tax_sign': tax.ref_tax_sign,
1403 'tax_code_id': tax.tax_code_id.id,
1404 'ref_tax_code_id': tax.ref_tax_code_id.id,})
1406 elif tax.type=='code':
1407 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1408 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1409 exec tax.python_compute_inv in localdict
1410 amount = localdict['result']
1415 'account_collected_id': tax.account_collected_id.id,
1416 'account_paid_id': tax.account_paid_id.id,
1417 'base_code_id': tax.base_code_id.id,
1418 'ref_base_code_id': tax.ref_base_code_id.id,
1419 'sequence': tax.sequence,
1420 'base_sign': tax.base_sign,
1421 'tax_sign': tax.tax_sign,
1422 'ref_base_sign': tax.ref_base_sign,
1423 'ref_tax_sign': tax.ref_tax_sign,
1424 'price_unit': cur_price_unit - amount,
1425 'tax_code_id': tax.tax_code_id.id,
1426 'ref_tax_code_id': tax.ref_tax_code_id.id,
1429 amount2 = res[-1]['amount']
1430 if len(tax.child_ids):
1431 if tax.child_depend:
1436 for t in tax.child_ids:
1437 parent_tax = self._unit_compute_inv(cr, uid, [t], amount, address_id, product, partner)
1438 res.extend(parent_tax)
1439 if tax.include_base_amount:
1440 cur_price_unit-=amount
1444 def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1446 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1447 Price Unit is a VAT included price
1451 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1452 one tax for each tax id in IDS and their childs
1454 res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
1456 r['amount'] *= quantity
1460 # ---------------------------------------------------------
1461 # Account Entries Models
1462 # ---------------------------------------------------------
1464 class account_model(osv.osv):
1465 _name = "account.model"
1466 _description = "Account Model"
1468 'name': fields.char('Model Name', size=64, required=True, help="This is a model for recurring accounting entries"),
1469 'ref': fields.char('Ref', size=64),
1470 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
1471 'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'),
1472 'legend' :fields.text('Legend',readonly=True,size=100),
1476 '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''',
1481 class account_model_line(osv.osv):
1482 _name = "account.model.line"
1483 _description = "Account Model Entries"
1485 'name': fields.char('Name', size=64, required=True),
1486 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the resources from the lowest sequences to the higher ones"),
1487 'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity on entries"),
1488 'debit': fields.float('Debit', digits=(16,2)),
1489 'credit': fields.float('Credit', digits=(16,2)),
1491 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"),
1493 'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True),
1495 'ref': fields.char('Ref.', size=16),
1497 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."),
1498 'currency_id': fields.many2one('res.currency', 'Currency'),
1500 'partner_id': fields.many2one('res.partner', 'Partner Ref.'),
1501 '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."),
1502 'date': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Current Date', required=True, help="The date of the generated entries"),
1505 'date': lambda *a: 'today'
1508 _sql_constraints = [
1509 ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in model !'),
1510 ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model !'),
1512 account_model_line()
1514 # ---------------------------------------------------------
1515 # Account Subscription
1516 # ---------------------------------------------------------
1519 class account_subscription(osv.osv):
1520 _name = "account.subscription"
1521 _description = "Account Subscription"
1523 'name': fields.char('Name', size=64, required=True),
1524 'ref': fields.char('Ref.', size=16),
1525 'model_id': fields.many2one('account.model', 'Model', required=True),
1527 'date_start': fields.date('Starting date', required=True),
1528 'period_total': fields.integer('Number of period', required=True),
1529 'period_nbr': fields.integer('Period', required=True),
1530 'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True),
1531 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status', required=True, readonly=True),
1533 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines')
1536 'date_start': lambda *a: time.strftime('%Y-%m-%d'),
1537 'period_type': lambda *a: 'month',
1538 'period_total': lambda *a: 12,
1539 'period_nbr': lambda *a: 1,
1540 'state': lambda *a: 'draft',
1542 def state_draft(self, cr, uid, ids, context={}):
1543 self.write(cr, uid, ids, {'state':'draft'})
1546 def check(self, cr, uid, ids, context={}):
1548 for sub in self.browse(cr, uid, ids, context):
1550 for line in sub.lines_id:
1551 if not line.move_id.id:
1555 todone.append(sub.id)
1557 self.write(cr, uid, todone, {'state':'done'})
1560 def remove_line(self, cr, uid, ids, context={}):
1562 for sub in self.browse(cr, uid, ids, context):
1563 for line in sub.lines_id:
1564 if not line.move_id.id:
1565 toremove.append(line.id)
1567 self.pool.get('account.subscription.line').unlink(cr, uid, toremove)
1568 self.write(cr, uid, ids, {'state':'draft'})
1571 def compute(self, cr, uid, ids, context={}):
1572 for sub in self.browse(cr, uid, ids, context):
1574 for i in range(sub.period_total):
1575 self.pool.get('account.subscription.line').create(cr, uid, {
1577 'subscription_id': sub.id,
1579 if sub.period_type=='day':
1580 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(days=sub.period_nbr)).strftime('%Y-%m-%d')
1581 if sub.period_type=='month':
1582 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(months=sub.period_nbr)).strftime('%Y-%m-%d')
1583 if sub.period_type=='year':
1584 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(years=sub.period_nbr)).strftime('%Y-%m-%d')
1585 self.write(cr, uid, ids, {'state':'running'})
1587 account_subscription()
1589 class account_subscription_line(osv.osv):
1590 _name = "account.subscription.line"
1591 _description = "Account Subscription Line"
1593 'subscription_id': fields.many2one('account.subscription', 'Subscription', required=True, select=True),
1594 'date': fields.date('Date', required=True),
1595 'move_id': fields.many2one('account.move', 'Entry'),
1599 def move_create(self, cr, uid, ids, context={}):
1601 for line in self.browse(cr, uid, ids, context):
1605 ids = self.pool.get('account.model').generate(cr, uid, [line.subscription_id.model_id.id], datas, context)
1606 tocheck[line.subscription_id.id] = True
1607 self.write(cr, uid, [line.id], {'move_id':ids[0]})
1609 self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context)
1612 account_subscription_line()
1615 class account_config_wizard(osv.osv_memory):
1616 _name = 'account.config.wizard'
1618 def _get_charts(self, cr, uid, context):
1619 module_obj=self.pool.get('ir.module.module')
1620 ids=module_obj.search(cr, uid, [('category_id', '=', 'Account Charts'), ('state', '<>', 'installed')])
1621 res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)]
1622 res.append((-1, 'None'))
1623 res.sort(lambda x,y: cmp(x[1],y[1]))
1627 'name':fields.char('Name', required=True, size=64, help="Name of the fiscal year as displayed on screens."),
1628 'code':fields.char('Code', required=True, size=64, help="Name of the fiscal year as displayed in reports."),
1629 'date1': fields.date('Starting Date', required=True),
1630 'date2': fields.date('Ending Date', required=True),
1631 'period':fields.selection([('month','Month'),('3months','3 Months')], 'Periods', required=True),
1632 'charts' : fields.selection(_get_charts, 'Charts of Account',required=True)
1635 'code': lambda *a: time.strftime('%Y'),
1636 'name': lambda *a: time.strftime('%Y'),
1637 'date1': lambda *a: time.strftime('%Y-01-01'),
1638 'date2': lambda *a: time.strftime('%Y-12-31'),
1639 'period':lambda *a:'month',
1641 def action_cancel(self,cr,uid,ids,conect=None):
1643 'view_type': 'form',
1644 "view_mode": 'form',
1645 'res_model': 'ir.actions.configuration.wizard',
1646 'type': 'ir.actions.act_window',
1650 def install_account_chart(self, cr, uid,ids, context=None):
1651 for res in self.read(cr,uid,ids):
1654 mod_obj = self.pool.get('ir.module.module')
1655 mod_obj.write(cr , uid, [id] ,{'state' : 'to install'})
1656 mod_obj.download(cr, uid, [id], context=context)
1658 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,))
1664 mod_obj.write(cr , uid, [id] ,{'state' : 'to install'})
1665 mod_obj.download(cr, uid, [id], context=context)
1670 db, pool = pooler.restart_pool(cr.dbname, update_module=True)
1672 def action_create(self, cr, uid,ids, context=None):
1673 for res in self.read(cr,uid,ids):
1674 if 'date1' in res and 'date2' in res:
1675 res_obj = self.pool.get('account.fiscalyear')
1676 start_date=res['date1']
1677 end_date=res['date2']
1678 name=res['name']#DateTime.strptime(start_date, '%Y-%m-%d').strftime('%m.%Y') + '-' + DateTime.strptime(end_date, '%Y-%m-%d').strftime('%m.%Y')
1682 'date_start':start_date,
1683 'date_stop':end_date,
1685 new_id=res_obj.create(cr, uid, vals, context=context)
1686 if res['period']=='month':
1687 res_obj.create_period(cr,uid,[new_id])
1688 elif res['period']=='3months':
1689 res_obj.create_period3(cr,uid,[new_id])
1690 self.install_account_chart(cr,uid,ids)
1692 'view_type': 'form',
1693 "view_mode": 'form',
1694 'res_model': 'ir.actions.configuration.wizard',
1695 'type': 'ir.actions.act_window',
1701 account_config_wizard()
1704 # ---------------------------------------------------------------
1705 # Account Templates : Account, Tax, Tax Code and chart. + Wizard
1706 # ---------------------------------------------------------------
1708 class account_tax_template(osv.osv):
1709 _name = 'account.tax.template'
1710 account_tax_template()
1712 class account_account_template(osv.osv):
1714 _name = "account.account.template"
1715 _description ='Templates for Accounts'
1718 'name': fields.char('Name', size=128, required=True, select=True),
1719 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
1720 'code': fields.char('Code', size=64),
1721 'type': fields.selection([
1722 ('receivable','Receivable'),
1723 ('payable','Payable'),
1725 ('consolidation','Consolidation'),
1727 ('closed','Closed'),
1728 ], 'Internal Type', required=True,),
1729 'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
1730 'reconcile': fields.boolean('Allow Reconciliation', help="Check this option if the user can make a reconciliation of the entries in this account."),
1731 'shortcut': fields.char('Shortcut', size=12),
1732 'note': fields.text('Note'),
1733 'parent_id': fields.many2one('account.account.template','Parent Account Template', ondelete='cascade'),
1734 'child_parent_ids':fields.one2many('account.account.template','parent_id','Children'),
1735 'tax_ids': fields.many2many('account.tax.template', 'account_account_template_tax_rel','account_id','tax_id', 'Default Taxes'),
1739 'reconcile': lambda *a: False,
1740 'type' : lambda *a :'view',
1743 def _check_recursion(self, cr, uid, ids):
1746 cr.execute('select parent_id from account_account_template where id in ('+','.join(map(str,ids))+')')
1747 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1754 (_check_recursion, 'Error ! You can not create recursive account templates.', ['parent_id'])
1758 def name_get(self, cr, uid, ids, context={}):
1761 reads = self.read(cr, uid, ids, ['name','code'], context)
1763 for record in reads:
1764 name = record['name']
1766 name = record['code']+' '+name
1767 res.append((record['id'],name ))
1770 account_account_template()
1772 class account_tax_code_template(osv.osv):
1774 _name = 'account.tax.code.template'
1775 _description = 'Tax Code Template'
1779 'name': fields.char('Tax Case Name', size=64, required=True),
1780 'code': fields.char('Case Code', size=64),
1781 'info': fields.text('Description'),
1782 'parent_id': fields.many2one('account.tax.code.template', 'Parent Code', select=True),
1783 'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Childs Codes'),
1784 'sign': fields.float('Sign for parent', required=True),
1788 'sign': lambda *args: 1.0,
1791 def name_get(self, cr, uid, ids, context=None):
1794 if isinstance(ids, (int, long)):
1796 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1797 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1800 def _check_recursion(self, cr, uid, ids):
1803 cr.execute('select distinct parent_id from account_tax_code_template where id in ('+','.join(map(str,ids))+')')
1804 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1811 (_check_recursion, 'Error ! You can not create recursive Tax Codes.', ['parent_id'])
1813 _order = 'code,name'
1814 account_tax_code_template()
1817 class account_chart_template(osv.osv):
1818 _name="account.chart.template"
1819 _description= "Templates for Account Chart"
1822 'name': fields.char('Name', size=64, required=True),
1823 'account_root_id': fields.many2one('account.account.template','Root Account',required=True,domain=[('parent_id','=',False)]),
1824 'tax_code_root_id': fields.many2one('account.tax.code.template','Root Tax Code',required=True,domain=[('parent_id','=',False)]),
1825 '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'),
1826 'bank_account_view_id': fields.many2one('account.account.template','Bank Account',required=True),
1827 'property_account_receivable': fields.many2one('account.account.template','Receivable Account'),
1828 'property_account_payable': fields.many2one('account.account.template','Payable Account'),
1829 'property_account_expense_categ': fields.many2one('account.account.template','Expense Category Account'),
1830 'property_account_income_categ': fields.many2one('account.account.template','Income Category Account'),
1831 'property_account_expense': fields.many2one('account.account.template','Expense Account on Product Template'),
1832 'property_account_income': fields.many2one('account.account.template','Income Account on Product Template'),
1835 account_chart_template()
1837 class account_tax_template(osv.osv):
1839 _name = 'account.tax.template'
1840 _description = 'Templates for Taxes'
1843 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
1844 'name': fields.char('Tax Name', size=64, required=True),
1845 '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."),
1846 'amount': fields.float('Amount', required=True, digits=(14,4)),
1847 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1848 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1849 '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."),
1850 'account_collected_id':fields.many2one('account.account.template', 'Invoice Tax Account'),
1851 'account_paid_id':fields.many2one('account.account.template', 'Refund Tax Account'),
1852 'parent_id':fields.many2one('account.tax.template', 'Parent Tax Account', select=True),
1853 '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."),
1854 'python_compute':fields.text('Python Code'),
1855 'python_compute_inv':fields.text('Python Code (reverse)'),
1856 'python_applicable':fields.text('Python Code'),
1857 '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."),
1860 # Fields used for the VAT declaration
1862 'base_code_id': fields.many2one('account.tax.code.template', 'Base Code', help="Use this code for the VAT declaration."),
1863 'tax_code_id': fields.many2one('account.tax.code.template', 'Tax Code', help="Use this code for the VAT declaration."),
1864 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1865 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1867 # Same fields for refund invoices
1869 'ref_base_code_id': fields.many2one('account.tax.code.template', 'Refund Base Code', help="Use this code for the VAT declaration."),
1870 'ref_tax_code_id': fields.many2one('account.tax.code.template', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1871 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1872 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1873 '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."),
1874 'description': fields.char('Internal Name', size=32),
1877 def name_get(self, cr, uid, ids, context={}):
1881 for record in self.read(cr, uid, ids, ['description','name'], context):
1882 name = record['description'] and record['description'] or record['name']
1883 res.append((record['id'],name ))
1886 def _default_company(self, cr, uid, context={}):
1887 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1889 return user.company_id.id
1890 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1893 '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''',
1894 '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''',
1895 'applicable_type': lambda *a: 'true',
1896 'type': lambda *a: 'percent',
1897 'amount': lambda *a: 0,
1898 'sequence': lambda *a: 1,
1899 'tax_group': lambda *a: 'vat',
1900 'ref_tax_sign': lambda *a: 1,
1901 'ref_base_sign': lambda *a: 1,
1902 'tax_sign': lambda *a: 1,
1903 'base_sign': lambda *a: 1,
1904 'include_base_amount': lambda *a: False,
1909 account_tax_template()
1911 # Multi charts of Accounts wizard
1913 class wizard_multi_charts_accounts(osv.osv_memory):
1915 Create a new account chart for a company.
1918 * an account chart template
1919 * a number of digits for formatting code of non-view accounts
1920 * a list of bank account owned by the company
1922 * generates all accounts from the template and assign them to the right company
1923 * generates all taxes and tax codes, changing account assignations
1924 * generates all accounting properties and assign correctly
1926 _name='wizard.multi.charts.accounts'
1929 'company_id':fields.many2one('res.company','Company',required=True),
1930 'chart_template_id': fields.many2one('account.chart.template','Chart Template',required=True),
1931 'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Bank Accounts',required=True),
1932 'code_digits':fields.integer('# of Digits',required=True,help="No. of Digits to use for account code"),
1935 def _get_chart(self, cr, uid, context={}):
1936 ids = self.pool.get('account.chart.template').search(cr, uid, [], context=context)
1941 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr,uid,[uid],c)[0].company_id.id,
1942 'chart_template_id': _get_chart,
1943 'code_digits': lambda *a:6,
1946 def action_create(self, cr, uid, ids, context=None):
1947 obj_multi = self.browse(cr,uid,ids[0])
1948 obj_acc = self.pool.get('account.account')
1949 obj_acc_tax = self.pool.get('account.tax')
1950 obj_journal = self.pool.get('account.journal')
1951 obj_acc_template = self.pool.get('account.account.template')
1954 obj_acc_root = obj_multi.chart_template_id.account_root_id
1955 tax_code_root_id = obj_multi.chart_template_id.tax_code_root_id.id
1956 company_id = obj_multi.company_id.id
1959 acc_template_ref = {}
1960 tax_template_ref = {}
1961 tax_code_template_ref = {}
1964 #create all the tax code
1965 children_tax_code_template = self.pool.get('account.tax.code.template').search(cr, uid, [('parent_id','child_of',[tax_code_root_id])], order='id')
1966 for tax_code_template in self.pool.get('account.tax.code.template').browse(cr, uid, children_tax_code_template):
1968 'name': (tax_code_root_id == tax_code_template.id) and obj_multi.company_id.name or tax_code_template.name,
1969 'code': tax_code_template.code,
1970 'info': tax_code_template.info,
1971 '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,
1972 'company_id': company_id,
1973 'sign': tax_code_template.sign,
1975 new_tax_code = self.pool.get('account.tax.code').create(cr,uid,vals)
1976 #recording the new tax code to do the mapping
1977 tax_code_template_ref[tax_code_template.id] = new_tax_code
1980 for tax in obj_multi.chart_template_id.tax_template_ids:
1984 'sequence': tax.sequence,
1985 'amount':tax.amount,
1987 'applicable_type': tax.applicable_type,
1988 'domain':tax.domain,
1989 'parent_id': tax.parent_id and ((tax.parent_id.id in tax_template_ref) and tax_template_ref[tax.parent_id.id]) or False,
1990 'child_depend': tax.child_depend,
1991 'python_compute': tax.python_compute,
1992 'python_compute_inv': tax.python_compute_inv,
1993 'python_applicable': tax.python_applicable,
1994 'tax_group':tax.tax_group,
1995 'base_code_id': tax.base_code_id and tax_code_template_ref[tax.base_code_id.id] or False,
1996 'tax_code_id': tax.tax_code_id and tax_code_template_ref[tax.tax_code_id.id] or False,
1997 'base_sign': tax.base_sign,
1998 'tax_sign': tax.tax_sign,
1999 'ref_base_code_id': tax.ref_base_code_id and tax_code_template_ref[tax.ref_base_code_id.id] or False,
2000 'ref_tax_code_id': tax.ref_tax_code_id and tax_code_template_ref[tax.ref_tax_code_id.id] or False,
2001 'ref_base_sign': tax.ref_base_sign,
2002 'ref_tax_sign': tax.ref_tax_sign,
2003 'include_base_amount': tax.include_base_amount,
2004 'description':tax.description,
2005 'company_id': company_id,
2007 new_tax = obj_acc_tax.create(cr,uid,vals_tax)
2008 #as the accounts have not been created yet, we have to wait before filling these fields
2009 todo_dict[new_tax] = {
2010 'account_collected_id': tax.account_collected_id and tax.account_collected_id.id or False,
2011 'account_paid_id': tax.account_paid_id and tax.account_paid_id.id or False,
2013 tax_template_ref[tax.id] = new_tax
2015 #deactivate the parent_store functionnality on account_account for rapidity purpose
2016 self.pool._init = True
2018 children_acc_template = obj_acc_template.search(cr, uid, [('parent_id','child_of',[obj_acc_root.id])])
2019 children_acc_template.sort()
2020 for account_template in obj_acc_template.browse(cr, uid, children_acc_template):
2022 for tax in account_template.tax_ids:
2023 tax_ids.append(tax_template_ref[tax.id])
2024 #create the account_account
2026 dig = obj_multi.code_digits
2027 code_main = len(account_template.code)
2028 code_acc = account_template.code
2029 if code_main<=dig and account_template.type != 'view':
2030 code_acc=str(code_acc) + (str('0'*(dig-code_main)))
2032 'name': (obj_acc_root.id == account_template.id) and obj_multi.company_id.name or account_template.name,
2033 #'sign': account_template.sign,
2034 'currency_id': account_template.currency_id and account_template.currency_id.id or False,
2036 'type': account_template.type,
2037 'user_type': account_template.user_type and account_template.user_type.id or False,
2038 'reconcile': account_template.reconcile,
2039 'shortcut': account_template.shortcut,
2040 'note': account_template.note,
2041 '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,
2042 'tax_ids': [(6,0,tax_ids)],
2043 'company_id': company_id,
2045 new_account = obj_acc.create(cr,uid,vals)
2046 acc_template_ref[account_template.id] = new_account
2047 #reactivate the parent_store functionnality on account_account
2048 self.pool._init = False
2049 self.pool.get('account.account')._parent_store_compute(cr)
2051 for key,value in todo_dict.items():
2052 if value['account_collected_id'] or value['account_paid_id']:
2053 obj_acc_tax.write(cr, uid, [key], vals={
2054 'account_collected_id': acc_template_ref[value['account_collected_id']],
2055 'account_paid_id': acc_template_ref[value['account_paid_id']],
2060 view_id = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Journal View')])[0]
2061 seq_id = self.pool.get('ir.sequence').search(cr,uid,[('code','=','account.journal')])[0]
2062 seq_code = self.pool.get('ir.sequence').get(cr, uid, 'account.journal')
2064 vals_journal['view_id']=view_id
2065 vals_journal['sequence_id']=seq_id
2068 vals_journal['name'] = _('Sales Journal')
2069 vals_journal['type'] = 'sale'
2070 vals_journal['code'] = _('SAJ')
2072 if obj_multi.chart_template_id.property_account_receivable:
2073 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_receivable.id]
2074 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_receivable.id]
2076 obj_journal.create(cr,uid,vals_journal)
2079 vals_journal['name']=_('Purchase Journal')
2080 vals_journal['type']='purchase'
2081 vals_journal['code']=_('EXJ')
2083 if obj_multi.chart_template_id.property_account_payable:
2084 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_payable.id]
2085 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_payable.id]
2087 obj_journal.create(cr,uid,vals_journal)
2090 view_id_cash = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Cash Journal View')])[0]
2091 view_id_cur = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Multi-Currency Cash Journal View')])[0]
2092 ref_acc_bank = obj_multi.chart_template_id.bank_account_view_id
2095 for line in obj_multi.bank_accounts_id:
2096 #create the account_account for this bank journal
2097 tmp = self.pool.get('res.partner.bank').name_get(cr, uid, [line.acc_no.id])[0][1]
2098 dig = obj_multi.code_digits
2100 'name': line.acc_no.bank and line.acc_no.bank.name+' '+tmp or tmp,
2101 'currency_id': line.currency_id and line.currency_id.id or False,
2102 'code': str(int(ref_acc_bank.code.ljust(dig,'0')) + current_num),
2104 'user_type': account_template.user_type and account_template.user_type.id or False,
2106 'parent_id': acc_template_ref[ref_acc_bank.id] or False,
2107 'company_id': company_id,
2109 acc_cash_id = obj_acc.create(cr,uid,vals)
2111 #create the bank journal
2112 vals_journal['name']= vals['name']
2113 vals_journal['code']= _('BNK') + str(current_num)
2114 vals_journal['sequence_id'] = seq_id
2115 vals_journal['type'] = 'cash'
2116 if line.currency_id:
2117 vals_journal['view_id'] = view_id_cur
2118 vals_journal['currency'] = line.currency_id.id
2120 vals_journal['view_id'] = view_id_cash
2121 vals_journal['default_credit_account_id'] = acc_cash_id
2122 vals_journal['default_debit_account_id']= acc_cash_id
2123 obj_journal.create(cr,uid,vals_journal)
2127 #create the properties
2128 property_obj = self.pool.get('ir.property')
2129 fields_obj = self.pool.get('ir.model.fields')
2132 ('property_account_receivable','res.partner','account.account'),
2133 ('property_account_payable','res.partner','account.account'),
2134 ('property_account_expense_categ','product.category','account.account'),
2135 ('property_account_income_categ','product.category','account.account'),
2136 ('property_account_expense','product.template','account.account'),
2137 ('property_account_income','product.template','account.account')
2139 for record in todo_list:
2141 r = property_obj.search(cr, uid, [('name','=', record[0] ),('company_id','=',company_id)])
2142 account = getattr(obj_multi.chart_template_id, record[0])
2143 field = fields_obj.search(cr, uid, [('name','=',record[0]),('model','=',record[1]),('relation','=',record[2])])
2146 'company_id': company_id,
2147 'fields_id': field[0],
2148 'value': account and 'account.account,'+str(acc_template_ref[account.id]) or False,
2151 #the property exist: modify it
2152 property_obj.write(cr, uid, r, vals)
2154 #create the property
2155 property_obj.create(cr, uid, vals)
2158 'view_type': 'form',
2159 "view_mode": 'form',
2160 'res_model': 'ir.actions.configuration.wizard',
2161 'type': 'ir.actions.act_window',
2164 def action_cancel(self,cr,uid,ids,conect=None):
2166 'view_type': 'form',
2167 "view_mode": 'form',
2168 'res_model': 'ir.actions.configuration.wizard',
2169 'type': 'ir.actions.act_window',
2174 wizard_multi_charts_accounts()
2176 class account_bank_accounts_wizard(osv.osv_memory):
2177 _name='account.bank.accounts.wizard'
2180 'acc_no':fields.many2one('res.partner.bank','Account No.',required=True),
2181 'bank_account_id':fields.many2one('wizard.multi.charts.accounts', 'Bank Account', required=True),
2182 'currency_id':fields.many2one('res.currency', 'Currency'),
2185 account_bank_accounts_wizard()
2187 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: