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"
133 _parent_order = 'length(code),code'
135 def search(self, cr, uid, args, offset=0, limit=None, order=None,
136 context=None, count=False):
143 if args[pos][0]=='code' and args[pos][1] in ('like','ilike') and args[pos][2]:
144 args[pos] = ('code', '=like', str(args[pos][2].replace('%',''))+'%')
145 if args[pos][0]=='journal_id':
149 jour = self.pool.get('account.journal').browse(cr, uid, args[pos][2])
150 if (not (jour.account_control_ids or jour.type_control_ids)) or not args[pos][2]:
153 ids3 = map(lambda x: x.code, jour.type_control_ids)
154 ids1 = super(account_account,self).search(cr, uid, [('type','in',ids3)])
155 ids1 += map(lambda x: x.id, jour.account_control_ids)
156 args[pos] = ('id','in',ids1)
159 if context and context.has_key('consolidate_childs'): #add consolidated childs of accounts
160 ids = super(account_account,self).search(cr, uid, args, offset, limit,
161 order, context=context, count=count)
162 for consolidate_child in self.browse(cr, uid, context['account_id']).child_consol_ids:
163 ids.append(consolidate_child.id)
166 return super(account_account,self).search(cr, uid, args, offset, limit,
167 order, context=context, count=count)
169 def _get_children_and_consol(self, cr, uid, ids, context={}):
170 #this function search for all the children and all consolidated children (recursively) of the given account ids
171 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)], context=context)
173 for rec in self.browse(cr, uid, ids2, context=context):
174 for child in rec.child_consol_ids:
175 ids3.append(child.id)
177 ids3 = self._get_children_and_consol(cr, uid, ids3, context)
180 def __compute(self, cr, uid, ids, field_names, arg, context={}, query=''):
181 #compute the balance/debit/credit accordingly to the value of field_name for the given account ids
183 'balance': "COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance ",
184 'debit': "COALESCE(SUM(l.debit), 0) as debit ",
185 'credit': "COALESCE(SUM(l.credit), 0) as credit "
187 #get all the necessary accounts
188 ids2 = self._get_children_and_consol(cr, uid, ids, context)
189 acc_set = ",".join(map(str, ids2))
190 #compute for each account the balance/debit/credit from the move lines
193 query = self.pool.get('account.move.line')._query_get(cr, uid,
195 cr.execute(("SELECT l.account_id as id, " +\
196 ' , '.join(map(lambda x: mapping[x], field_names)) +
198 "account_move_line l " \
200 "l.account_id IN (%s) " \
201 "AND " + query + " " \
202 "GROUP BY l.account_id") % (acc_set, ))
204 for res in cr.dictfetchall():
205 accounts[res['id']] = res
207 #for the asked accounts, get from the dictionnary 'accounts' the value of it
210 res[id] = self._get_account_values(cr, uid, id, accounts, field_names, context)
213 def _get_account_values(self, cr, uid, id, accounts, field_names, context={}):
214 res = {}.fromkeys(field_names, 0.0)
215 browse_rec = self.browse(cr, uid, id)
216 if browse_rec.type == 'consolidation':
217 ids2 = self.read(cr, uid, [browse_rec.id], ['child_consol_ids'], context)[0]['child_consol_ids']
218 for t in self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])]):
219 if t not in ids2 and t != browse_rec.id:
222 tmp = self._get_account_values(cr, uid, i, accounts, field_names, context)
223 for a in field_names:
226 ids2 = self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])])
228 for a in field_names:
229 res[a] += accounts.get(i, {}).get(a, 0.0)
232 def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}):
234 for rec in self.browse(cr, uid, ids, context):
235 result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code)
238 def _get_child_ids(self, cr, uid, ids, field_name, arg, context={}):
240 for record in self.browse(cr, uid, ids, context):
241 if record.child_parent_ids:
242 result[record.id]=[x.id for x in record.child_parent_ids]
246 if record.child_consol_ids:
247 for acc in record.child_consol_ids:
248 result[record.id].append(acc.id)
253 'name': fields.char('Name', size=128, required=True, select=True),
254 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
255 'code': fields.char('Code', size=64, required=True),
256 'type': fields.selection([
257 ('receivable','Receivable'),
258 ('payable','Payable'),
260 ('consolidation','Consolidation'),
263 ], 'Internal Type', required=True,),
265 'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
266 'parent_id': fields.many2one('account.account','Parent', ondelete='cascade'),
267 'child_parent_ids':fields.one2many('account.account','parent_id','Children'),
268 'child_consol_ids':fields.many2many('account.account', 'account_account_consol_rel', 'child_id', 'parent_id', 'Consolidated Children'),
269 'child_id': fields.function(_get_child_ids, method=True, type='many2many',relation="account.account",string="Children Accounts"),
270 'balance': fields.function(__compute, digits=(16,2), method=True, string='Balance', multi='balance'),
271 'credit': fields.function(__compute, digits=(16,2), method=True, string='Credit', multi='balance'),
272 'debit': fields.function(__compute, digits=(16,2), method=True, string='Debit', multi='balance'),
273 'reconcile': fields.boolean('Reconcile', help="Check this account if the user can make a reconciliation of the entries in this account."),
274 'shortcut': fields.char('Shortcut', size=12),
275 'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel',
276 'account_id','tax_id', 'Default Taxes'),
277 'note': fields.text('Note'),
278 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Company Currency'),
279 'company_id': fields.many2one('res.company', 'Company', required=True),
280 'active': fields.boolean('Active', select=2),
282 'parent_left': fields.integer('Parent Left', select=1),
283 'parent_right': fields.integer('Parent Right', select=1),
284 'currency_mode': fields.selection([('current','At Date'),('average','Average Rate')], 'Outgoing Currencies Rate',
286 'This will select how is computed the current currency rate for outgoing transactions. '\
287 'In most countries the legal method is "average" but only a few softwares are able to '\
288 'manage this. So if you import from another software, you may have to use the rate at date. ' \
289 'Incoming transactions, always use the rate at date.', \
291 'check_history': fields.boolean('Display History',
292 help="Check this box if you want to print all entries when printing the General Ledger, "\
293 "otherwise it will only print its balance."),
296 def _default_company(self, cr, uid, context={}):
297 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
299 return user.company_id.id
300 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
303 'type' : lambda *a :'view',
304 'reconcile': lambda *a: False,
305 'company_id': _default_company,
306 'active': lambda *a: True,
307 'check_history': lambda *a: True,
308 'currency_mode': lambda *a: 'current'
311 def _check_recursion(self, cr, uid, ids):
312 obj_self=self.browse(cr,uid,ids[0])
313 p_id=obj_self.parent_id and obj_self.parent_id.id
314 if (obj_self in obj_self.child_consol_ids) or (p_id and (p_id is obj_self.id)):
317 cr.execute('select distinct child_id from account_account_consol_rel where parent_id in ('+','.join(map(str,ids))+')')
318 child_ids = filter(None, map(lambda x:x[0], cr.fetchall()))
320 if (p_id and (p_id in c_ids)) or (obj_self.id in c_ids):
323 s_ids=self.search(cr,uid,[('parent_id','in',c_ids)])
324 if p_id and (p_id in s_ids):
331 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
333 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
341 if name and str(name).startswith('partner:'):
342 part_id = int(name.split(':')[1])
343 part = self.pool.get('res.partner').browse(cr, user, part_id, context)
344 args += [('id','in', (part.property_account_payable.id, part.property_account_receivable.id))]
346 if name and str(name).startswith('type:'):
347 type = name.split(':')[1]
348 args += [('type','=', type)]
353 ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit)
355 ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit)
357 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
359 ids = self.search(cr, user, args, context=context, limit=limit)
360 return self.name_get(cr, user, ids, context=context)
362 def name_get(self, cr, uid, ids, context={}):
365 reads = self.read(cr, uid, ids, ['name','code'], context)
368 name = record['name']
370 name = record['code']+' '+name
371 res.append((record['id'],name ))
374 def copy(self, cr, uid, id, default={}, context={},done_list=[]):
375 account = self.browse(cr, uid, id, context=context)
379 default=default.copy()
380 default['parent_id'] = False
381 if account.id in done_list:
383 done_list.append(account.id)
385 for child in account.child_id:
386 child_ids=self.copy(cr, uid, child.id, default, context=context,done_list=done_list)
388 new_child_ids.append(child_ids)
389 default['child_parent_ids'] = [(6, 0, new_child_ids)]
391 default['child_parent_ids'] = False
392 return super(account_account, self).copy(cr, uid, id, default, context=context)
394 def write(self, cr, uid, ids, vals, context=None):
397 if 'active' in vals and not vals['active']:
398 line_obj = self.pool.get('account.move.line')
399 account_ids = self.search(cr, uid, [('id', 'child_of', ids)])
400 if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]):
401 raise osv.except_osv(_('Error !'), _('You can not deactivate an account that contains account moves.'))
402 return super(account_account, self).write(cr, uid, ids, vals, context=context)
405 class account_journal_view(osv.osv):
406 _name = "account.journal.view"
407 _description = "Journal View"
409 'name': fields.char('Journal View', size=64, required=True),
410 'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
413 account_journal_view()
416 class account_journal_column(osv.osv):
417 def _col_get(self, cr, user, context={}):
419 cols = self.pool.get('account.move.line')._columns
421 result.append( (col, cols[col].string) )
424 _name = "account.journal.column"
425 _description = "Journal Column"
427 'name': fields.char('Column Name', size=64, required=True),
428 'field': fields.selection(_col_get, 'Field Name', method=True, required=True, size=32),
429 'view_id': fields.many2one('account.journal.view', 'Journal View', select=True),
430 'sequence': fields.integer('Sequence'),
431 'required': fields.boolean('Required'),
432 'readonly': fields.boolean('Readonly'),
435 account_journal_column()
437 class account_journal(osv.osv):
438 _name = "account.journal"
439 _description = "Journal"
441 'name': fields.char('Journal Name', size=64, required=True, translate=True),
442 'code': fields.char('Code', size=16),
443 'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True),
444 'refund_journal': fields.boolean('Refund Journal'),
446 'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]),
447 'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]),
449 'active': fields.boolean('Active'),
450 '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."),
451 'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account', domain="[('type','!=','view')]"),
452 'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account', domain="[('type','!=','view')]"),
453 '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."),
454 'update_posted': fields.boolean('Allow Cancelling Entries'),
455 '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."),
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"),
462 'fy_seq_id': fields.one2many('fiscalyear.seq', 'journal_id', 'Sequences'),
466 'active': lambda *a: 1,
467 'user_id': lambda self,cr,uid,context: uid,
469 def create(self, cr, uid, vals, context={}):
470 journal_id = super(osv.osv, self).create(cr, uid, vals, context)
471 # journal_name = self.browse(cr, uid, [journal_id])[0].code
472 # periods = self.pool.get('account.period')
473 # ids = periods.search(cr, uid, [('date_stop','>=',time.strftime('%Y-%m-%d'))])
474 # for period in periods.browse(cr, uid, ids):
475 # self.pool.get('account.journal.period').create(cr, uid, {
476 # 'name': (journal_name or '')+':'+(period.code or ''),
477 # 'journal_id': journal_id,
478 # 'period_id': period.id
481 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
488 ids = self.search(cr, user, [('code','ilike',name)]+ args, limit=limit)
490 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
491 return self.name_get(cr, user, ids, context=context)
494 class account_fiscalyear(osv.osv):
495 _name = "account.fiscalyear"
496 _description = "Fiscal Year"
498 'name': fields.char('Fiscal Year', size=64, required=True),
499 'code': fields.char('Code', size=6, required=True),
500 'company_id': fields.many2one('res.company', 'Company',
501 help="Keep empty if the fiscal year belongs to several companies."),
502 'date_start': fields.date('Start date', required=True),
503 'date_stop': fields.date('End date', required=True),
504 'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'),
505 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', redonly=True),
509 'state': lambda *a: 'draft',
511 _order = "date_start"
513 def _check_duration(self,cr,uid,ids):
514 obj_fy=self.browse(cr,uid,ids[0])
515 if obj_fy.date_stop < obj_fy.date_start:
520 (_check_duration, 'Error ! The date duration of the Fiscal Year is invalid. ', ['date_stop'])
523 def create_period3(self,cr, uid, ids, context={}):
524 return self.create_period(cr, uid, ids, context, 3)
526 def create_period(self,cr, uid, ids, context={}, interval=1):
527 for fy in self.browse(cr, uid, ids, context):
529 ds = mx.DateTime.strptime(fy.date_start, '%Y-%m-%d')
530 while ds.strftime('%Y-%m-%d')<fy.date_stop:
531 de = ds + RelativeDateTime(months=interval, days=-1)
533 if de.strftime('%Y-%m-%d')>fy.date_stop:
534 de=mx.DateTime.strptime(fy.date_stop, '%Y-%m-%d')
536 self.pool.get('account.period').create(cr, uid, {
537 'name': ds.strftime('%m/%Y'),
538 'code': ds.strftime('%m/%Y'),
539 'date_start': ds.strftime('%Y-%m-%d'),
540 'date_stop': de.strftime('%Y-%m-%d'),
541 'fiscalyear_id': fy.id,
543 ds = ds + RelativeDateTime(months=interval)
546 def find(self, cr, uid, dt=None, exception=True, context={}):
548 dt = time.strftime('%Y-%m-%d')
549 ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)])
552 raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.'))
558 class account_period(osv.osv):
559 _name = "account.period"
560 _description = "Account period"
562 'name': fields.char('Period Name', size=64, required=True),
563 'code': fields.char('Code', size=12),
564 'special': fields.boolean('Opening/Closing Period', size=12,
565 help="These periods can overlap."),
566 'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}),
567 'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}),
568 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True),
569 'state': fields.selection([('draft','Draft'), ('done','Done')], 'Status', readonly=True)
572 'state': lambda *a: 'draft',
574 _order = "date_start"
576 def _check_duration(self,cr,uid,ids,context={}):
577 obj_period=self.browse(cr,uid,ids[0])
578 if obj_period.date_stop < obj_period.date_start:
582 def _check_year_limit(self,cr,uid,ids,context={}):
583 for obj_period in self.browse(cr,uid,ids):
584 if obj_period.special:
586 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:
589 pids = self.search(cr, uid, [('date_stop','>=',obj_period.date_start),('date_start','<=',obj_period.date_stop),('special','=',False),('id','<>',obj_period.id)])
590 for period in self.browse(cr, uid, pids):
591 if period.fiscalyear_id.company_id.id==obj_period.fiscalyear_id.company_id.id:
596 (_check_duration, 'Error ! The date duration of the Period(s) is invalid. ', ['date_stop']),
597 (_check_year_limit, 'Invalid period ! Some periods overlap or the date duration is not in the limit of the fiscal year. ', ['date_stop'])
600 def next(self, cr, uid, period, step, context={}):
601 ids = self.search(cr, uid, [('date_start','>',period.date_start)])
606 def find(self, cr, uid, dt=None, context={}):
608 dt = time.strftime('%Y-%m-%d')
609 #CHECKME: shouldn't we check the state of the period?
610 ids = self.search(cr, uid, [('date_start','<=',dt),('date_stop','>=',dt)])
612 raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.'))
615 def action_draft(self, cr, uid, ids, *args):
616 users_roles = self.pool.get('res.users').browse(cr, uid, uid).roles_id
617 for role in users_roles:
618 if role.name=='Period':
621 cr.execute('update account_journal_period set state=%s where period_id=%s', (mode, id))
622 cr.execute('update account_period set state=%s where id=%s', (mode, id))
627 class account_journal_period(osv.osv):
628 _name = "account.journal.period"
629 _description = "Journal - Period"
631 def _icon_get(self, cr, uid, ids, field_name, arg=None, context={}):
632 result = {}.fromkeys(ids, 'STOCK_NEW')
633 for r in self.read(cr, uid, ids, ['state']):
635 'draft': 'STOCK_NEW',
636 'printed': 'STOCK_PRINT_PREVIEW',
637 'done': 'STOCK_DIALOG_AUTHENTICATION',
638 }.get(r['state'], 'STOCK_NEW')
642 'name': fields.char('Journal-Period Name', size=64, required=True),
643 'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"),
644 'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"),
645 'icon': fields.function(_icon_get, method=True, string='Icon', type='string'),
646 'active': fields.boolean('Active', required=True),
647 'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'Status', required=True, readonly=True)
650 def _check(self, cr, uid, ids, context={}):
651 for obj in self.browse(cr, uid, ids, context):
652 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))
655 raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !'))
658 def write(self, cr, uid, ids, vals, context={}):
659 self._check(cr, uid, ids, context)
660 return super(account_journal_period, self).write(cr, uid, ids, vals, context)
662 def create(self, cr, uid, vals, context={}):
663 period_id=vals.get('period_id',False)
665 period = self.pool.get('account.period').browse(cr, uid,period_id)
666 vals['state']=period.state
667 return super(account_journal_period, self).create(cr, uid, vals, context)
669 def unlink(self, cr, uid, ids, context={}):
670 self._check(cr, uid, ids, context)
671 return super(account_journal_period, self).unlink(cr, uid, ids, context)
674 'state': lambda *a: 'draft',
675 'active': lambda *a: True,
679 account_journal_period()
681 class account_fiscalyear(osv.osv):
682 _inherit = "account.fiscalyear"
683 _description = "Fiscal Year"
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_year(self, cr, uid, ids, name, args, context):
1141 if 'fiscalyear_id' in context and context['fiscalyear_id']:
1142 fiscalyear_id = context['fiscalyear_id']
1144 fiscalyear_id = self.pool.get('account.fiscalyear').find(cr, uid, exception=False)
1147 pids = map(lambda x: str(x.id), self.pool.get('account.fiscalyear').browse(cr, uid, fiscalyear_id).period_ids)
1149 where = ' and period_id in (' + (','.join(pids))+')'
1150 return self._sum(cr, uid, ids, name, args, context,
1153 def _sum_period(self, cr, uid, ids, name, args, context):
1154 if 'period_id' in context and context['period_id']:
1155 period_id = context['period_id']
1157 period_id = self.pool.get('account.period').find(cr, uid)
1158 if not len(period_id):
1159 return dict.fromkeys(ids, 0.0)
1160 period_id = period_id[0]
1161 return self._sum(cr, uid, ids, name, args, context,
1162 where=' and line.period_id='+str(period_id))
1164 _name = 'account.tax.code'
1165 _description = 'Tax Code'
1168 'name': fields.char('Tax Case Name', size=64, required=True),
1169 'code': fields.char('Case Code', size=64),
1170 'info': fields.text('Description'),
1171 'sum': fields.function(_sum_year, method=True, string="Year Sum"),
1172 'sum_period': fields.function(_sum_period, method=True, string="Period Sum"),
1173 'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True),
1174 'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'),
1175 'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'),
1176 'company_id': fields.many2one('res.company', 'Company', required=True),
1177 'sign': fields.float('Sign for parent', required=True),
1180 def name_get(self, cr, uid, ids, context=None):
1183 if isinstance(ids, (int, long)):
1185 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1186 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1189 def _default_company(self, cr, uid, context={}):
1190 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1192 return user.company_id.id
1193 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1195 'company_id': _default_company,
1196 'sign': lambda *args: 1.0,
1198 def _check_recursion(self, cr, uid, ids):
1201 cr.execute('select distinct parent_id from account_tax_code where id in ('+','.join(map(str,ids))+')')
1202 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1209 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
1211 _order = 'code,name'
1214 class account_tax(osv.osv):
1218 Type: percent, fixed, none, code
1219 PERCENT: tax = price * amount
1220 FIXED: tax = price + amount
1222 CODE: execute python code. localcontext = {'price_unit':pu, 'address':address_object}
1223 return result in the context
1224 Ex: result=round(price_unit*0.21,4)
1226 _name = 'account.tax'
1227 _description = 'Tax'
1229 'name': fields.char('Tax Name', size=64, required=True, help="This name will be used to be displayed on reports"),
1230 '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."),
1231 'amount': fields.float('Amount', required=True, digits=(14,4)),
1232 'active': fields.boolean('Active'),
1233 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1234 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True,
1235 help="If not applicable (computed through a Python code), the tax do not appears on the invoice."),
1236 '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."),
1237 'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'),
1238 'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'),
1239 'parent_id':fields.many2one('account.tax', 'Parent Tax Account', select=True),
1240 'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'),
1241 '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."),
1242 'python_compute':fields.text('Python Code'),
1243 'python_compute_inv':fields.text('Python Code (reverse)'),
1244 'python_applicable':fields.text('Python Code'),
1245 '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."),
1248 # Fields used for the VAT declaration
1250 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."),
1251 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."),
1252 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1253 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1255 # Same fields for refund invoices
1257 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the VAT declaration."),
1258 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1259 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1260 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1261 '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"),
1262 'company_id': fields.many2one('res.company', 'Company', required=True),
1263 'description': fields.char('Internal Name',size=32),
1264 '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.")
1267 def name_get(self, cr, uid, ids, context={}):
1271 for record in self.read(cr, uid, ids, ['description','name'], context):
1272 name = record['description'] and record['description'] or record['name']
1273 res.append((record['id'],name ))
1276 def _default_company(self, cr, uid, context={}):
1277 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1279 return user.company_id.id
1280 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1282 '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''',
1283 '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''',
1284 'applicable_type': lambda *a: 'true',
1285 'type': lambda *a: 'percent',
1286 'amount': lambda *a: 0,
1287 'price_include': lambda *a: 0,
1288 'active': lambda *a: 1,
1289 'sequence': lambda *a: 1,
1290 'tax_group': lambda *a: 'vat',
1291 'ref_tax_sign': lambda *a: 1,
1292 'ref_base_sign': lambda *a: 1,
1293 'tax_sign': lambda *a: 1,
1294 'base_sign': lambda *a: 1,
1295 'include_base_amount': lambda *a: False,
1296 'company_id': _default_company,
1300 def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1303 if tax.applicable_type=='code':
1304 localdict = {'price_unit':price_unit, 'address':self.pool.get('res.partner.address').browse(cr, uid, address_id), 'product':product, 'partner':partner}
1305 exec tax.python_applicable in localdict
1306 if localdict.get('result', False):
1312 def _unit_compute(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1313 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1316 cur_price_unit=price_unit
1318 # we compute the amount for the current tax object and append it to the result
1320 data = {'id':tax.id,
1322 'account_collected_id':tax.account_collected_id.id,
1323 'account_paid_id':tax.account_paid_id.id,
1324 'base_code_id': tax.base_code_id.id,
1325 'ref_base_code_id': tax.ref_base_code_id.id,
1326 'sequence': tax.sequence,
1327 'base_sign': tax.base_sign,
1328 'tax_sign': tax.tax_sign,
1329 'ref_base_sign': tax.ref_base_sign,
1330 'ref_tax_sign': tax.ref_tax_sign,
1331 'price_unit': cur_price_unit,
1332 'tax_code_id': tax.tax_code_id.id,
1333 'ref_tax_code_id': tax.ref_tax_code_id.id,
1336 if tax.type=='percent':
1337 amount = cur_price_unit * tax.amount
1338 data['amount'] = amount
1340 elif tax.type=='fixed':
1341 data['amount'] = tax.amount
1342 elif tax.type=='code':
1343 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1344 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1345 exec tax.python_compute in localdict
1346 amount = localdict['result']
1347 data['amount'] = amount
1348 amount2 = data['amount']
1349 if len(tax.child_ids):
1350 if tax.child_depend:
1353 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner)
1354 res.extend(child_tax)
1355 if tax.include_base_amount:
1356 cur_price_unit+=amount2
1359 def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1362 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1366 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1367 one tax for each tax id in IDS and their childs
1369 res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner)
1371 r['amount'] *= quantity
1374 def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1375 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1379 cur_price_unit=price_unit
1381 tax_parent_tot = 0.0
1383 if (tax.type=='percent') and not tax.include_base_amount:
1384 tax_parent_tot+=tax.amount
1387 if tax.type=='percent':
1388 if tax.include_base_amount:
1389 amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
1391 amount = (cur_price_unit / (1 + tax_parent_tot)) * tax.amount
1393 elif tax.type=='fixed':
1396 elif tax.type=='code':
1397 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1398 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1399 exec tax.python_compute_inv in localdict
1400 amount = localdict['result']
1402 if tax.include_base_amount:
1403 cur_price_unit -= amount
1412 'account_collected_id': tax.account_collected_id.id,
1413 'account_paid_id': tax.account_paid_id.id,
1414 'base_code_id': tax.base_code_id.id,
1415 'ref_base_code_id': tax.ref_base_code_id.id,
1416 'sequence': tax.sequence,
1417 'base_sign': tax.base_sign,
1418 'tax_sign': tax.tax_sign,
1419 'ref_base_sign': tax.ref_base_sign,
1420 'ref_tax_sign': tax.ref_tax_sign,
1421 'price_unit': cur_price_unit,
1422 'tax_code_id': tax.tax_code_id.id,
1423 'ref_tax_code_id': tax.ref_tax_code_id.id,
1425 if len(tax.child_ids):
1426 if tax.child_depend:
1430 parent_tax = self._unit_compute_inv(cr, uid, tax.child_ids, amount, address_id, product, partner)
1431 res.extend(parent_tax)
1436 total += r['amount']
1438 r['price_unit'] -= total
1442 def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1444 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1445 Price Unit is a VAT included price
1449 tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1450 one tax for each tax id in IDS and their childs
1452 res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
1454 r['amount'] *= quantity
1458 # ---------------------------------------------------------
1459 # Account Entries Models
1460 # ---------------------------------------------------------
1462 class account_model(osv.osv):
1463 _name = "account.model"
1464 _description = "Account Model"
1466 'name': fields.char('Model Name', size=64, required=True, help="This is a model for recurring accounting entries"),
1467 'ref': fields.char('Ref', size=64),
1468 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
1469 'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'),
1470 'legend' :fields.text('Legend',readonly=True,size=100),
1474 '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'),
1480 class account_model_line(osv.osv):
1481 _name = "account.model.line"
1482 _description = "Account Model Entries"
1484 'name': fields.char('Name', size=64, required=True),
1485 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the resources from the lowest sequences to the higher ones"),
1486 'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity on entries"),
1487 'debit': fields.float('Debit', digits=(16,2)),
1488 'credit': fields.float('Credit', digits=(16,2)),
1490 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"),
1492 'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True),
1494 'ref': fields.char('Ref.', size=16),
1496 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."),
1497 'currency_id': fields.many2one('res.currency', 'Currency'),
1499 'partner_id': fields.many2one('res.partner', 'Partner Ref.'),
1500 '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."),
1501 'date': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Current Date', required=True, help="The date of the generated entries"),
1504 'date': lambda *a: 'today'
1507 _sql_constraints = [
1508 ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in model !'),
1509 ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model !'),
1511 account_model_line()
1513 # ---------------------------------------------------------
1514 # Account Subscription
1515 # ---------------------------------------------------------
1518 class account_subscription(osv.osv):
1519 _name = "account.subscription"
1520 _description = "Account Subscription"
1522 'name': fields.char('Name', size=64, required=True),
1523 'ref': fields.char('Ref.', size=16),
1524 'model_id': fields.many2one('account.model', 'Model', required=True),
1526 'date_start': fields.date('Starting date', required=True),
1527 'period_total': fields.integer('Number of period', required=True),
1528 'period_nbr': fields.integer('Period', required=True),
1529 'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True),
1530 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status', required=True, readonly=True),
1532 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines')
1535 'date_start': lambda *a: time.strftime('%Y-%m-%d'),
1536 'period_type': lambda *a: 'month',
1537 'period_total': lambda *a: 12,
1538 'period_nbr': lambda *a: 1,
1539 'state': lambda *a: 'draft',
1541 def state_draft(self, cr, uid, ids, context={}):
1542 self.write(cr, uid, ids, {'state':'draft'})
1545 def check(self, cr, uid, ids, context={}):
1547 for sub in self.browse(cr, uid, ids, context):
1549 for line in sub.lines_id:
1550 if not line.move_id.id:
1554 todone.append(sub.id)
1556 self.write(cr, uid, todone, {'state':'done'})
1559 def remove_line(self, cr, uid, ids, context={}):
1561 for sub in self.browse(cr, uid, ids, context):
1562 for line in sub.lines_id:
1563 if not line.move_id.id:
1564 toremove.append(line.id)
1566 self.pool.get('account.subscription.line').unlink(cr, uid, toremove)
1567 self.write(cr, uid, ids, {'state':'draft'})
1570 def compute(self, cr, uid, ids, context={}):
1571 for sub in self.browse(cr, uid, ids, context):
1573 for i in range(sub.period_total):
1574 self.pool.get('account.subscription.line').create(cr, uid, {
1576 'subscription_id': sub.id,
1578 if sub.period_type=='day':
1579 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(days=sub.period_nbr)).strftime('%Y-%m-%d')
1580 if sub.period_type=='month':
1581 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(months=sub.period_nbr)).strftime('%Y-%m-%d')
1582 if sub.period_type=='year':
1583 ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(years=sub.period_nbr)).strftime('%Y-%m-%d')
1584 self.write(cr, uid, ids, {'state':'running'})
1586 account_subscription()
1588 class account_subscription_line(osv.osv):
1589 _name = "account.subscription.line"
1590 _description = "Account Subscription Line"
1592 'subscription_id': fields.many2one('account.subscription', 'Subscription', required=True, select=True),
1593 'date': fields.date('Date', required=True),
1594 'move_id': fields.many2one('account.move', 'Entry'),
1598 def move_create(self, cr, uid, ids, context={}):
1600 for line in self.browse(cr, uid, ids, context):
1604 ids = self.pool.get('account.model').generate(cr, uid, [line.subscription_id.model_id.id], datas, context)
1605 tocheck[line.subscription_id.id] = True
1606 self.write(cr, uid, [line.id], {'move_id':ids[0]})
1608 self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context)
1611 account_subscription_line()
1614 class account_config_wizard(osv.osv_memory):
1615 _name = 'account.config.wizard'
1617 def _get_charts(self, cr, uid, context):
1618 module_obj=self.pool.get('ir.module.module')
1619 ids=module_obj.search(cr, uid, [('category_id', '=', 'Account Charts'), ('state', '<>', 'installed')])
1620 res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)]
1621 res.append((-1, 'None'))
1622 res.sort(key=lambda x: x[1])
1626 'name':fields.char('Name', required=True, size=64, help="Name of the fiscal year as displayed on screens."),
1627 'code':fields.char('Code', required=True, size=64, help="Name of the fiscal year as displayed in reports."),
1628 'date1': fields.date('Starting Date', required=True),
1629 'date2': fields.date('Ending Date', required=True),
1630 'period':fields.selection([('month','Month'),('3months','3 Months')], 'Periods', required=True),
1631 'charts' : fields.selection(_get_charts, 'Charts of Account',required=True)
1634 'code': lambda *a: time.strftime('%Y'),
1635 'name': lambda *a: time.strftime('%Y'),
1636 'date1': lambda *a: time.strftime('%Y-01-01'),
1637 'date2': lambda *a: time.strftime('%Y-12-31'),
1638 'period':lambda *a:'month',
1640 def action_cancel(self,cr,uid,ids,conect=None):
1642 'view_type': 'form',
1643 "view_mode": 'form',
1644 'res_model': 'ir.actions.configuration.wizard',
1645 'type': 'ir.actions.act_window',
1649 def install_account_chart(self, cr, uid, ids, context=None):
1650 for res in self.read(cr,uid,ids):
1651 chart_id = res['charts']
1653 mod_obj = self.pool.get('ir.module.module')
1654 mod_obj.button_install(cr, uid, [chart_id], context=context)
1656 db, pool = pooler.restart_pool(cr.dbname, update_module=True)
1658 def action_create(self, cr, uid,ids, context=None):
1659 for res in self.read(cr,uid,ids):
1660 if 'date1' in res and 'date2' in res:
1661 res_obj = self.pool.get('account.fiscalyear')
1662 start_date=res['date1']
1663 end_date=res['date2']
1664 name=res['name']#DateTime.strptime(start_date, '%Y-%m-%d').strftime('%m.%Y') + '-' + DateTime.strptime(end_date, '%Y-%m-%d').strftime('%m.%Y')
1668 'date_start':start_date,
1669 'date_stop':end_date,
1671 new_id=res_obj.create(cr, uid, vals, context=context)
1672 if res['period']=='month':
1673 res_obj.create_period(cr,uid,[new_id])
1674 elif res['period']=='3months':
1675 res_obj.create_period3(cr,uid,[new_id])
1676 self.install_account_chart(cr,uid,ids)
1678 'view_type': 'form',
1679 "view_mode": 'form',
1680 'res_model': 'ir.actions.configuration.wizard',
1681 'type': 'ir.actions.act_window',
1687 account_config_wizard()
1690 # ---------------------------------------------------------------
1691 # Account Templates : Account, Tax, Tax Code and chart. + Wizard
1692 # ---------------------------------------------------------------
1694 class account_tax_template(osv.osv):
1695 _name = 'account.tax.template'
1696 account_tax_template()
1698 class account_account_template(osv.osv):
1700 _name = "account.account.template"
1701 _description ='Templates for Accounts'
1704 'name': fields.char('Name', size=128, required=True, select=True),
1705 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
1706 'code': fields.char('Code', size=64),
1707 'type': fields.selection([
1708 ('receivable','Receivable'),
1709 ('payable','Payable'),
1711 ('consolidation','Consolidation'),
1713 ('closed','Closed'),
1714 ], 'Internal Type', required=True,),
1715 'user_type': fields.many2one('account.account.type', 'Account Type', required=True),
1716 'reconcile': fields.boolean('Allow Reconciliation', help="Check this option if the user can make a reconciliation of the entries in this account."),
1717 'shortcut': fields.char('Shortcut', size=12),
1718 'note': fields.text('Note'),
1719 'parent_id': fields.many2one('account.account.template','Parent Account Template', ondelete='cascade'),
1720 'child_parent_ids':fields.one2many('account.account.template','parent_id','Children'),
1721 'tax_ids': fields.many2many('account.tax.template', 'account_account_template_tax_rel','account_id','tax_id', 'Default Taxes'),
1725 'reconcile': lambda *a: False,
1726 'type' : lambda *a :'view',
1729 def _check_recursion(self, cr, uid, ids):
1732 cr.execute('select parent_id from account_account_template where id in ('+','.join(map(str,ids))+')')
1733 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1740 (_check_recursion, 'Error ! You can not create recursive account templates.', ['parent_id'])
1744 def name_get(self, cr, uid, ids, context={}):
1747 reads = self.read(cr, uid, ids, ['name','code'], context)
1749 for record in reads:
1750 name = record['name']
1752 name = record['code']+' '+name
1753 res.append((record['id'],name ))
1756 account_account_template()
1758 class account_tax_code_template(osv.osv):
1760 _name = 'account.tax.code.template'
1761 _description = 'Tax Code Template'
1765 'name': fields.char('Tax Case Name', size=64, required=True),
1766 'code': fields.char('Case Code', size=64),
1767 'info': fields.text('Description'),
1768 'parent_id': fields.many2one('account.tax.code.template', 'Parent Code', select=True),
1769 'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Childs Codes'),
1770 'sign': fields.float('Sign for parent', required=True),
1774 'sign': lambda *args: 1.0,
1777 def name_get(self, cr, uid, ids, context=None):
1780 if isinstance(ids, (int, long)):
1782 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1783 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1786 def _check_recursion(self, cr, uid, ids):
1789 cr.execute('select distinct parent_id from account_tax_code_template where id in ('+','.join(map(str,ids))+')')
1790 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1797 (_check_recursion, 'Error ! You can not create recursive Tax Codes.', ['parent_id'])
1799 _order = 'code,name'
1800 account_tax_code_template()
1803 class account_chart_template(osv.osv):
1804 _name="account.chart.template"
1805 _description= "Templates for Account Chart"
1808 'name': fields.char('Name', size=64, required=True),
1809 'account_root_id': fields.many2one('account.account.template','Root Account',required=True,domain=[('parent_id','=',False)]),
1810 'tax_code_root_id': fields.many2one('account.tax.code.template','Root Tax Code',required=True,domain=[('parent_id','=',False)]),
1811 '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'),
1812 'bank_account_view_id': fields.many2one('account.account.template','Bank Account',required=True),
1813 'property_account_receivable': fields.many2one('account.account.template','Receivable Account'),
1814 'property_account_payable': fields.many2one('account.account.template','Payable Account'),
1815 'property_account_expense_categ': fields.many2one('account.account.template','Expense Category Account'),
1816 'property_account_income_categ': fields.many2one('account.account.template','Income Category Account'),
1817 'property_account_expense': fields.many2one('account.account.template','Expense Account on Product Template'),
1818 'property_account_income': fields.many2one('account.account.template','Income Account on Product Template'),
1821 account_chart_template()
1823 class account_tax_template(osv.osv):
1825 _name = 'account.tax.template'
1826 _description = 'Templates for Taxes'
1829 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
1830 'name': fields.char('Tax Name', size=64, required=True),
1831 '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."),
1832 'amount': fields.float('Amount', required=True, digits=(14,4)),
1833 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1834 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1835 '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."),
1836 'account_collected_id':fields.many2one('account.account.template', 'Invoice Tax Account'),
1837 'account_paid_id':fields.many2one('account.account.template', 'Refund Tax Account'),
1838 'parent_id':fields.many2one('account.tax.template', 'Parent Tax Account', select=True),
1839 '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."),
1840 'python_compute':fields.text('Python Code'),
1841 'python_compute_inv':fields.text('Python Code (reverse)'),
1842 'python_applicable':fields.text('Python Code'),
1843 '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."),
1846 # Fields used for the VAT declaration
1848 'base_code_id': fields.many2one('account.tax.code.template', 'Base Code', help="Use this code for the VAT declaration."),
1849 'tax_code_id': fields.many2one('account.tax.code.template', 'Tax Code', help="Use this code for the VAT declaration."),
1850 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1851 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1853 # Same fields for refund invoices
1855 'ref_base_code_id': fields.many2one('account.tax.code.template', 'Refund Base Code', help="Use this code for the VAT declaration."),
1856 'ref_tax_code_id': fields.many2one('account.tax.code.template', 'Refund Tax Code', help="Use this code for the VAT declaration."),
1857 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."),
1858 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."),
1859 '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."),
1860 'description': fields.char('Internal Name', size=32),
1863 def name_get(self, cr, uid, ids, context={}):
1867 for record in self.read(cr, uid, ids, ['description','name'], context):
1868 name = record['description'] and record['description'] or record['name']
1869 res.append((record['id'],name ))
1872 def _default_company(self, cr, uid, context={}):
1873 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1875 return user.company_id.id
1876 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1879 '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''',
1880 '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''',
1881 'applicable_type': lambda *a: 'true',
1882 'type': lambda *a: 'percent',
1883 'amount': lambda *a: 0,
1884 'sequence': lambda *a: 1,
1885 'tax_group': lambda *a: 'vat',
1886 'ref_tax_sign': lambda *a: 1,
1887 'ref_base_sign': lambda *a: 1,
1888 'tax_sign': lambda *a: 1,
1889 'base_sign': lambda *a: 1,
1890 'include_base_amount': lambda *a: False,
1895 account_tax_template()
1897 # Fiscal Position Templates
1899 class account_fiscal_position_template(osv.osv):
1900 _name = 'account.fiscal.position.template'
1901 _description = 'Template for Fiscal Position'
1904 'name': fields.char('Fiscal Position Template', size=64, translate=True, required=True),
1905 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
1906 'account_ids': fields.one2many('account.fiscal.position.account.template', 'position_id', 'Accounts Mapping'),
1907 'tax_ids': fields.one2many('account.fiscal.position.tax.template', 'position_id', 'Taxes Mapping')
1910 account_fiscal_position_template()
1912 class account_fiscal_position_tax_template(osv.osv):
1913 _name = 'account.fiscal.position.tax.template'
1914 _description = 'Fiscal Position Template Taxes Mapping'
1915 _rec_name = 'position_id'
1918 'position_id': fields.many2one('account.fiscal.position.template', 'Fiscal Position', required=True, ondelete='cascade'),
1919 'tax_src_id': fields.many2one('account.tax.template', 'Tax Source', required=True),
1920 'tax_dest_id': fields.many2one('account.tax.template', 'Replacement Tax')
1923 account_fiscal_position_tax_template()
1925 class account_fiscal_position_account_template(osv.osv):
1926 _name = 'account.fiscal.position.account.template'
1927 _description = 'Fiscal Position Template Accounts Mapping'
1928 _rec_name = 'position_id'
1930 'position_id': fields.many2one('account.fiscal.position.template', 'Fiscal Position', required=True, ondelete='cascade'),
1931 'account_src_id': fields.many2one('account.account.template', 'Account Source', domain=[('type','<>','view')], required=True),
1932 'account_dest_id': fields.many2one('account.account.template', 'Account Destination', domain=[('type','<>','view')], required=True)
1935 account_fiscal_position_account_template()
1937 # Multi charts of Accounts wizard
1939 class wizard_multi_charts_accounts(osv.osv_memory):
1941 Create a new account chart for a company.
1944 * an account chart template
1945 * a number of digits for formatting code of non-view accounts
1946 * a list of bank account owned by the company
1948 * generates all accounts from the template and assign them to the right company
1949 * generates all taxes and tax codes, changing account assignations
1950 * generates all accounting properties and assign correctly
1952 _name='wizard.multi.charts.accounts'
1955 'company_id':fields.many2one('res.company','Company',required=True),
1956 'chart_template_id': fields.many2one('account.chart.template','Chart Template',required=True),
1957 'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Bank Accounts',required=True),
1958 'code_digits':fields.integer('# of Digits',required=True,help="No. of Digits to use for account code"),
1961 def _get_chart(self, cr, uid, context={}):
1962 ids = self.pool.get('account.chart.template').search(cr, uid, [], context=context)
1967 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr,uid,[uid],c)[0].company_id.id,
1968 'chart_template_id': _get_chart,
1969 'code_digits': lambda *a:6,
1972 def action_create(self, cr, uid, ids, context=None):
1973 obj_multi = self.browse(cr,uid,ids[0])
1974 obj_acc = self.pool.get('account.account')
1975 obj_acc_tax = self.pool.get('account.tax')
1976 obj_journal = self.pool.get('account.journal')
1977 obj_acc_template = self.pool.get('account.account.template')
1978 obj_fiscal_position_template = self.pool.get('account.fiscal.position.template')
1979 obj_fiscal_position = self.pool.get('account.fiscal.position')
1982 obj_acc_root = obj_multi.chart_template_id.account_root_id
1983 tax_code_root_id = obj_multi.chart_template_id.tax_code_root_id.id
1984 company_id = obj_multi.company_id.id
1987 acc_template_ref = {}
1988 tax_template_ref = {}
1989 tax_code_template_ref = {}
1992 #create all the tax code
1993 children_tax_code_template = self.pool.get('account.tax.code.template').search(cr, uid, [('parent_id','child_of',[tax_code_root_id])], order='id')
1994 for tax_code_template in self.pool.get('account.tax.code.template').browse(cr, uid, children_tax_code_template):
1996 'name': (tax_code_root_id == tax_code_template.id) and obj_multi.company_id.name or tax_code_template.name,
1997 'code': tax_code_template.code,
1998 'info': tax_code_template.info,
1999 '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,
2000 'company_id': company_id,
2001 'sign': tax_code_template.sign,
2003 new_tax_code = self.pool.get('account.tax.code').create(cr,uid,vals)
2004 #recording the new tax code to do the mapping
2005 tax_code_template_ref[tax_code_template.id] = new_tax_code
2008 for tax in obj_multi.chart_template_id.tax_template_ids:
2012 'sequence': tax.sequence,
2013 'amount':tax.amount,
2015 'applicable_type': tax.applicable_type,
2016 'domain':tax.domain,
2017 'parent_id': tax.parent_id and ((tax.parent_id.id in tax_template_ref) and tax_template_ref[tax.parent_id.id]) or False,
2018 'child_depend': tax.child_depend,
2019 'python_compute': tax.python_compute,
2020 'python_compute_inv': tax.python_compute_inv,
2021 'python_applicable': tax.python_applicable,
2022 'tax_group':tax.tax_group,
2023 '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,
2024 '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,
2025 'base_sign': tax.base_sign,
2026 'tax_sign': tax.tax_sign,
2027 '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,
2028 '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,
2029 'ref_base_sign': tax.ref_base_sign,
2030 'ref_tax_sign': tax.ref_tax_sign,
2031 'include_base_amount': tax.include_base_amount,
2032 'description':tax.description,
2033 'company_id': company_id,
2035 new_tax = obj_acc_tax.create(cr,uid,vals_tax)
2036 #as the accounts have not been created yet, we have to wait before filling these fields
2037 todo_dict[new_tax] = {
2038 'account_collected_id': tax.account_collected_id and tax.account_collected_id.id or False,
2039 'account_paid_id': tax.account_paid_id and tax.account_paid_id.id or False,
2041 tax_template_ref[tax.id] = new_tax
2043 #deactivate the parent_store functionnality on account_account for rapidity purpose
2044 self.pool._init = True
2046 children_acc_template = obj_acc_template.search(cr, uid, [('parent_id','child_of',[obj_acc_root.id])])
2047 children_acc_template.sort()
2048 for account_template in obj_acc_template.browse(cr, uid, children_acc_template):
2050 for tax in account_template.tax_ids:
2051 tax_ids.append(tax_template_ref[tax.id])
2052 #create the account_account
2054 dig = obj_multi.code_digits
2055 code_main = account_template.code and len(account_template.code) or 0
2056 code_acc = account_template.code or ''
2057 if code_main>0 and code_main<=dig and account_template.type != 'view':
2058 code_acc=str(code_acc) + (str('0'*(dig-code_main)))
2060 'name': (obj_acc_root.id == account_template.id) and obj_multi.company_id.name or account_template.name,
2061 #'sign': account_template.sign,
2062 'currency_id': account_template.currency_id and account_template.currency_id.id or False,
2064 'type': account_template.type,
2065 'user_type': account_template.user_type and account_template.user_type.id or False,
2066 'reconcile': account_template.reconcile,
2067 'shortcut': account_template.shortcut,
2068 'note': account_template.note,
2069 '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,
2070 'tax_ids': [(6,0,tax_ids)],
2071 'company_id': company_id,
2073 new_account = obj_acc.create(cr,uid,vals)
2074 acc_template_ref[account_template.id] = new_account
2075 #reactivate the parent_store functionnality on account_account
2076 self.pool._init = False
2077 self.pool.get('account.account')._parent_store_compute(cr)
2079 for key,value in todo_dict.items():
2080 if value['account_collected_id'] or value['account_paid_id']:
2081 obj_acc_tax.write(cr, uid, [key], vals={
2082 'account_collected_id': acc_template_ref[value['account_collected_id']],
2083 'account_paid_id': acc_template_ref[value['account_paid_id']],
2088 view_id = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Journal View')])[0]
2089 seq_id = self.pool.get('ir.sequence').search(cr,uid,[('code','=','account.journal')])[0]
2090 seq_code = self.pool.get('ir.sequence').get(cr, uid, 'account.journal')
2092 vals_journal['view_id']=view_id
2093 vals_journal['sequence_id']=seq_id
2096 vals_journal['name'] = _('Sales Journal')
2097 vals_journal['type'] = 'sale'
2098 vals_journal['code'] = _('SAJ')
2100 if obj_multi.chart_template_id.property_account_receivable:
2101 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_income_categ.id]
2102 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_income_categ.id]
2104 obj_journal.create(cr,uid,vals_journal)
2107 vals_journal['name']=_('Purchase Journal')
2108 vals_journal['type']='purchase'
2109 vals_journal['code']=_('EXJ')
2111 if obj_multi.chart_template_id.property_account_payable:
2112 vals_journal['default_credit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_expense_categ.id]
2113 vals_journal['default_debit_account_id'] = acc_template_ref[obj_multi.chart_template_id.property_account_expense_categ.id]
2115 obj_journal.create(cr,uid,vals_journal)
2118 view_id_cash = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Cash Journal View')])[0]
2119 view_id_cur = self.pool.get('account.journal.view').search(cr,uid,[('name','=','Multi-Currency Cash Journal View')])[0]
2120 ref_acc_bank = obj_multi.chart_template_id.bank_account_view_id
2123 for line in obj_multi.bank_accounts_id:
2124 #create the account_account for this bank journal
2125 tmp = self.pool.get('res.partner.bank').name_get(cr, uid, [line.acc_no.id])[0][1]
2126 dig = obj_multi.code_digits
2128 'name': line.acc_no.bank and line.acc_no.bank.name+' '+tmp or tmp,
2129 'currency_id': line.currency_id and line.currency_id.id or False,
2130 'code': str(ref_acc_bank.code.ljust(dig,'0') + str(current_num)),
2132 'user_type': account_template.user_type and account_template.user_type.id or False,
2134 'parent_id': acc_template_ref[ref_acc_bank.id] or False,
2135 'company_id': company_id,
2137 acc_cash_id = obj_acc.create(cr,uid,vals)
2139 #create the bank journal
2140 vals_journal['name']= vals['name']
2141 vals_journal['code']= _('BNK') + str(current_num)
2142 vals_journal['sequence_id'] = seq_id
2143 vals_journal['type'] = 'cash'
2144 if line.currency_id:
2145 vals_journal['view_id'] = view_id_cur
2146 vals_journal['currency'] = line.currency_id.id
2148 vals_journal['view_id'] = view_id_cash
2149 vals_journal['default_credit_account_id'] = acc_cash_id
2150 vals_journal['default_debit_account_id']= acc_cash_id
2151 obj_journal.create(cr,uid,vals_journal)
2155 #create the properties
2156 property_obj = self.pool.get('ir.property')
2157 fields_obj = self.pool.get('ir.model.fields')
2160 ('property_account_receivable','res.partner','account.account'),
2161 ('property_account_payable','res.partner','account.account'),
2162 ('property_account_expense_categ','product.category','account.account'),
2163 ('property_account_income_categ','product.category','account.account'),
2164 ('property_account_expense','product.template','account.account'),
2165 ('property_account_income','product.template','account.account')
2167 for record in todo_list:
2169 r = property_obj.search(cr, uid, [('name','=', record[0] ),('company_id','=',company_id)])
2170 account = getattr(obj_multi.chart_template_id, record[0])
2171 field = fields_obj.search(cr, uid, [('name','=',record[0]),('model','=',record[1]),('relation','=',record[2])])
2174 'company_id': company_id,
2175 'fields_id': field[0],
2176 'value': account and 'account.account,'+str(acc_template_ref[account.id]) or False,
2179 #the property exist: modify it
2180 property_obj.write(cr, uid, r, vals)
2182 #create the property
2183 property_obj.create(cr, uid, vals)
2185 fp_ids = obj_fiscal_position_template.search(cr, uid,[('chart_template_id', '=', obj_multi.chart_template_id.id)])
2188 for position in obj_fiscal_position_template.browse(cr, uid, fp_ids):
2191 'company_id' : company_id,
2192 'name' : position.name,
2194 new_fp = obj_fiscal_position.create(cr, uid, vals_fp)
2196 obj_tax_fp = self.pool.get('account.fiscal.position.tax')
2197 obj_ac_fp = self.pool.get('account.fiscal.position.account')
2199 for tax in position.tax_ids:
2201 'tax_src_id' : tax_template_ref[tax.tax_src_id.id],
2202 'tax_dest_id' : tax_template_ref[tax.tax_dest_id.id],
2203 'position_id' : new_fp,
2205 obj_tax_fp.create(cr, uid, vals_tax)
2207 for acc in position.account_ids:
2209 'account_src_id' : acc_template_ref[acc.account_src_id.id],
2210 'account_dest_id' : acc_template_ref[acc.account_dest_id.id],
2211 'position_id' : new_fp,
2213 obj_ac_fp.create(cr, uid, vals_acc)
2216 'view_type': 'form',
2217 "view_mode": 'form',
2218 'res_model': 'ir.actions.configuration.wizard',
2219 'type': 'ir.actions.act_window',
2222 def action_cancel(self,cr,uid,ids,conect=None):
2224 'view_type': 'form',
2225 "view_mode": 'form',
2226 'res_model': 'ir.actions.configuration.wizard',
2227 'type': 'ir.actions.act_window',
2232 wizard_multi_charts_accounts()
2234 class account_bank_accounts_wizard(osv.osv_memory):
2235 _name='account.bank.accounts.wizard'
2238 'acc_no':fields.many2one('res.partner.bank','Account No.',required=True),
2239 'bank_account_id':fields.many2one('wizard.multi.charts.accounts', 'Bank Account', required=True),
2240 'currency_id':fields.many2one('res.currency', 'Currency'),
2243 account_bank_accounts_wizard()
2245 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: