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=64, 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 "If Date=15/01, Number of Days=22, Day of Month=-1, then the due date is 28/02."),
82 '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)."),
83 'payment_id': fields.many2one('account.payment.term','Payment Term', required=True, select=True),
86 'value': lambda *a: 'balance',
87 'sequence': lambda *a: 5,
88 'days2': lambda *a: 0,
91 account_payment_term_line()
94 class account_account_type(osv.osv):
95 _name = "account.account.type"
96 _description = "Account Type"
98 'name': fields.char('Acc. Type Name', size=64, required=True, translate=True),
99 'code': fields.char('Code', size=32, required=True),
100 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of account types."),
101 'partner_account': fields.boolean('Partner account'),
102 'close_method': fields.selection([('none','None'), ('balance','Balance'), ('detail','Detail'),('unreconciled','Unreconciled')], 'Deferral Method', required=True),
103 '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.'),
106 'close_method': lambda *a: 'none',
107 'sequence': lambda *a: 5,
108 'sign': lambda *a: 1,
111 account_account_type()
113 def _code_get(self, cr, uid, context={}):
114 acc_type_obj = self.pool.get('account.account.type')
115 ids = acc_type_obj.search(cr, uid, [])
116 res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context)
117 return [(r['code'], r['name']) for r in res]
119 #----------------------------------------------------------
121 #----------------------------------------------------------
123 class account_tax(osv.osv):
124 _name = 'account.tax'
127 class account_account(osv.osv):
128 _order = "parent_left"
129 _parent_order = "code"
130 _name = "account.account"
131 _description = "Account"
134 def search(self, cr, uid, args, offset=0, limit=None, order=None,
135 context=None, count=False):
142 if args[pos][0]=='code' and args[pos][1] in ('like','ilike') and args[pos][2]:
143 args[pos] = ('code', '=like', str(args[pos][2].replace('%',''))+'%')
144 if args[pos][0]=='journal_id':
148 jour = self.pool.get('account.journal').browse(cr, uid, args[pos][2])
149 if (not (jour.account_control_ids or jour.type_control_ids)) or not args[pos][2]:
152 ids3 = map(lambda x: x.code, jour.type_control_ids)
153 ids1 = super(account_account,self).search(cr, uid, [('type','in',ids3)])
154 ids1 += map(lambda x: x.id, jour.account_control_ids)
155 args[pos] = ('id','in',ids1)
158 if context and context.has_key('consolidate_childs'): #add consolidated childs of accounts
159 ids = super(account_account,self).search(cr, uid, args, offset, limit,
160 order, context=context, count=count)
161 for consolidate_child in self.browse(cr, uid, context['account_id']).child_consol_ids:
162 ids.append(consolidate_child.id)
165 return super(account_account,self).search(cr, uid, args, offset, limit,
166 order, context=context, count=count)
168 def _get_children_and_consol(self, cr, uid, ids, context={}):
169 #this function search for all the children and all consolidated children (recursively) of the given account ids
170 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)], context=context)
172 for rec in self.browse(cr, uid, ids2, context=context):
173 for child in rec.child_consol_ids:
174 ids3.append(child.id)
176 ids3 = self._get_children_and_consol(cr, uid, ids3, 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),0) - COALESCE(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."),
295 def _default_company(self, cr, uid, context={}):
296 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
298 return user.company_id.id
299 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
302 'type' : lambda *a :'view',
303 'reconcile': lambda *a: False,
304 'company_id': _default_company,
305 'active': lambda *a: True,
306 'check_history': lambda *a: True,
307 'currency_mode': lambda *a: 'current'
310 def _check_recursion(self, cr, uid, ids):
311 obj_self=self.browse(cr,uid,ids[0])
312 p_id=obj_self.parent_id and obj_self.parent_id.id
313 if (obj_self in obj_self.child_consol_ids) or (p_id and (p_id is obj_self.id)):
316 cr.execute('select distinct child_id from account_account_consol_rel where parent_id in ('+','.join(map(str,ids))+')')
317 child_ids = filter(None, map(lambda x:x[0], cr.fetchall()))
319 if (p_id and (p_id in c_ids)) or (obj_self.id in c_ids):
322 s_ids=self.search(cr,uid,[('parent_id','in',c_ids)])
323 if p_id and (p_id in s_ids):
330 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
332 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
340 if name and str(name).startswith('partner:'):
341 part_id = int(name.split(':')[1])
342 part = self.pool.get('res.partner').browse(cr, user, part_id, context)
343 args += [('id','in', (part.property_account_payable.id, part.property_account_receivable.id))]
345 if name and str(name).startswith('type:'):
346 type = name.split(':')[1]
347 args += [('type','=', type)]
352 ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit)
354 ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit)
356 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
358 ids = self.search(cr, user, args, context=context, limit=limit)
359 return self.name_get(cr, user, ids, context=context)
361 def name_get(self, cr, uid, ids, context={}):
364 reads = self.read(cr, uid, ids, ['name','code'], context)
367 name = record['name']
369 name = record['code']+' '+name
370 res.append((record['id'],name ))
373 def copy(self, cr, uid, id, default={}, context={},done_list=[]):
374 account = self.browse(cr, uid, id, context=context)
378 default=default.copy()
379 default['parent_id'] = False
380 if account.id in done_list:
382 done_list.append(account.id)
384 for child in account.child_id:
385 child_ids=self.copy(cr, uid, child.id, default, context=context,done_list=done_list)
387 new_child_ids.append(child_ids)
388 default['child_parent_ids'] = [(6, 0, new_child_ids)]
390 default['child_parent_ids'] = False
391 return super(account_account, self).copy(cr, uid, id, default, context=context)
393 def write(self, cr, uid, ids, vals, context=None):
396 if 'active' in vals and not vals['active']:
397 line_obj = self.pool.get('account.move.line')
398 account_ids = self.search(cr, uid, [('id', 'child_of', ids)])
399 if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]):
400 raise osv.except_osv(_('Error !'), _('You can not deactivate an account that contains account moves.'))
401 return super(account_account, self).write(cr, uid, ids, vals, context=context)
404 class account_journal_view(osv.osv):
405 _name = "account.journal.view"
406 _description = "Journal View"
408 'name': fields.char('Journal View', size=64, required=True),
409 'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
412 account_journal_view()
415 class account_journal_column(osv.osv):
416 def _col_get(self, cr, user, context={}):
418 cols = self.pool.get('account.move.line')._columns
420 result.append( (col, cols[col].string) )
423 _name = "account.journal.column"
424 _description = "Journal Column"
426 'name': fields.char('Column Name', size=64, required=True),
427 'field': fields.selection(_col_get, 'Field Name', method=True, required=True, size=32),
428 'view_id': fields.many2one('account.journal.view', 'Journal View', select=True),
429 'sequence': fields.integer('Sequence'),
430 'required': fields.boolean('Required'),
431 'readonly': fields.boolean('Readonly'),
434 account_journal_column()
436 class account_journal(osv.osv):
437 _name = "account.journal"
438 _description = "Journal"
440 'name': fields.char('Journal Name', size=64, required=True, translate=True),
441 'code': fields.char('Code', size=16),
442 'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True),
443 'refund_journal': fields.boolean('Refund Journal'),
445 'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]),
446 'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]),
448 'active': fields.boolean('Active'),
449 'view_id': fields.many2one('account.journal.view', 'View', required=True, help="Gives the view used when writing or browsing entries in this journal. The view tell 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."),
450 'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account', domain="[('type','!=','view')]"),
451 'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account', domain="[('type','!=','view')]"),
452 'centralisation': fields.boolean('Centralised counterpart', help="Check this box if you want that each entry doesn't create a counterpart but share the same counterpart for each entry of this journal. This is used in fiscal year closing."),
453 'update_posted': fields.boolean('Allow Cancelling Entries'),
454 '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."),
455 'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="The sequence gives the display order for a list of journals", required=True),
456 'user_id': fields.many2one('res.users', 'User', help="The responsible user of this journal"),
457 'groups_id': fields.many2many('res.groups', 'account_journal_group_rel', 'journal_id', 'group_id', 'Groups'),
458 'currency': fields.many2one('res.currency', 'Currency', help='The currency used to enter statement'),
459 '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.'),
460 'company_id': fields.related('default_credit_account_id','company_id',type='many2one', relation="res.company", string="Company"),
461 'fy_seq_id': fields.one2many('fiscalyear.seq', 'journal_id', 'Sequences'),
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 'company_id': fields.many2one('res.company', 'Company',
500 help="Keep empty if the fiscal year belongs to several companies."),
501 'date_start': fields.date('Start date', required=True),
502 'date_stop': fields.date('End date', required=True),
503 'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'),
504 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', redonly=True),
508 'state': lambda *a: 'draft',
510 _order = "date_start"
512 def _check_duration(self,cr,uid,ids):
513 obj_fy=self.browse(cr,uid,ids[0])
514 if obj_fy.date_stop < obj_fy.date_start:
519 (_check_duration, 'Error ! The date duration of the Fiscal Year is invalid. ', ['date_stop'])
522 def create_period3(self,cr, uid, ids, context={}):
523 return self.create_period(cr, uid, ids, context, 3)
525 def create_period(self,cr, uid, ids, context={}, interval=1):
526 for fy in self.browse(cr, uid, ids, context):
528 ds = mx.DateTime.strptime(fy.date_start, '%Y-%m-%d')
529 while ds.strftime('%Y-%m-%d')<fy.date_stop:
530 de = ds + RelativeDateTime(months=interval, days=-1)
532 if de.strftime('%Y-%m-%d')>fy.date_stop:
533 de=mx.DateTime.strptime(fy.date_stop, '%Y-%m-%d')
535 self.pool.get('account.period').create(cr, uid, {
536 'name': ds.strftime('%m/%Y'),
537 'code': ds.strftime('%m/%Y'),
538 'date_start': ds.strftime('%Y-%m-%d'),
539 'date_stop': de.strftime('%Y-%m-%d'),
540 'fiscalyear_id': fy.id,
542 ds = ds + RelativeDateTime(months=interval)
545 def find(self, cr, uid, dt=None, exception=True, context={}):
547 dt = time.strftime('%Y-%m-%d')
548 ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)])
551 raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.'))
557 class account_period(osv.osv):
558 _name = "account.period"
559 _description = "Account period"
561 'name': fields.char('Period Name', size=64, required=True),
562 'code': fields.char('Code', size=12),
563 'special': fields.boolean('Special Period', size=12,
564 help="Special periods are periods that can overlap, like the 13rd period in fiscal years for closing entries."),
565 'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}),
566 'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}),
567 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True),
568 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', readonly=True)
571 'state': lambda *a: 'draft',
573 _order = "date_start"
575 def _check_duration(self,cr,uid,ids,context={}):
576 obj_period=self.browse(cr,uid,ids[0])
577 if obj_period.date_stop < obj_period.date_start:
581 def _check_year_limit(self,cr,uid,ids,context={}):
582 for obj_period in self.browse(cr,uid,ids):
583 if obj_period.special:
585 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:
588 pids = self.search(cr, uid, [('date_stop','>=',obj_period.date_start),('date_start','<=',obj_period.date_stop),('special','=',False),('id','<>',obj_period.id)])
589 for period in self.browse(cr, uid, pids):
590 if period.fiscalyear_id.company_id.id==obj_period.fiscalyear_id.company_id.id:
595 (_check_duration, 'Error ! The date duration of the Period(s) is invalid. ', ['date_stop']),
596 (_check_year_limit, 'Invalid period ! Some periods overlap or the date duration is not in the limit of the fiscal year. ', ['date_stop'])
599 def next(self, cr, uid, period, step, context={}):
600 ids = self.search(cr, uid, [('date_start','>',period.date_start)])
605 def find(self, cr, uid, dt=None, context={}):
607 dt = time.strftime('%Y-%m-%d')
608 #CHECKME: shouldn't we check the state of the period?
609 ids = self.search(cr, uid, [('date_start','<=',dt),('date_stop','>=',dt)])
611 raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.'))
614 def action_draft(self, cr, uid, ids, *args):
615 users_roles = self.pool.get('res.users').browse(cr, uid, uid).roles_id
616 for role in users_roles:
617 if role.name=='Period':
620 cr.execute('update account_journal_period set state=%s where period_id=%s', (mode, id))
621 cr.execute('update account_period set state=%s where id=%s', (mode, id))
626 class account_journal_period(osv.osv):
627 _name = "account.journal.period"
628 _description = "Journal - Period"
630 def _icon_get(self, cr, uid, ids, field_name, arg=None, context={}):
631 result = {}.fromkeys(ids, 'STOCK_NEW')
632 for r in self.read(cr, uid, ids, ['state']):
634 'draft': 'STOCK_NEW',
635 'printed': 'STOCK_PRINT_PREVIEW',
636 'done': 'STOCK_DIALOG_AUTHENTICATION',
637 }.get(r['state'], 'STOCK_NEW')
641 'name': fields.char('Journal-Period Name', size=64, required=True),
642 'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"),
643 'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"),
644 'icon': fields.function(_icon_get, method=True, string='Icon', type='string'),
645 'active': fields.boolean('Active', required=True),
646 'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'Status', required=True, readonly=True)
649 def _check(self, cr, uid, ids, context={}):
650 for obj in self.browse(cr, uid, ids, context):
651 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))
654 raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !'))
657 def write(self, cr, uid, ids, vals, context={}):
658 self._check(cr, uid, ids, context)
659 return super(account_journal_period, self).write(cr, uid, ids, vals, context)
661 def create(self, cr, uid, vals, context={}):
662 period_id=vals.get('period_id',False)
664 period = self.pool.get('account.period').browse(cr, uid,period_id)
665 vals['state']=period.state
666 return super(account_journal_period, self).create(cr, uid, vals, context)
668 def unlink(self, cr, uid, ids, context={}):
669 self._check(cr, uid, ids, context)
670 return super(account_journal_period, self).unlink(cr, uid, ids, context)
673 'state': lambda *a: 'draft',
674 'active': lambda *a: True,
678 account_journal_period()
680 class account_fiscalyear(osv.osv):
681 _inherit = "account.fiscalyear"
682 _description = "Fiscal Year"
684 'start_journal_period_id':fields.many2one('account.journal.period','New Entries Journal'),
685 'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True),
689 #----------------------------------------------------------
691 #----------------------------------------------------------
692 class account_move(osv.osv):
693 _name = "account.move"
694 _description = "Account Entry"
697 def name_get(self, cursor, user, ids, context=None):
701 data_move = self.pool.get('account.move').browse(cursor,user,ids)
702 for move in data_move:
703 if move.state=='draft':
704 name = '*' + str(move.id)
707 res.append((move.id, name))
711 def _get_period(self, cr, uid, context):
712 periods = self.pool.get('account.period').find(cr, uid)
718 def _amount_compute(self, cr, uid, ids, name, args, context, where =''):
719 if not ids: return {}
720 cr.execute('select move_id,sum(debit) from account_move_line where move_id in ('+','.join(map(str,ids))+') group by move_id')
721 result = dict(cr.fetchall())
723 result.setdefault(id, 0.0)
727 'name': fields.char('Number', size=64, required=True),
728 'ref': fields.char('Ref', size=64),
729 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}),
730 'journal_id': fields.many2one('account.journal', 'Journal', required=True, states={'posted':[('readonly',True)]}),
731 'state': fields.selection([('draft','Draft'), ('posted','Posted')], 'Status', required=True, readonly=True),
732 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}),
733 'to_check': fields.boolean('To Be Verified'),
734 'partner_id': fields.related('line_id', 'partner_id', type="many2one", relation="res.partner", string="Partner"),
735 'amount': fields.function(_amount_compute, method=True, string='Amount', digits=(16,2)),
736 'date': fields.date('Date', required=True),
737 'type': fields.selection([
738 ('pay_voucher','Cash Payment'),
739 ('bank_pay_voucher','Bank Payment'),
740 ('rec_voucher','Cash Receipt'),
741 ('bank_rec_voucher','Bank Receipt'),
742 ('cont_voucher','Contra'),
743 ('journal_sale_vou','Journal Sale'),
744 ('journal_pur_voucher','Journal Purchase'),
745 ('journal_voucher','Journal Voucher'),
746 ],'Type', readonly=True, select=True, states={'draft':[('readonly',False)]}),
749 'name': lambda *a: '/',
750 'state': lambda *a: 'draft',
751 'period_id': _get_period,
752 'type' : lambda *a : 'journal_voucher',
753 'date': lambda *a:time.strftime('%Y-%m-%d'),
756 def _check_centralisation(self, cursor, user, ids):
757 for move in self.browse(cursor, user, ids):
758 if move.journal_id.centralisation:
759 move_ids = self.search(cursor, user, [
760 ('period_id', '=', move.period_id.id),
761 ('journal_id', '=', move.journal_id.id),
763 if len(move_ids) > 1:
767 def _check_period_journal(self, cursor, user, ids):
768 for move in self.browse(cursor, user, ids):
769 for line in move.line_id:
770 if line.period_id.id != move.period_id.id:
772 if line.journal_id.id != move.journal_id.id:
777 (_check_centralisation,
778 'You can not create more than one move per period on centralized journal',
780 (_check_period_journal,
781 'You can not create entries on different period/journal in the same move',
784 def post(self, cr, uid, ids, context=None):
785 if self.validate(cr, uid, ids, context) and len(ids):
786 for move in self.browse(cr, uid, ids):
789 journal = move.journal_id
790 if journal.sequence_id:
791 new_name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id)
793 raise osv.except_osv(_('Error'), _('No sequence defined in the journal !'))
795 self.write(cr, uid, [move.id], {'name':new_name})
797 cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('posted',))
799 raise osv.except_osv(_('Integrity Error !'), _('You can not validate a non balanced entry !'))
802 def button_validate(self, cursor, user, ids, context=None):
803 return self.post(cursor, user, ids, context=context)
805 def button_cancel(self, cr, uid, ids, context={}):
806 for line in self.browse(cr, uid, ids, context):
807 if not line.journal_id.update_posted:
808 raise osv.except_osv(_('Error !'), _('You can not modify a posted entry of this journal !\nYou should mark the journal to allow canceling entries.'))
810 cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('draft',))
813 def write(self, cr, uid, ids, vals, context={}):
815 c['novalidate'] = True
816 result = super(osv.osv, self).write(cr, uid, ids, vals, c)
817 self.validate(cr, uid, ids, context)
821 # TODO: Check if period is closed !
823 def create(self, cr, uid, vals, context={}):
824 if 'line_id' in vals:
825 if 'journal_id' in vals:
826 for l in vals['line_id']:
828 l[2]['journal_id'] = vals['journal_id']
829 context['journal_id'] = vals['journal_id']
830 if 'period_id' in vals:
831 for l in vals['line_id']:
833 l[2]['period_id'] = vals['period_id']
834 context['period_id'] = vals['period_id']
836 default_period = self._get_period(cr, uid, context)
837 for l in vals['line_id']:
839 l[2]['period_id'] = default_period
840 context['period_id'] = default_period
842 accnt_journal = self.pool.get('account.journal').browse(cr, uid, vals['journal_id'])
843 if 'line_id' in vals:
845 c['novalidate'] = True
846 result = super(account_move, self).create(cr, uid, vals, c)
847 self.validate(cr, uid, [result], context)
849 result = super(account_move, self).create(cr, uid, vals, context)
852 def copy(self, cr, uid, id, default=None, context=None):
855 default = default.copy()
856 default.update({'state':'draft', 'name':'/',})
857 return super(account_move, self).copy(cr, uid, id, default, context)
859 def unlink(self, cr, uid, ids, context={}, check=True):
861 for move in self.browse(cr, uid, ids, context):
862 if move['state'] <> 'draft':
863 raise osv.except_osv(_('UserError'),
864 _('You can not delete posted movement: "%s"!') % \
866 line_ids = map(lambda x: x.id, move.line_id)
867 context['journal_id'] = move.journal_id.id
868 context['period_id'] = move.period_id.id
869 self.pool.get('account.move.line')._update_check(cr, uid, line_ids, context)
870 self.pool.get('account.move.line').unlink(cr, uid, line_ids, context=context)
871 toremove.append(move.id)
872 result = super(account_move, self).unlink(cr, uid, toremove, context)
875 def _compute_balance(self, cr, uid, id, context={}):
876 move = self.browse(cr, uid, [id])[0]
878 for line in move.line_id:
879 amount+= (line.debit - line.credit)
882 def _centralise(self, cr, uid, move, mode):
884 account_id = move.journal_id.default_debit_account_id.id
887 raise osv.except_osv(_('UserError'),
888 _('There is no default default debit account defined \n' \
889 'on journal "%s"') % move.journal_id.name)
891 account_id = move.journal_id.default_credit_account_id.id
894 raise osv.except_osv(_('UserError'),
895 _('There is no default default credit account defined \n' \
896 'on journal "%s"') % move.journal_id.name)
898 # find the first line of this move with the current mode
899 # or create it if it doesn't exist
900 cr.execute('select id from account_move_line where move_id=%s and centralisation=%s limit 1', (move.id, mode))
905 line_id = self.pool.get('account.move.line').create(cr, uid, {
906 'name': 'Centralisation '+mode,
907 'centralisation': mode,
908 'account_id': account_id,
910 'journal_id': move.journal_id.id,
911 'period_id': move.period_id.id,
912 'date': move.period_id.date_stop,
915 }, {'journal_id': move.journal_id.id, 'period_id': move.period_id.id})
917 # find the first line of this move with the other mode
918 # so that we can exclude it from our calculation
919 cr.execute('select id from account_move_line where move_id=%s and centralisation=%s limit 1', (move.id, mode2))
926 cr.execute('select sum('+mode+') from account_move_line where move_id=%s and id<>%s', (move.id, line_id2))
927 result = cr.fetchone()[0] or 0.0
928 cr.execute('update account_move_line set '+mode2+'=%s where id=%s', (result, line_id))
932 # Validate a balanced move. If it is a centralised journal, create a move.
934 def validate(self, cr, uid, ids, context={}):
936 for move in self.browse(cr, uid, ids, context):
937 #unlink analytic lines on move_lines
938 for obj_line in move.line_id:
939 for obj in obj_line.analytic_lines:
940 self.pool.get('account.analytic.line').unlink(cr,uid,obj.id)
942 journal = move.journal_id
947 for line in move.line_id:
948 amount += line.debit - line.credit
949 line_ids.append(line.id)
950 if line.state=='draft':
951 line_draft_ids.append(line.id)
954 company_id = line.account_id.company_id.id
955 if not company_id == line.account_id.company_id.id:
956 raise osv.except_osv(_('Error'), _("Couldn't create move between different companies"))
958 if line.account_id.currency_id:
959 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):
960 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)))
962 if abs(amount) < 0.0001:
963 if not len(line_draft_ids):
965 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
966 'journal_id': move.journal_id.id,
967 'period_id': move.period_id.id,
969 }, context, check=False)
973 if journal.type not in ('purchase','sale'):
976 for line in move.line_id:
977 if move.journal_id.type == 'sale':
980 key = 'account_paid_id'
983 key = 'account_collected_id'
987 key = 'account_collected_id'
990 key = 'account_paid_id'
991 if line.account_id.tax_ids:
992 code = amount = False
993 for tax in line.account_id.tax_ids:
995 acc = getattr(tax, key).id
996 account[acc] = (getattr(tax,
997 field_base + 'tax_code_id').id,
998 getattr(tax, field_base + 'tax_sign'))
999 account2[(acc,getattr(tax,
1000 field_base + 'tax_code_id').id)] = (getattr(tax,
1001 field_base + 'tax_code_id').id,
1002 getattr(tax, field_base + 'tax_sign'))
1003 code = getattr(tax, field_base + 'base_code_id').id
1004 amount = getattr(tax, field_base+'base_sign') * \
1005 (line.debit + line.credit)
1007 if code and not (line.tax_code_id or line.tax_amount):
1008 self.pool.get('account.move.line').write(cr, uid,
1010 'tax_code_id': code,
1011 'tax_amount': amount
1012 }, context=context, check=False)
1017 key = (line.account_id.id, line.tax_code_id.id)
1019 code = account2[key][0]
1020 amount = account2[key][1] * (line.debit + line.credit)
1021 elif line.account_id.id in account:
1022 code = account[line.account_id.id][0]
1023 amount = account[line.account_id.id][1] * (line.debit + line.credit)
1024 if (code or amount) and not (line.tax_code_id or line.tax_amount):
1025 self.pool.get('account.move.line').write(cr, uid, [line.id], {
1026 'tax_code_id': code,
1027 'tax_amount': amount
1028 }, context, check=False)
1033 if journal.centralisation:
1034 self._centralise(cr, uid, move, 'debit')
1035 self._centralise(cr, uid, move, 'credit')
1036 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
1038 }, context, check=False)
1041 self.pool.get('account.move.line').write(cr, uid, line_ids, {
1042 'journal_id': move.journal_id.id,
1043 'period_id': move.period_id.id,
1044 #'tax_code_id': False,
1045 #'tax_amount': False,
1047 }, context, check=False)
1051 for tmp in move.line_id:
1052 list_ids.append(tmp.id)
1053 self.pool.get('account.move.line').create_analytic_lines(cr, uid, list_ids, context)
1057 class account_move_reconcile(osv.osv):
1058 _name = "account.move.reconcile"
1059 _description = "Account Reconciliation"
1061 'name': fields.char('Name', size=64, required=True),
1062 'type': fields.char('Type', size=16, required=True),
1063 'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry lines'),
1064 'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'),
1065 'create_date': fields.date('Creation date', readonly=True),
1068 'name': lambda self,cr,uid,ctx={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile') or '/',
1070 def reconcile_partial_check(self, cr, uid, ids, type='auto', context={}):
1071 for rec in self.browse(cr, uid, ids, context):
1073 for line in rec.line_partial_ids:
1074 total += (line.debit or 0.0) - (line.credit or 0.0)
1076 self.pool.get('account.move.line').write(cr, uid,
1077 map(lambda x: x.id, rec.line_partial_ids),
1078 {'reconcile_id': rec.id }
1082 def name_get(self, cr, uid, ids, context=None):
1086 for r in self.browse(cr, uid, ids, context):
1087 total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0)
1089 name = '%s (%.2f)' % (r.name, total)
1090 result.append((r.id,name))
1092 result.append((r.id,r.name))
1096 account_move_reconcile()
1098 #----------------------------------------------------------
1100 #----------------------------------------------------------
1103 child_depend: la taxe depend des taxes filles
1105 class account_tax_code(osv.osv):
1107 A code for the tax object.
1109 This code is used for some tax declarations.
1111 def _sum(self, cr, uid, ids, name, args, context, where =''):
1112 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
1113 acc_set = ",".join(map(str, ids2))
1114 if context.get('based_on', 'invoices') == 'payments':
1115 cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1116 FROM account_move_line AS line, \
1117 account_move AS move \
1118 LEFT JOIN account_invoice invoice ON \
1119 (invoice.move_id = move.id) \
1120 WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1121 AND move.id = line.move_id \
1122 AND ((invoice.state = \'paid\') \
1123 OR (invoice.id IS NULL)) \
1124 GROUP BY line.tax_code_id')
1126 cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
1127 FROM account_move_line AS line \
1128 WHERE line.tax_code_id in ('+acc_set+') '+where+' \
1129 GROUP BY line.tax_code_id')
1130 res=dict(cr.fetchall())
1131 for record in self.browse(cr, uid, ids, context):
1132 def _rec_get(record):
1133 amount = res.get(record.id, 0.0)
1134 for rec in record.child_ids:
1135 amount += _rec_get(rec) * rec.sign
1137 res[record.id] = round(_rec_get(record), 2)
1140 def _sum_period(self, cr, uid, ids, name, args, context):
1141 if 'period_id' in context and context['period_id']:
1142 period_id = context['period_id']
1144 period_id = self.pool.get('account.period').find(cr, uid)
1145 if not len(period_id):
1146 return dict.fromkeys(ids, 0.0)
1147 period_id = period_id[0]
1148 return self._sum(cr, uid, ids, name, args, context,
1149 where=' and line.period_id='+str(period_id))
1151 _name = 'account.tax.code'
1152 _description = 'Tax Code'
1155 'name': fields.char('Tax Case Name', size=64, required=True),
1156 'code': fields.char('Case Code', size=64),
1157 'info': fields.text('Description'),
1158 'sum': fields.function(_sum, method=True, string="Year Sum"),
1159 'sum_period': fields.function(_sum_period, method=True, string="Period Sum"),
1160 'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True),
1161 'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'),
1162 'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'),
1163 'company_id': fields.many2one('res.company', 'Company', required=True),
1164 'sign': fields.float('Sign for parent', required=True),
1167 def name_get(self, cr, uid, ids, context=None):
1170 if isinstance(ids, (int, long)):
1172 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1173 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1176 def _default_company(self, cr, uid, context={}):
1177 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1179 return user.company_id.id
1180 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1182 'company_id': _default_company,
1183 'sign': lambda *args: 1.0,
1185 def _check_recursion(self, cr, uid, ids):
1188 cr.execute('select distinct parent_id from account_tax_code where id in ('+','.join(map(str,ids))+')')
1189 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1196 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
1198 _order = 'code,name'
1201 class account_tax(osv.osv):
1205 Type: percent, fixed, none, code
1206 PERCENT: tax = price * amount
1207 FIXED: tax = price + amount
1209 CODE: execute python code. localcontext = {'price_unit':pu, 'address':address_object}
1210 return result in the context
1211 Ex: result=round(price_unit*0.21,4)
1213 _name = 'account.tax'
1214 _description = 'Tax'
1216 'name': fields.char('Tax Name', size=64, required=True, help="This name will be used to be displayed on reports"),
1217 '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."),
1218 'amount': fields.float('Amount', required=True, digits=(14,4)),
1219 'active': fields.boolean('Active'),
1220 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1221 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1222 '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."),
1223 'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'),
1224 'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'),
1225 'parent_id':fields.many2one('account.tax', 'Parent Tax Account', select=True),
1226 'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'),
1227 '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."),
1228 'python_compute':fields.text('Python Code'),
1229 'python_compute_inv':fields.text('Python Code (reverse)'),
1230 'python_applicable':fields.text('Python Code'),
1231 '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."),
1234 # Fields used for the VAT declaration
1236 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."),
1237 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."),
1238 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1239 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1241 # Same fields for refund invoices
1243 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the VAT declaration."),
1244 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1245 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1246 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1247 '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"),
1248 'company_id': fields.many2one('res.company', 'Company', required=True),
1249 'description': fields.char('Internal Name',size=32),
1250 '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.")
1253 def name_get(self, cr, uid, ids, context={}):
1257 for record in self.read(cr, uid, ids, ['description','name'], context):
1258 name = record['description'] and record['description'] or record['name']
1259 res.append((record['id'],name ))
1262 def _default_company(self, cr, uid, context={}):
1263 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1265 return user.company_id.id
1266 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1268 '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''',
1269 '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''',
1270 'applicable_type': lambda *a: 'true',
1271 'type': lambda *a: 'percent',
1272 'amount': lambda *a: 0,
1273 'price_include': lambda *a: 0,
1274 'active': lambda *a: 1,
1275 'sequence': lambda *a: 1,
1276 'tax_group': lambda *a: 'vat',
1277 'ref_tax_sign': lambda *a: 1,
1278 'ref_base_sign': lambda *a: 1,
1279 'tax_sign': lambda *a: 1,
1280 'base_sign': lambda *a: 1,
1281 'include_base_amount': lambda *a: False,
1282 'company_id': _default_company,
1286 def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1289 if tax.applicable_type=='code':
1290 localdict = {'price_unit':price_unit, 'address':self.pool.get('res.partner.address').browse(cr, uid, address_id), 'product':product, 'partner':partner}
1291 exec tax.python_applicable in localdict
1292 if localdict.get('result', False):
1298 def _unit_compute(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1299 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1302 cur_price_unit=price_unit
1304 # we compute the amount for the current tax object and append it to the result
1306 data = {'id':tax.id,
1308 'account_collected_id':tax.account_collected_id.id,
1309 'account_paid_id':tax.account_paid_id.id,
1310 'base_code_id': tax.base_code_id.id,
1311 'ref_base_code_id': tax.ref_base_code_id.id,
1312 'sequence': tax.sequence,
1313 'base_sign': tax.base_sign,
1314 'tax_sign': tax.tax_sign,
1315 'ref_base_sign': tax.ref_base_sign,
1316 'ref_tax_sign': tax.ref_tax_sign,
1317 'price_unit': cur_price_unit,
1318 'tax_code_id': tax.tax_code_id.id,
1319 'ref_tax_code_id': tax.ref_tax_code_id.id,
1322 if tax.type=='percent':
1323 amount = cur_price_unit * tax.amount
1324 data['amount'] = amount
1326 elif tax.type=='fixed':
1327 data['amount'] = tax.amount
1328 elif tax.type=='code':
1329 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1330 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1331 exec tax.python_compute in localdict
1332 amount = localdict['result']
1333 data['amount'] = amount
1334 amount2 = data['amount']
1335 if len(tax.child_ids):
1336 if tax.child_depend:
1339 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner)
1340 res.extend(child_tax)
1341 if tax.include_base_amount:
1342 cur_price_unit+=amount2
1345 def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1348 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1352 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1353 one tax for each tax id in IDS and their childs
1355 res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner)
1357 r['amount'] *= quantity
1360 def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1361 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1365 cur_price_unit=price_unit
1367 tax_parent_tot = 0.0
1369 if (tax.type=='percent') and not tax.include_base_amount:
1370 tax_parent_tot+=tax.amount
1373 if tax.type=='percent':
1374 if tax.include_base_amount:
1375 amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
1377 amount = (cur_price_unit / (1 + tax_parent_tot)) * tax.amount
1379 elif tax.type=='fixed':
1382 elif tax.type=='code':
1383 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1384 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1385 exec tax.python_compute_inv in localdict
1386 amount = localdict['result']
1388 if tax.include_base_amount:
1389 cur_price_unit -= amount
1398 'account_collected_id': tax.account_collected_id.id,
1399 'account_paid_id': tax.account_paid_id.id,
1400 'base_code_id': tax.base_code_id.id,
1401 'ref_base_code_id': tax.ref_base_code_id.id,
1402 'sequence': tax.sequence,
1403 'base_sign': tax.base_sign,
1404 'tax_sign': tax.tax_sign,
1405 'ref_base_sign': tax.ref_base_sign,
1406 'ref_tax_sign': tax.ref_tax_sign,
1407 'price_unit': cur_price_unit,
1408 'tax_code_id': tax.tax_code_id.id,
1409 'ref_tax_code_id': tax.ref_tax_code_id.id,
1411 if len(tax.child_ids):
1412 if tax.child_depend:
1416 parent_tax = self._unit_compute_inv(cr, uid, tax.child_ids, amount, address_id, product, partner)
1417 res.extend(parent_tax)
1422 total += r['amount']
1424 r['price_unit'] -= total
1428 def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1430 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1431 Price Unit is a VAT included price
1435 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1436 one tax for each tax id in IDS and their childs
1438 res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
1440 r['amount'] *= quantity
1444 # ---------------------------------------------------------
1445 # Account Entries Models
1446 # ---------------------------------------------------------
1448 class account_model(osv.osv):
1449 _name = "account.model"
1450 _description = "Account Model"
1452 'name': fields.char('Model Name', size=64, required=True, help="This is a model for recurring accounting entries"),
1453 'ref': fields.char('Ref', size=64),
1454 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
1455 'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'),
1456 'legend' :fields.text('Legend',readonly=True,size=100),
1460 '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'),
1466 class account_model_line(osv.osv):
1467 _name = "account.model.line"
1468 _description = "Account Model Entries"
1470 'name': fields.char('Name', size=64, required=True),
1471 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the resources from the lowest sequences to the higher ones"),
1472 'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity on entries"),
1473 'debit': fields.float('Debit', digits=(16,2)),
1474 'credit': fields.float('Credit', digits=(16,2)),
1476 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"),
1478 'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True),
1480 'ref': fields.char('Ref.', size=16),
1482 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."),
1483 'currency_id': fields.many2one('res.currency', 'Currency'),
1485 'partner_id': fields.many2one('res.partner', 'Partner Ref.'),
1486 '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."),
1487 'date': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Current Date', required=True, help="The date of the generated entries"),
1490 'date': lambda *a: 'today'
1493 _sql_constraints = [
1494 ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in model !'),
1495 ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model !'),
1497 account_model_line()
1499 # ---------------------------------------------------------
1500 # Account Subscription
1501 # ---------------------------------------------------------
1504 class account_subscription(osv.osv):
1505 _name = "account.subscription"
1506 _description = "Account Subscription"
1508 'name': fields.char('Name', size=64, required=True),
1509 'ref': fields.char('Ref.', size=16),
1510 'model_id': fields.many2one('account.model', 'Model', required=True),
1512 'date_start': fields.date('Starting date', required=True),
1513 'period_total': fields.integer('Number of period', required=True),
1514 'period_nbr': fields.integer('Period', required=True),
1515 'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True),
1516 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status', required=True, readonly=True),
1518 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines')
1521 'date_start': lambda *a: time.strftime('%Y-%m-%d'),
1522 'period_type': lambda *a: 'month',
1523 'period_total': lambda *a: 12,
1524 'period_nbr': lambda *a: 1,
1525 'state': lambda *a: 'draft',
1527 def state_draft(self, cr, uid, ids, context={}):
1528 self.write(cr, uid, ids, {'state':'draft'})
1531 def check(self, cr, uid, ids, context={}):
1533 for sub in self.browse(cr, uid, ids, context):
1535 for line in sub.lines_id:
1536 if not line.move_id.id:
1540 todone.append(sub.id)
1542 self.write(cr, uid, todone, {'state':'done'})
1545 def remove_line(self, cr, uid, ids, context={}):
1547 for sub in self.browse(cr, uid, ids, context):
1548 for line in sub.lines_id:
1549 if not line.move_id.id:
1550 toremove.append(line.id)
1552 self.pool.get('account.subscription.line').unlink(cr, uid, toremove)
1553 self.write(cr, uid, ids, {'state':'draft'})
1556 def compute(self, cr, uid, ids, context={}):
1557 for sub in self.browse(cr, uid, ids, context):
1559 for i in range(sub.period_total):
1560 self.pool.get('account.subscription.line').create(cr, uid, {
1562 'subscription_id': sub.id,
1564 if sub.period_type=='day':
1565 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(days=sub.period_nbr)).strftime('%Y-%m-%d')
1566 if sub.period_type=='month':
1567 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(months=sub.period_nbr)).strftime('%Y-%m-%d')
1568 if sub.period_type=='year':
1569 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(years=sub.period_nbr)).strftime('%Y-%m-%d')
1570 self.write(cr, uid, ids, {'state':'running'})
1572 account_subscription()
1574 class account_subscription_line(osv.osv):
1575 _name = "account.subscription.line"
1576 _description = "Account Subscription Line"
1578 'subscription_id': fields.many2one('account.subscription', 'Subscription', required=True, select=True),
1579 'date': fields.date('Date', required=True),
1580 'move_id': fields.many2one('account.move', 'Entry'),
1584 def move_create(self, cr, uid, ids, context={}):
1586 for line in self.browse(cr, uid, ids, context):
1590 ids = self.pool.get('account.model').generate(cr, uid, [line.subscription_id.model_id.id], datas, context)
1591 tocheck[line.subscription_id.id] = True
1592 self.write(cr, uid, [line.id], {'move_id':ids[0]})
1594 self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context)
1597 account_subscription_line()
1600 class account_config_wizard(osv.osv_memory):
1601 _name = 'account.config.wizard'
1603 def _get_charts(self, cr, uid, context):
1604 module_obj=self.pool.get('ir.module.module')
1605 ids=module_obj.search(cr, uid, [('category_id', '=', 'Account Charts'), ('state', '<>', 'installed')])
1606 res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)]
1607 res.append((-1, 'None'))
1608 res.sort(key=lambda x: x[1])
1612 'name':fields.char('Name', required=True, size=64, help="Name of the fiscal year as displayed on screens."),
1613 'code':fields.char('Code', required=True, size=64, help="Name of the fiscal year as displayed in reports."),
1614 'date1': fields.date('Starting Date', required=True),
1615 'date2': fields.date('Ending Date', required=True),
1616 'period':fields.selection([('month','Month'),('3months','3 Months')], 'Periods', required=True),
1617 'charts' : fields.selection(_get_charts, 'Charts of Account',required=True)
1620 'code': lambda *a: time.strftime('%Y'),
1621 'name': lambda *a: time.strftime('%Y'),
1622 'date1': lambda *a: time.strftime('%Y-01-01'),
1623 'date2': lambda *a: time.strftime('%Y-12-31'),
1624 'period':lambda *a:'month',
1626 def action_cancel(self,cr,uid,ids,conect=None):
1628 'view_type': 'form',
1629 "view_mode": 'form',
1630 'res_model': 'ir.actions.configuration.wizard',
1631 'type': 'ir.actions.act_window',
1635 def install_account_chart(self, cr, uid, ids, context=None):
1636 for res in self.read(cr,uid,ids):
1637 chart_id = res['charts']
1639 mod_obj = self.pool.get('ir.module.module')
1640 mod_obj.button_install(cr, uid, [chart_id], context=context)
1642 db, pool = pooler.restart_pool(cr.dbname, update_module=True)
1644 def action_create(self, cr, uid,ids, context=None):
1645 for res in self.read(cr,uid,ids):
1646 if 'date1' in res and 'date2' in res:
1647 res_obj = self.pool.get('account.fiscalyear')
1648 start_date=res['date1']
1649 end_date=res['date2']
1650 name=res['name']#DateTime.strptime(start_date, '%Y-%m-%d').strftime('%m.%Y') + '-' + DateTime.strptime(end_date, '%Y-%m-%d').strftime('%m.%Y')
1654 'date_start':start_date,
1655 'date_stop':end_date,
1657 new_id=res_obj.create(cr, uid, vals, context=context)
1658 if res['period']=='month':
1659 res_obj.create_period(cr,uid,[new_id])
1660 elif res['period']=='3months':
1661 res_obj.create_period3(cr,uid,[new_id])
1662 self.install_account_chart(cr,uid,ids)
1664 'view_type': 'form',
1665 "view_mode": 'form',
1666 'res_model': 'ir.actions.configuration.wizard',
1667 'type': 'ir.actions.act_window',
1673 account_config_wizard()
1676 # ---------------------------------------------------------------
1677 # Account Templates : Account, Tax, Tax Code and chart. + Wizard
1678 # ---------------------------------------------------------------
1680 class account_tax_template(osv.osv):
1681 _name = 'account.tax.template'
1682 account_tax_template()
1684 class account_account_template(osv.osv):
1686 _name = "account.account.template"
1687 _description ='Templates for Accounts'
1690 'name': fields.char('Name', size=128, required=True, select=True),
1691 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
1692 'code': fields.char('Code', size=64),
1693 'type': fields.selection([
1694 ('receivable','Receivable'),
1695 ('payable','Payable'),
1697 ('consolidation','Consolidation'),
1699 ('closed','Closed'),
1700 ], 'Internal Type', required=True,),
1701 'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
1702 'reconcile': fields.boolean('Allow Reconciliation', help="Check this option if the user can make a reconciliation of the entries in this account."),
1703 'shortcut': fields.char('Shortcut', size=12),
1704 'note': fields.text('Note'),
1705 'parent_id': fields.many2one('account.account.template','Parent Account Template', ondelete='cascade'),
1706 'child_parent_ids':fields.one2many('account.account.template','parent_id','Children'),
1707 'tax_ids': fields.many2many('account.tax.template', 'account_account_template_tax_rel','account_id','tax_id', 'Default Taxes'),
1711 'reconcile': lambda *a: False,
1712 'type' : lambda *a :'view',
1715 def _check_recursion(self, cr, uid, ids):
1718 cr.execute('select parent_id from account_account_template where id in ('+','.join(map(str,ids))+')')
1719 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1726 (_check_recursion, 'Error ! You can not create recursive account templates.', ['parent_id'])
1730 def name_get(self, cr, uid, ids, context={}):
1733 reads = self.read(cr, uid, ids, ['name','code'], context)
1735 for record in reads:
1736 name = record['name']
1738 name = record['code']+' '+name
1739 res.append((record['id'],name ))
1742 account_account_template()
1744 class account_tax_code_template(osv.osv):
1746 _name = 'account.tax.code.template'
1747 _description = 'Tax Code Template'
1751 'name': fields.char('Tax Case Name', size=64, required=True),
1752 'code': fields.char('Case Code', size=64),
1753 'info': fields.text('Description'),
1754 'parent_id': fields.many2one('account.tax.code.template', 'Parent Code', select=True),
1755 'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Childs Codes'),
1756 'sign': fields.float('Sign for parent', required=True),
1760 'sign': lambda *args: 1.0,
1763 def name_get(self, cr, uid, ids, context=None):
1766 if isinstance(ids, (int, long)):
1768 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1769 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1772 def _check_recursion(self, cr, uid, ids):
1775 cr.execute('select distinct parent_id from account_tax_code_template where id in ('+','.join(map(str,ids))+')')
1776 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1783 (_check_recursion, 'Error ! You can not create recursive Tax Codes.', ['parent_id'])
1785 _order = 'code,name'
1786 account_tax_code_template()
1789 class account_chart_template(osv.osv):
1790 _name="account.chart.template"
1791 _description= "Templates for Account Chart"
1794 'name': fields.char('Name', size=64, required=True),
1795 'account_root_id': fields.many2one('account.account.template','Root Account',required=True,domain=[('parent_id','=',False)]),
1796 'tax_code_root_id': fields.many2one('account.tax.code.template','Root Tax Code',required=True,domain=[('parent_id','=',False)]),
1797 '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'),
1798 'bank_account_view_id': fields.many2one('account.account.template','Bank Account',required=True),
1799 'property_account_receivable': fields.many2one('account.account.template','Receivable Account'),
1800 'property_account_payable': fields.many2one('account.account.template','Payable Account'),
1801 'property_account_expense_categ': fields.many2one('account.account.template','Expense Category Account'),
1802 'property_account_income_categ': fields.many2one('account.account.template','Income Category Account'),
1803 'property_account_expense': fields.many2one('account.account.template','Expense Account on Product Template'),
1804 'property_account_income': fields.many2one('account.account.template','Income Account on Product Template'),
1807 account_chart_template()
1809 class account_tax_template(osv.osv):
1811 _name = 'account.tax.template'
1812 _description = 'Templates for Taxes'
1815 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
1816 'name': fields.char('Tax Name', size=64, required=True),
1817 '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."),
1818 'amount': fields.float('Amount', required=True, digits=(14,4)),
1819 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1820 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1821 '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."),
1822 'account_collected_id':fields.many2one('account.account.template', 'Invoice Tax Account'),
1823 'account_paid_id':fields.many2one('account.account.template', 'Refund Tax Account'),
1824 'parent_id':fields.many2one('account.tax.template', 'Parent Tax Account', select=True),
1825 '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."),
1826 'python_compute':fields.text('Python Code'),
1827 'python_compute_inv':fields.text('Python Code (reverse)'),
1828 'python_applicable':fields.text('Python Code'),
1829 '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."),
1832 # Fields used for the VAT declaration
1834 'base_code_id': fields.many2one('account.tax.code.template', 'Base Code', help="Use this code for the VAT declaration."),
1835 'tax_code_id': fields.many2one('account.tax.code.template', 'Tax Code', help="Use this code for the VAT declaration."),
1836 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1837 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1839 # Same fields for refund invoices
1841 'ref_base_code_id': fields.many2one('account.tax.code.template', 'Refund Base Code', help="Use this code for the VAT declaration."),
1842 'ref_tax_code_id': fields.many2one('account.tax.code.template', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1843 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1844 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1845 '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."),
1846 'description': fields.char('Internal Name', size=32),
1849 def name_get(self, cr, uid, ids, context={}):
1853 for record in self.read(cr, uid, ids, ['description','name'], context):
1854 name = record['description'] and record['description'] or record['name']
1855 res.append((record['id'],name ))
1858 def _default_company(self, cr, uid, context={}):
1859 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1861 return user.company_id.id
1862 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1865 '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''',
1866 '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''',
1867 'applicable_type': lambda *a: 'true',
1868 'type': lambda *a: 'percent',
1869 'amount': lambda *a: 0,
1870 'sequence': lambda *a: 1,
1871 'tax_group': lambda *a: 'vat',
1872 'ref_tax_sign': lambda *a: 1,
1873 'ref_base_sign': lambda *a: 1,
1874 'tax_sign': lambda *a: 1,
1875 'base_sign': lambda *a: 1,
1876 'include_base_amount': lambda *a: False,
1881 account_tax_template()
1883 # Fiscal Position Templates
1885 class account_fiscal_position_template(osv.osv):
1886 _name = 'account.fiscal.position.template'
1887 _description = 'Template for Fiscal Position'
1890 'name': fields.char('Fiscal Position Template', size=64, translate=True, required=True),
1891 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
1892 'account_ids': fields.one2many('account.fiscal.position.account.template', 'position_id', 'Accounts Mapping'),
1893 'tax_ids': fields.one2many('account.fiscal.position.tax.template', 'position_id', 'Taxes Mapping')
1896 account_fiscal_position_template()
1898 class account_fiscal_position_tax_template(osv.osv):
1899 _name = 'account.fiscal.position.tax.template'
1900 _description = 'Fiscal Position Template Taxes Mapping'
1901 _rec_name = 'position_id'
1904 'position_id': fields.many2one('account.fiscal.position.template', 'Fiscal Position', required=True, ondelete='cascade'),
1905 'tax_src_id': fields.many2one('account.tax.template', 'Tax Source', required=True),
1906 'tax_dest_id': fields.many2one('account.tax.template', 'Replacement Tax')
1909 account_fiscal_position_tax_template()
1911 class account_fiscal_position_account_template(osv.osv):
1912 _name = 'account.fiscal.position.account.template'
1913 _description = 'Fiscal Position Template Accounts Mapping'
1914 _rec_name = 'position_id'
1916 'position_id': fields.many2one('account.fiscal.position.template', 'Fiscal Position', required=True, ondelete='cascade'),
1917 'account_src_id': fields.many2one('account.account.template', 'Account Source', domain=[('type','<>','view')], required=True),
1918 'account_dest_id': fields.many2one('account.account.template', 'Account Destination', domain=[('type','<>','view')], required=True)
1921 account_fiscal_position_account_template()
1923 # Multi charts of Accounts wizard
1925 class wizard_multi_charts_accounts(osv.osv_memory):
1927 Create a new account chart for a company.
1930 * an account chart template
1931 * a number of digits for formatting code of non-view accounts
1932 * a list of bank account owned by the company
1934 * generates all accounts from the template and assign them to the right company
1935 * generates all taxes and tax codes, changing account assignations
1936 * generates all accounting properties and assign correctly
1938 _name='wizard.multi.charts.accounts'
1941 'company_id':fields.many2one('res.company','Company',required=True),
1942 'chart_template_id': fields.many2one('account.chart.template','Chart Template',required=True),
1943 'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Bank Accounts',required=True),
1944 'code_digits':fields.integer('# of Digits',required=True,help="No. of Digits to use for account code"),
1947 def _get_chart(self, cr, uid, context={}):
1948 ids = self.pool.get('account.chart.template').search(cr, uid, [], context=context)
1953 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr,uid,[uid],c)[0].company_id.id,
1954 'chart_template_id': _get_chart,
1955 'code_digits': lambda *a:6,
1958 def action_create(self, cr, uid, ids, context=None):
1959 obj_multi = self.browse(cr,uid,ids[0])
1960 obj_acc = self.pool.get('account.account')
1961 obj_acc_tax = self.pool.get('account.tax')
1962 obj_journal = self.pool.get('account.journal')
1963 obj_acc_template = self.pool.get('account.account.template')
1964 obj_fiscal_position_template = self.pool.get('account.fiscal.position.template')
1965 obj_fiscal_position = self.pool.get('account.fiscal.position')
1968 obj_acc_root = obj_multi.chart_template_id.account_root_id
1969 tax_code_root_id = obj_multi.chart_template_id.tax_code_root_id.id
1970 company_id = obj_multi.company_id.id
1973 acc_template_ref = {}
1974 tax_template_ref = {}
1975 tax_code_template_ref = {}
1978 #create all the tax code
1979 children_tax_code_template = self.pool.get('account.tax.code.template').search(cr, uid, [('parent_id','child_of',[tax_code_root_id])], order='id')
1980 for tax_code_template in self.pool.get('account.tax.code.template').browse(cr, uid, children_tax_code_template):
1982 'name': (tax_code_root_id == tax_code_template.id) and obj_multi.company_id.name or tax_code_template.name,
1983 'code': tax_code_template.code,
1984 'info': tax_code_template.info,
1985 '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,
1986 'company_id': company_id,
1987 'sign': tax_code_template.sign,
1989 new_tax_code = self.pool.get('account.tax.code').create(cr,uid,vals)
1990 #recording the new tax code to do the mapping
1991 tax_code_template_ref[tax_code_template.id] = new_tax_code
1994 for tax in obj_multi.chart_template_id.tax_template_ids:
1998 'sequence': tax.sequence,
1999 'amount':tax.amount,
2001 'applicable_type': tax.applicable_type,
2002 'domain':tax.domain,
2003 'parent_id': tax.parent_id and ((tax.parent_id.id in tax_template_ref) and tax_template_ref[tax.parent_id.id]) or False,
2004 'child_depend': tax.child_depend,
2005 'python_compute': tax.python_compute,
2006 'python_compute_inv': tax.python_compute_inv,
2007 'python_applicable': tax.python_applicable,
2008 'tax_group':tax.tax_group,
2009 '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,
2010 '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,
2011 'base_sign': tax.base_sign,
2012 'tax_sign': tax.tax_sign,
2013 '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,
2014 '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,
2015 'ref_base_sign': tax.ref_base_sign,
2016 'ref_tax_sign': tax.ref_tax_sign,
2017 'include_base_amount': tax.include_base_amount,
2018 'description':tax.description,
2019 'company_id': company_id,
2021 new_tax = obj_acc_tax.create(cr,uid,vals_tax)
2022 #as the accounts have not been created yet, we have to wait before filling these fields
2023 todo_dict[new_tax] = {
2024 'account_collected_id': tax.account_collected_id and tax.account_collected_id.id or False,
2025 'account_paid_id': tax.account_paid_id and tax.account_paid_id.id or False,
2027 tax_template_ref[tax.id] = new_tax
2029 #deactivate the parent_store functionnality on account_account for rapidity purpose
2030 self.pool._init = True
2032 children_acc_template = obj_acc_template.search(cr, uid, [('parent_id','child_of',[obj_acc_root.id])])
2033 children_acc_template.sort()
2034 for account_template in obj_acc_template.browse(cr, uid, children_acc_template):
2036 for tax in account_template.tax_ids:
2037 tax_ids.append(tax_template_ref[tax.id])
2038 #create the account_account
2040 dig = obj_multi.code_digits
2041 code_main = account_template.code and len(account_template.code) or 0
2042 code_acc = account_template.code or ''
2043 if code_main>0 and code_main<=dig and account_template.type != 'view':
2044 code_acc=str(code_acc) + (str('0'*(dig-code_main)))
2046 'name': (obj_acc_root.id == account_template.id) and obj_multi.company_id.name or account_template.name,
2047 #'sign': account_template.sign,
2048 'currency_id': account_template.currency_id and account_template.currency_id.id or False,
2050 'type': account_template.type,
2051 'user_type': account_template.user_type and account_template.user_type.id or False,
2052 'reconcile': account_template.reconcile,
2053 'shortcut': account_template.shortcut,
2054 'note': account_template.note,
2055 '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,
2056 'tax_ids': [(6,0,tax_ids)],
2057 'company_id': company_id,
2059 new_account = obj_acc.create(cr,uid,vals)
2060 acc_template_ref[account_template.id] = new_account
2061 #reactivate the parent_store functionnality on account_account
2062 self.pool._init = False
2063 self.pool.get('account.account')._parent_store_compute(cr)
2065 for key,value in todo_dict.items():
2066 if value['account_collected_id'] or value['account_paid_id']:
2067 obj_acc_tax.write(cr, uid, [key], vals={
2068 'account_collected_id': acc_template_ref[value['account_collected_id']],
2069 'account_paid_id': acc_template_ref[value['account_paid_id']],
2074 view_id = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Journal View')])[0]
2075 seq_id = self.pool.get('ir.sequence').search(cr,uid,[('code','=','account.journal')])[0]
2076 seq_code = self.pool.get('ir.sequence').get(cr, uid, 'account.journal')
2078 vals_journal['view_id']=view_id
2079 vals_journal['sequence_id']=seq_id
2082 vals_journal['name'] = _('Sales Journal')
2083 vals_journal['type'] = 'sale'
2084 vals_journal['code'] = _('SAJ')
2086 if obj_multi.chart_template_id.property_account_receivable:
2087 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_income_categ.id]
2088 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_income_categ.id]
2090 obj_journal.create(cr,uid,vals_journal)
2093 vals_journal['name']=_('Purchase Journal')
2094 vals_journal['type']='purchase'
2095 vals_journal['code']=_('EXJ')
2097 if obj_multi.chart_template_id.property_account_payable:
2098 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_expense_categ.id]
2099 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_expense_categ.id]
2101 obj_journal.create(cr,uid,vals_journal)
2104 view_id_cash = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Cash Journal View')])[0]
2105 view_id_cur = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Multi-Currency Cash Journal View')])[0]
2106 ref_acc_bank = obj_multi.chart_template_id.bank_account_view_id
2109 for line in obj_multi.bank_accounts_id:
2110 #create the account_account for this bank journal
2111 tmp = self.pool.get('res.partner.bank').name_get(cr, uid, [line.acc_no.id])[0][1]
2112 dig = obj_multi.code_digits
2114 'name': line.acc_no.bank and line.acc_no.bank.name+' '+tmp or tmp,
2115 'currency_id': line.currency_id and line.currency_id.id or False,
2116 'code': str(ref_acc_bank.code.ljust(dig,'0') + str(current_num)),
2118 'user_type': account_template.user_type and account_template.user_type.id or False,
2120 'parent_id': acc_template_ref[ref_acc_bank.id] or False,
2121 'company_id': company_id,
2123 acc_cash_id = obj_acc.create(cr,uid,vals)
2125 #create the bank journal
2126 vals_journal['name']= vals['name']
2127 vals_journal['code']= _('BNK') + str(current_num)
2128 vals_journal['sequence_id'] = seq_id
2129 vals_journal['type'] = 'cash'
2130 if line.currency_id:
2131 vals_journal['view_id'] = view_id_cur
2132 vals_journal['currency'] = line.currency_id.id
2134 vals_journal['view_id'] = view_id_cash
2135 vals_journal['default_credit_account_id'] = acc_cash_id
2136 vals_journal['default_debit_account_id']= acc_cash_id
2137 obj_journal.create(cr,uid,vals_journal)
2141 #create the properties
2142 property_obj = self.pool.get('ir.property')
2143 fields_obj = self.pool.get('ir.model.fields')
2146 ('property_account_receivable','res.partner','account.account'),
2147 ('property_account_payable','res.partner','account.account'),
2148 ('property_account_expense_categ','product.category','account.account'),
2149 ('property_account_income_categ','product.category','account.account'),
2150 ('property_account_expense','product.template','account.account'),
2151 ('property_account_income','product.template','account.account')
2153 for record in todo_list:
2155 r = property_obj.search(cr, uid, [('name','=', record[0] ),('company_id','=',company_id)])
2156 account = getattr(obj_multi.chart_template_id, record[0])
2157 field = fields_obj.search(cr, uid, [('name','=',record[0]),('model','=',record[1]),('relation','=',record[2])])
2160 'company_id': company_id,
2161 'fields_id': field[0],
2162 'value': account and 'account.account,'+str(acc_template_ref[account.id]) or False,
2165 #the property exist: modify it
2166 property_obj.write(cr, uid, r, vals)
2168 #create the property
2169 property_obj.create(cr, uid, vals)
2171 fp_ids = obj_fiscal_position_template.search(cr, uid,[('chart_template_id', '=', obj_multi.chart_template_id.id)])
2174 for position in obj_fiscal_position_template.browse(cr, uid, fp_ids):
2177 'company_id' : company_id,
2178 'name' : position.name,
2180 new_fp = obj_fiscal_position.create(cr, uid, vals_fp)
2182 obj_tax_fp = self.pool.get('account.fiscal.position.tax')
2183 obj_ac_fp = self.pool.get('account.fiscal.position.account')
2185 for tax in position.tax_ids:
2187 'tax_src_id' : tax_template_ref[tax.tax_src_id.id],
2188 'tax_dest_id' : tax_template_ref[tax.tax_dest_id.id],
2189 'position_id' : new_fp,
2191 obj_tax_fp.create(cr, uid, vals_tax)
2193 for acc in position.account_ids:
2195 'account_src_id' : acc_template_ref[acc.account_src_id.id],
2196 'account_dest_id' : acc_template_ref[acc.account_dest_id.id],
2197 'position_id' : new_fp,
2199 obj_ac_fp.create(cr, uid, vals_acc)
2202 'view_type': 'form',
2203 "view_mode": 'form',
2204 'res_model': 'ir.actions.configuration.wizard',
2205 'type': 'ir.actions.act_window',
2208 def action_cancel(self,cr,uid,ids,conect=None):
2210 'view_type': 'form',
2211 "view_mode": 'form',
2212 'res_model': 'ir.actions.configuration.wizard',
2213 'type': 'ir.actions.act_window',
2218 wizard_multi_charts_accounts()
2220 class account_bank_accounts_wizard(osv.osv_memory):
2221 _name='account.bank.accounts.wizard'
2224 'acc_no':fields.many2one('res.partner.bank','Account No.',required=True),
2225 'bank_account_id':fields.many2one('wizard.multi.charts.accounts', 'Bank Account', required=True),
2226 'currency_id':fields.many2one('res.currency', 'Currency'),
2229 account_bank_accounts_wizard()
2231 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: