75605c1ab146d3e987444cb470f5f5cf92591b60
[odoo/odoo.git] / addons / account / account.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
5 #
6 # $Id$
7 #
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
13 # Service Company
14 #
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
19 #
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 # GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28 #
29 ##############################################################################
30 import time
31 import netsvc
32 from osv import fields, osv
33
34 from tools.misc import currency
35 from tools.translate import _
36
37 import mx.DateTime
38 from mx.DateTime import RelativeDateTime, now, DateTime, localtime
39
40
41 class account_payment_term(osv.osv):
42         _name = "account.payment.term"
43         _description = "Payment Term"
44         _columns = {
45                 'name': fields.char('Payment Term', size=32, translate=True),
46                 'active': fields.boolean('Active'),
47                 'note': fields.text('Description', translate=True),
48                 'line_ids': fields.one2many('account.payment.term.line', 'payment_id', 'Terms'),
49         }
50         _defaults = {
51                 'active': lambda *a: 1,
52         }
53         _order = "name"
54         def compute(self, cr, uid, id, value, date_ref=False, context={}):
55                 if not date_ref:
56                         date_ref = now().strftime('%Y-%m-%d')
57                 pt = self.browse(cr, uid, id, context)
58                 amount = value
59                 result = []
60                 for line in pt.line_ids:
61                         if line.value=='fixed':
62                                 amt = round(line.value_amount, 2)
63                         elif line.value=='procent':
64                                 amt = round(value * line.value_amount, 2)
65                         elif line.value=='balance':
66                                 amt = round(amount, 2)
67                         if amt:
68                                 next_date = mx.DateTime.strptime(date_ref, '%Y-%m-%d') + RelativeDateTime(days=line.days)
69                                 if line.condition == 'end of month':
70                                         next_date += RelativeDateTime(day=-1)
71                                 result.append( (next_date.strftime('%Y-%m-%d'), amt) )
72                                 amount -= amt
73                 return result
74
75 account_payment_term()
76
77 class account_payment_term_line(osv.osv):
78         _name = "account.payment.term.line"
79         _description = "Payment Term Line"
80         _columns = {
81                 'name': fields.char('Line Name', size=32,required=True),
82                 '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"),
83                 'value': fields.selection([('procent','Percent'),('balance','Balance'),('fixed','Fixed Amount')], 'Value',required=True),
84                 'value_amount': fields.float('Value Amount'),
85                 'days': fields.integer('Number of Days',required=True),
86                 'condition': fields.selection([('net days','Net Days'),('end of month','End of Month')], 'Condition', required=True, help="The payment delay condition id a number of days expressed in 2 ways: net days or end of the month. The 'net days' condition implies that the paiment arrive after 'Number of Days' calendar days. The 'end of the month' condition requires that the paiement arrives before the end of the month that is that is after 'Number of Days' calendar days."),
87                 'payment_id': fields.many2one('account.payment.term','Payment Term', required=True, select=True),
88         }
89         _defaults = {
90                 'value': lambda *a: 'balance',
91                 'sequence': lambda *a: 5,
92                 'condition': lambda *a: 'net days',
93         }
94         _order = "sequence"
95 account_payment_term_line()
96
97
98 class account_account_type(osv.osv):
99         _name = "account.account.type"
100         _description = "Account Type"
101         _columns = {
102                 'name': fields.char('Acc. Type Name', size=64, required=True, translate=True),
103                 'code': fields.char('Code', size=32, required=True),
104                 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of account types."),
105                 'code_from': fields.char('Code From', size=10, help="Gives the range of account code available for this type of account. These fields are given for information and are not used in any constraint."),
106                 'code_to': fields.char('Code To', size=10, help="Gives the range of account code available for this type of account. These fields are just given for information and are not used in any constraint."),
107                 'partner_account': fields.boolean('Partner account'),
108                 'close_method': fields.selection([('none','None'), ('balance','Balance'), ('detail','Detail'),('unreconciled','Unreconciled')], 'Deferral Method', required=True),
109         }
110         _defaults = {
111                 'close_method': lambda *a: 'none',
112                 'sequence': lambda *a: 5,
113         }
114         _order = "sequence"
115 account_account_type()
116
117 def _code_get(self, cr, uid, context={}):
118         acc_type_obj = self.pool.get('account.account.type')
119         ids = acc_type_obj.search(cr, uid, [])
120         res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context)
121         return [(r['code'], r['name']) for r in res]
122
123 #----------------------------------------------------------
124 # Accounts
125 #----------------------------------------------------------
126
127 class account_tax(osv.osv):
128         _name = 'account.tax'
129 account_tax()
130
131 class account_account(osv.osv):
132         _order = "code"
133         _name = "account.account"
134         _description = "Account"
135
136         def search(self, cr, uid, args, offset=0, limit=None, order=None,
137                         context=None, count=False):
138                 if context is None:
139                         context = {}
140                 pos = 0
141                 while pos<len(args):
142                         if args[pos][0]=='journal_id':
143                                 if not args[pos][2]:
144                                         del args[pos]
145                                         continue
146                                 jour = self.pool.get('account.journal').browse(cr, uid, args[pos][2])
147                                 if (not (jour.account_control_ids or jour.type_control_ids)) or not args[pos][2]:
148                                         del args[pos]
149                                         continue
150                                 ids3 = map(lambda x: x.code, jour.type_control_ids)
151                                 ids1 = super(account_account,self).search(cr, uid, [('type','in',ids3)])
152                                 ids1 += map(lambda x: x.id, jour.account_control_ids)
153                                 args[pos] = ('id','in',ids1)
154                         pos+=1
155                 return super(account_account,self).search(cr, uid, args, offset, limit,
156                                 order, context=context, count=count)
157
158         def _credit(self, cr, uid, ids, field_name, arg, context={}):
159                 acc_set = ",".join(map(str, ids))
160                 query = self.pool.get('account.move.line')._query_get(cr, uid, context=context)
161                 cr.execute(("SELECT a.id, " \
162                                         "SUM(COALESCE(l.credit * a.sign, 0)) " \
163                                 "FROM account_account a " \
164                                         "LEFT JOIN account_move_line l " \
165                                         "ON (a.id = l.account_id) " \
166                                 "WHERE a.type != 'view' " \
167                                         "AND a.id IN (%s) " \
168                                         "AND " + query + " " \
169                                         "AND a.active " \
170                                 "GROUP BY a.id") % (acc_set, ))
171                 res2 = cr.fetchall()
172                 res = {}
173                 for id in ids:
174                         res[id] = 0.0
175                 for account_id, sum in res2:
176                         res[account_id] += sum
177                 return res
178
179         def _debit(self, cr, uid, ids, field_name, arg, context={}):
180                 acc_set = ",".join(map(str, ids))
181                 query = self.pool.get('account.move.line')._query_get(cr, uid, context=context)
182                 cr.execute(("SELECT a.id, " \
183                                         "SUM(COALESCE(l.debit * a.sign, 0)) " \
184                                 "FROM account_account a " \
185                                         "LEFT JOIN account_move_line l " \
186                                         "ON (a.id = l.account_id) " \
187                                 "WHERE a.type != 'view' " \
188                                         "AND a.id IN (%s) " \
189                                         "AND " + query + " " \
190                                         "AND a.active " \
191                                 "GROUP BY a.id") % (acc_set, ))
192                 res2 = cr.fetchall()
193                 res = {}
194                 for id in ids:
195                         res[id] = 0.0
196                 for account_id, sum in res2:
197                         res[account_id] += sum
198                 return res
199
200         def _balance(self, cr, uid, ids, field_name, arg, context={}):
201                 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
202                 ids2 = {}.fromkeys(ids + ids2).keys()
203                 acc_set = ",".join(map(str, ids2))
204                 query = self.pool.get('account.move.line')._query_get(cr, uid,
205                                 context=context)
206                 cr.execute(("SELECT a.id, " \
207                                         "SUM((COALESCE(l.debit, 0) - COALESCE(l.credit, 0))) " \
208                                 "FROM account_account a " \
209                                         "LEFT JOIN account_move_line l " \
210                                         "ON (a.id=l.account_id) " \
211                                 "WHERE a.type != 'view' " \
212                                         "AND a.id IN (%s) " \
213                                         "AND " + query + " " \
214                                         "AND a.active " \
215                                 "GROUP BY a.id") % (acc_set, ))
216                 res = {}
217                 for account_id, sum in cr.fetchall():
218                         res[account_id] = round(sum,2)
219                 cr.execute("SELECT a.id, a.company_id " \
220                                 "FROM account_account a " \
221                                 "WHERE id IN (%s)" % acc_set)
222                 resc = dict(cr.fetchall())
223                 cr.execute("SELECT id, currency_id FROM res_company")
224                 rescur = dict(cr.fetchall())
225
226                 for id in ids:
227                         ids3 = self.search(cr, uid, [('parent_id', 'child_of', [id])])
228                         to_currency_id = rescur[resc[id]]
229                         for idx in ids3:
230                                 if idx <> id:
231                                         res.setdefault(id, 0.0)
232                                         if resc[idx]<>resc[id] and resc[idx] and resc[id]:
233                                                 from_currency_id = rescur[resc[idx]]
234                                                 res[id] += self.pool.get('res.currency').compute(cr,
235                                                                 uid, from_currency_id, to_currency_id,
236                                                                 res.get(idx, 0.0), context=context)
237                                         else:
238                                                 res[id] += res.get(idx, 0.0)
239                 for id in ids:
240                         res[id] = round(res.get(id,0.0), 2)
241                 return res
242
243         def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}):
244                 result = {}
245                 for rec in self.browse(cr, uid, ids, context):
246                         result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code)
247                 return result
248
249         _columns = {
250                 'name': fields.char('Name', size=128, required=True, select=True),
251                 'sign': fields.selection([(-1, 'Negative'), (1, 'Positive')], 'Sign', required=True, help='Allows to change the displayed amount of the balance to see positive results instead of negative ones in expenses accounts'),
252                 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."),
253                 'code': fields.char('Code', size=64),
254                 'type': fields.selection(_code_get, 'Account Type', required=True),
255                 'parent_id': fields.many2many('account.account', 'account_account_rel', 'child_id', 'parent_id', 'Parents'),
256                 'child_id': fields.many2many('account.account', 'account_account_rel', 'parent_id', 'child_id', 'Children'),
257                 'balance': fields.function(_balance, digits=(16,2), method=True, string='Balance'),
258                 'credit': fields.function(_credit, digits=(16,2), method=True, string='Credit'),
259                 'debit': fields.function(_debit, digits=(16,2), method=True, string='Debit'),
260                 'reconcile': fields.boolean('Reconcile', help="Check this account if the user can make a reconciliation of the entries in this account."),
261                 'shortcut': fields.char('Shortcut', size=12),
262                 'close_method': fields.selection([('none','None'), ('balance','Balance'), ('detail','Detail'),('unreconciled','Unreconciled')], 'Deferral Method', required=True, help="Tell Tiny ERP how to process the entries of this account when you close a fiscal year. None removes all entries to start with an empty account for the new fiscal year. Balance creates only one entry to keep the balance for the new fiscal year. Detail keeps the detail of all entries of the preceeding years. Unreconciled keeps the detail of unreconciled entries only."),
263                 'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel',
264                         'account_id','tax_id', 'Default Taxes'),
265                 'note': fields.text('Note'),
266                 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Company Currency'),
267                 'company_id': fields.many2one('res.company', 'Company', required=True),
268                 'active': fields.boolean('Active', select=2),
269         }
270
271         def _default_company(self, cr, uid, context={}):
272                 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
273                 if user.company_id:
274                         return user.company_id.id
275                 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
276
277         _defaults = {
278                 'sign': lambda *a: 1,
279                 'type': lambda *a: 'view',
280                 'reconcile': lambda *a: False,
281                 'close_method': lambda *a: 'balance',
282                 'company_id': _default_company,
283                 'active': lambda *a: True,
284         }
285
286         def _check_recursion(self, cr, uid, ids):
287                 level = 100
288                 while len(ids):
289                         cr.execute('select distinct parent_id from account_account_rel where child_id in ('+','.join(map(str,ids))+')')
290                         ids = filter(None, map(lambda x:x[0], cr.fetchall()))
291                         if not level:
292                                 return False
293                         level -= 1
294                 return True
295
296         _constraints = [
297                 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
298         ]
299         def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
300                 if not args:
301                         args=[]
302                 if not context:
303                         context = {}
304                 args = args[:]
305                 ids = []
306                 try:
307                         if name and str(name).startswith('partner:'):
308                                 part_id = int(name.split(':')[1])
309                                 part = self.pool.get('res.partner').browse(cr, user, part_id, context)
310                                 args += [('id','in', (part.property_account_payable.id, part.property_account_receivable.id))]
311                                 name = False
312                         if name and str(name).startswith('type:'):
313                                 type = name.split(':')[1]
314                                 args += [('type','=', type)]
315                                 name = False
316                 except:
317                         pass
318                 if name:
319                         ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit)
320                         if not ids:
321                                 ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit)
322                         if not ids:
323                                 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
324                 else:
325                         ids = self.search(cr, user, args, context=context, limit=limit)
326                 return self.name_get(cr, user, ids, context=context)
327
328         def name_get(self, cr, uid, ids, context={}):
329                 if not len(ids):
330                         return []
331                 reads = self.read(cr, uid, ids, ['name','code'], context)
332                 res = []
333                 for record in reads:
334                         name = record['name']
335                         if record['code']:
336                                 name = record['code']+' - '+name
337                         res.append((record['id'],name ))
338                 return res
339
340         def copy(self, cr, uid, id, default=None, context={}):
341                 account = self.browse(cr, uid, id, context=context)
342                 new_child_ids = []
343                 default['parent_id'] = False
344                 if account:
345                         for child in account.child_id:
346                                 new_child_ids.append(self.copy(cr, uid, child.id, default, context=context))
347                         default['child_id'] = [(6, 0, new_child_ids)]
348                 else:
349                         default['child_id'] = False
350                 return super(account_account, self).copy(cr, uid, id, default, context=context)
351
352         def write(self, cr, uid, ids, vals, context=None):
353                 if not context:
354                         context={}
355                 if 'active' in vals and not vals['active']:
356                         line_obj = self.pool.get('account.move.line')
357                         account_ids = self.search(cr, uid, [('parent_id', 'child_of', ids)])
358                         if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]):
359                                 vals=vals.copy()
360                                 del vals['active']
361                 return super(account_account, self).write(cr, uid, ids, vals, context=context)
362 account_account()
363
364 class account_journal_view(osv.osv):
365         _name = "account.journal.view"
366         _description = "Journal View"
367         _columns = {
368                 'name': fields.char('Journal View', size=64, required=True),
369                 'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
370         }
371         _order = "name"
372 account_journal_view()
373
374
375 class account_journal_column(osv.osv):
376         def _col_get(self, cr, user, context={}):
377                 result = []
378                 cols = self.pool.get('account.move.line')._columns
379                 for col in cols:
380                         result.append( (col, cols[col].string) )
381                 result.sort()
382                 return result
383         _name = "account.journal.column"
384         _description = "Journal Column"
385         _columns = {
386                 'name': fields.char('Column Name', size=64, required=True),
387                 'field': fields.selection(_col_get, 'Field Name', method=True, required=True, size=32),
388                 'view_id': fields.many2one('account.journal.view', 'Journal View', select=True),
389                 'sequence': fields.integer('Sequence'),
390                 'required': fields.boolean('Required'),
391                 'readonly': fields.boolean('Readonly'),
392         }
393         _order = "sequence"
394 account_journal_column()
395
396 class account_journal(osv.osv):
397         _name = "account.journal"
398         _description = "Journal"
399         _columns = {
400                 'name': fields.char('Journal Name', size=64, required=True, translate=True),
401                 'code': fields.char('Code', size=16),
402                 'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True),
403
404                 'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]),
405                 'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]),
406
407                 'active': fields.boolean('Active'),
408                 'view_id': fields.many2one('account.journal.view', 'View', required=True, help="Gives the view used when writing or browsing entries in this journal. The view tell Tiny ERP which fields should be visible, required or readonly and in which order. You can create your own view for a faster encoding in each journal."),
409                 'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account'),
410                 'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account'),
411                 '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."),
412                 'update_posted': fields.boolean('Allow Cancelling Entries'),
413                 'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="The sequence gives the display order for a list of journals", required=True),
414                 'user_id': fields.many2one('res.users', 'User', help="The responsible user of this journal"),
415                 'groups_id': fields.many2many('res.groups', 'account_journal_group_rel', 'journal_id', 'group_id', 'Groups'),
416                 'currency': fields.many2one('res.currency', 'Currency', help='The currency used to enter statement'),
417         }
418         _defaults = {
419                 'active': lambda *a: 1,
420                 'user_id': lambda self,cr,uid,context: uid,
421         }
422         def create(self, cr, uid, vals, context={}):
423                 journal_id = super(osv.osv, self).create(cr, uid, vals, context)
424 #               journal_name = self.browse(cr, uid, [journal_id])[0].code
425 #               periods = self.pool.get('account.period')
426 #               ids = periods.search(cr, uid, [('date_stop','>=',time.strftime('%Y-%m-%d'))])
427 #               for period in periods.browse(cr, uid, ids):
428 #                       self.pool.get('account.journal.period').create(cr, uid, {
429 #                               'name': (journal_name or '')+':'+(period.code or ''),
430 #                               'journal_id': journal_id,
431 #                               'period_id': period.id
432 #                       })
433                 return journal_id
434         def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
435                 if not args:
436                         args=[]
437                 if not context:
438                         context={}
439                 ids = []
440                 if name:
441                         ids = self.search(cr, user, [('code','ilike',name)]+ args, limit=limit)
442                 if not ids:
443                         ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit)
444                 return self.name_get(cr, user, ids, context=context)
445 account_journal()
446
447 class account_fiscalyear(osv.osv):
448         _name = "account.fiscalyear"
449         _description = "Fiscal Year"
450         _columns = {
451                 'name': fields.char('Fiscal Year', size=64, required=True),
452                 'code': fields.char('Code', size=6, required=True),
453                 'date_start': fields.date('Start date', required=True),
454                 'date_stop': fields.date('End date', required=True),
455                 'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'),
456                 'state': fields.selection([('draft','Draft'), ('done','Done')], 'State', redonly=True),
457         }
458
459         _defaults = {
460                 'state': lambda *a: 'draft',
461         }
462         _order = "date_start"
463         def create_period3(self,cr, uid, ids, context={}):
464                 return self.create_period(cr, uid, ids, context, 3)
465
466         def create_period(self,cr, uid, ids, context={}, interval=1):
467                 for fy in self.browse(cr, uid, ids, context):
468                         dt = fy.date_start
469                         ds = mx.DateTime.strptime(fy.date_start, '%Y-%m-%d')
470                         while ds.strftime('%Y-%m-%d')<fy.date_stop:
471                                 de = ds + RelativeDateTime(months=interval, days=-1)
472                                 self.pool.get('account.period').create(cr, uid, {
473                                         'name': ds.strftime('%d/%m') + ' - '+de.strftime('%d/%m'),
474                                         'code': ds.strftime('%d/%m') + '-'+de.strftime('%d/%m'),
475                                         'date_start': ds.strftime('%Y-%m-%d'),
476                                         'date_stop': de.strftime('%Y-%m-%d'),
477                                         'fiscalyear_id': fy.id,
478                                 })
479                                 ds = ds + RelativeDateTime(months=interval)
480                 return True
481
482         def find(self, cr, uid, dt=None, exception=True, context={}):
483                 if not dt:
484                         dt = time.strftime('%Y-%m-%d')
485                 ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)])
486                 if not ids:
487                         if exception:
488                                 raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.'))
489                         else:
490                                 return False
491                 return ids[0]
492 account_fiscalyear()
493
494 class account_period(osv.osv):
495         _name = "account.period"
496         _description = "Account period"
497         _columns = {
498                 'name': fields.char('Period Name', size=64, required=True),
499                 'code': fields.char('Code', size=12),
500                 'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}),
501                 'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}),
502                 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True),
503                 'state': fields.selection([('draft','Draft'), ('done','Done')], 'State', readonly=True)
504         }
505         _defaults = {
506                 'state': lambda *a: 'draft',
507         }
508         _order = "date_start"
509         def next(self, cr, uid, period, step, context={}):
510                 ids = self.search(cr, uid, [('date_start','>',period.date_start)])
511                 if len(ids)>=step:
512                         return ids[step-1]
513                 return False
514
515         def find(self, cr, uid, dt=None, context={}):
516                 if not dt:
517                         dt = time.strftime('%Y-%m-%d')
518 #CHECKME: shouldn't we check the state of the period?
519                 ids = self.search(cr, uid, [('date_start','<=',dt),('date_stop','>=',dt)])
520                 if not ids:
521                         raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.'))
522                 return ids
523 account_period()
524
525 class account_journal_period(osv.osv):
526         _name = "account.journal.period"
527         _description = "Journal - Period"
528
529         def _icon_get(self, cr, uid, ids, field_name, arg=None, context={}):
530                 result = {}.fromkeys(ids, 'STOCK_NEW')
531                 for r in self.read(cr, uid, ids, ['state']):
532                         result[r['id']] = {
533                                 'draft': 'STOCK_NEW',
534                                 'printed': 'STOCK_PRINT_PREVIEW',
535                                 'done': 'STOCK_DIALOG_AUTHENTICATION',
536                         }.get(r['state'], 'STOCK_NEW')
537                 return result
538
539         _columns = {
540                 'name': fields.char('Journal-Period Name', size=64, required=True),
541                 'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"),
542                 'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"),
543                 'icon': fields.function(_icon_get, method=True, string='Icon', type='string'),
544                 'active': fields.boolean('Active', required=True),
545                 'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'State', required=True, readonly=True)
546         }
547
548         def _check(self, cr, uid, ids, context={}):
549                 for obj in self.browse(cr, uid, ids, context):
550                         cr.execute('select * from account_move_line where journal_id=%d and period_id=%d limit 1', (obj.journal_id.id, obj.period_id.id))
551                         res = cr.fetchall()
552                         if res:
553                                 raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !'))
554                 return True
555
556         def write(self, cr, uid, ids, vals, context={}):
557                 self._check(cr, uid, ids, context)
558                 return super(account_journal_period, self).write(cr, uid, ids, vals, context)
559
560         def create(self, cr, uid, vals, context={}):
561                 period_id=vals.get('period_id',False)
562                 if period_id:
563                         period = self.pool.get('account.period').browse(cr, uid,period_id)
564                         vals['state']=period.state
565                 return super(account_journal_period, self).create(cr, uid, vals, context)
566
567         def unlink(self, cr, uid, ids, context={}):
568                 self._check(cr, uid, ids, context)
569                 return super(account_journal_period, self).unlink(cr, uid, ids, context)
570
571         _defaults = {
572                 'state': lambda *a: 'draft',
573                 'active': lambda *a: True,
574         }
575         _order = "period_id"
576
577 account_journal_period()
578
579 class account_fiscalyear(osv.osv):
580         _inherit = "account.fiscalyear"
581         _description = "Fiscal Year"
582         _columns = {
583                 'start_journal_period_id':fields.many2one('account.journal.period','New Entries Journal'),
584                 'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True),
585         }
586
587 account_fiscalyear()
588 #----------------------------------------------------------
589 # Entries
590 #----------------------------------------------------------
591 class account_move(osv.osv):
592         _name = "account.move"
593         _description = "Account Entry"
594
595         def name_get(self, cursor, user, ids, context=None):
596                 if not len(ids):
597                         return []
598                 res=[]
599                 data_move = self.pool.get('account.move').browse(cursor,user,ids)
600                 for move in data_move:
601                         if move.state=='draft':
602                                 name = '*' + move.name
603                         else:
604                                 name = move.name
605                         res.append((move.id, name))
606                 return res
607
608
609         def _get_period(self, cr, uid, context):
610                 periods = self.pool.get('account.period').find(cr, uid)
611                 if periods:
612                         return periods[0]
613                 else:
614                         return False
615         _columns = {
616                 'name': fields.char('Entry Name', size=64, required=True),
617                 'ref': fields.char('Ref', size=64),
618                 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}),
619                 'journal_id': fields.many2one('account.journal', 'Journal', required=True, states={'posted':[('readonly',True)]}),
620                 'state': fields.selection([('draft','Draft'), ('posted','Posted')], 'State', required=True, readonly=True),
621                 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}),
622         }
623         _defaults = {
624                 'state': lambda *a: 'draft',
625                 'period_id': _get_period,
626         }
627
628         def _check_centralisation(self, cursor, user, ids):
629                 for move in self.browse(cursor, user, ids):
630                         if move.journal_id.centralisation:
631                                 move_ids = self.search(cursor, user, [
632                                         ('period_id', '=', move.period_id.id),
633                                         ('journal_id', '=', move.journal_id.id),
634                                         ])
635                                 if len(move_ids) > 1:
636                                         return False
637                 return True
638
639         def _check_period_journal(self, cursor, user, ids):
640                 for move in self.browse(cursor, user, ids):
641                         for line in move.line_id:
642                                 if line.period_id.id != move.period_id.id:
643                                         return False
644                                 if line.journal_id.id != move.journal_id.id:
645                                         return False
646                 return True
647
648         _constraints = [
649                 (_check_centralisation,
650                         'You can not create more than one move per period on centralized journal',
651                         ['journal_id']),
652                 (_check_period_journal,
653                         'You can not create entries on different period/journal in the same move',
654                         ['line_id']),
655         ]
656         def post(self, cr, uid, ids, context=None):
657                 if self.validate(cr, uid, ids, context) and len(ids):
658                         cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('posted',))
659                 else:
660                         raise osv.except_osv(_('Integrity Error !'), _('You can not validate a non balanced entry !'))
661                 return True
662
663         def button_validate(self, cursor, user, ids, context=None):
664                 return self.post(cursor, user, ids, context=context)
665
666         def button_cancel(self, cr, uid, ids, context={}):
667                 for line in self.browse(cr, uid, ids, context):
668                         if not line.journal_id.update_posted:
669                                 raise osv.except_osv(_('Error !'), _('You can not modify a posted entry of this journal !'))
670                 if len(ids):
671                         cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('draft',))
672                 return True
673
674         def write(self, cr, uid, ids, vals, context={}):
675                 c = context.copy()
676                 c['novalidate'] = True
677                 result = super(osv.osv, self).write(cr, uid, ids, vals, c)
678                 self.validate(cr, uid, ids, context)
679                 return result
680
681         #
682         # TODO: Check if period is closed !
683         #
684         def create(self, cr, uid, vals, context={}):
685                 if 'line_id' in vals:
686                         if 'journal_id' in vals:
687                                 for l in vals['line_id']:
688                                         if not l[0]:
689                                                 l[2]['journal_id'] = vals['journal_id']
690                                 context['journal_id'] = vals['journal_id']
691                         if 'period_id' in vals:
692                                 for l in vals['line_id']:
693                                         if not l[0]:
694                                                 l[2]['period_id'] = vals['period_id']
695                                 context['period_id'] = vals['period_id']
696                         else:
697                                 default_period = self._get_period(cr, uid, context)
698                                 for l in vals['line_id']:
699                                         if not l[0]:
700                                                 l[2]['period_id'] = default_period
701                                 context['period_id'] = default_period
702
703                 if not 'name' in vals:
704                         journal = self.pool.get('account.journal').browse(cr, uid, context.get('journal_id', vals.get('journal_id', False)))
705                         if journal.sequence_id:
706                                 vals['name'] = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id)
707                         else:
708                                 raise osv.except_osv(_('Error'), _('No sequence defined in the journal !'))
709                 if 'line_id' in vals:
710                         c = context.copy()
711                         c['novalidate'] = True
712                         result = super(account_move, self).create(cr, uid, vals, c)
713                         self.validate(cr, uid, [result], context)
714                 else:
715                         result = super(account_move, self).create(cr, uid, vals, context)
716                 return result
717
718         def unlink(self, cr, uid, ids, context={}, check=True):
719                 toremove = []
720                 for move in self.browse(cr, uid, ids, context):
721                         if move['state'] <> 'draft':
722                                 raise osv.except_osv(_('UserError'),
723                                                 _('You can not delete posted movement: "%s"!') % \
724                                                                 move['name'])
725                         line_ids = map(lambda x: x.id, move.line_id)
726                         context['journal_id'] = move.journal_id.id
727                         context['period_id'] = move.period_id.id
728                         self.pool.get('account.move.line')._update_check(cr, uid, line_ids, context)
729                         toremove.append(move.id)
730                 result = super(account_move, self).unlink(cr, uid, toremove, context)
731                 return result
732
733         def _compute_balance(self, cr, uid, id, context={}):
734                 move = self.browse(cr, uid, [id])[0]
735                 amount = 0
736                 for line in move.line_id:
737                         amount+= (line.debit - line.credit)
738                 return amount
739
740         def _centralise(self, cr, uid, move, mode):
741                 if mode=='credit':
742                         account_id = move.journal_id.default_debit_account_id.id
743                         mode2 = 'debit'
744                         if not account_id:
745                                 raise osv.except_osv(_('UserError'),
746                                                 _('There is no default default debit account defined \n' \
747                                                                 'on journal "%s"') % move.journal_id.name)
748                 else:
749                         account_id = move.journal_id.default_credit_account_id.id
750                         mode2 = 'credit'
751                         if not account_id:
752                                 raise osv.except_osv(_('UserError'),
753                                                 _('There is no default default credit account defined \n' \
754                                                                 'on journal "%s"') % move.journal_id.name)
755
756                 # find the first line of this move with the current mode
757                 # or create it if it doesn't exist
758                 cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode))
759                 res = cr.fetchone()
760                 if res:
761                         line_id = res[0]
762                 else:
763                         line_id = self.pool.get('account.move.line').create(cr, uid, {
764                                 'name': 'Centralisation '+mode,
765                                 'centralisation': mode,
766                                 'account_id': account_id,
767                                 'move_id': move.id,
768                                 'journal_id': move.journal_id.id,
769                                 'period_id': move.period_id.id,
770                                 'date': move.period_id.date_stop,
771                                 'debit': 0.0,
772                                 'credit': 0.0,
773                         }, {'journal_id': move.journal_id.id, 'period_id': move.period_id.id})
774
775                 # find the first line of this move with the other mode
776                 # so that we can exclude it from our calculation
777                 cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode2))
778                 res = cr.fetchone()
779                 if res:
780                         line_id2 = res[0]
781                 else:
782                         line_id2 = 0
783
784                 cr.execute('select sum('+mode+') from account_move_line where move_id=%d and id<>%d', (move.id, line_id2))
785                 result = cr.fetchone()[0] or 0.0
786                 cr.execute('update account_move_line set '+mode2+'=%f where id=%d', (result, line_id))
787                 return True
788
789         #
790         # Validate a balanced move. If it is a centralised journal, create a move.
791         #
792         def validate(self, cr, uid, ids, context={}):
793                 ok = True
794                 for move in self.browse(cr, uid, ids, context):
795                         journal = move.journal_id
796                         amount = 0
797                         line_ids = []
798                         line_draft_ids = []
799                         company_id=None
800                         for line in move.line_id:
801                                 amount += line.debit - line.credit
802                                 line_ids.append(line.id)
803                                 if line.state=='draft':
804                                         line_draft_ids.append(line.id)
805
806                                 if not company_id:
807                                         company_id = line.account_id.company_id.id
808                                 if not company_id == line.account_id.company_id.id:
809                                         raise osv.except_osv(_('Error'), _('Couldn\'t create move between different companies'))
810
811                                 if line.account_id.currency_id:
812                                         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):
813                                                         raise osv.except_osv(_('Error'), _('Couldn\'t create move with currency different than the secondary currency of the account'))
814
815                         if abs(amount) < 0.0001:
816                                 if not len(line_draft_ids):
817                                         continue
818                                 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
819                                         'journal_id': move.journal_id.id,
820                                         'period_id': move.period_id.id,
821                                         'state': 'valid'
822                                 }, context, check=False)
823                                 todo = []
824                                 account = {}
825                                 account2 = {}
826                                 if journal.type not in ('purchase','sale'):
827                                         continue
828
829                                 for line in move.line_id:
830                                         if move.journal_id.type == 'sale':
831                                                 if line.debit:
832                                                         field_base = 'ref_'
833                                                         key = 'account_paid_id'
834                                                 else:
835                                                         field_base = ''
836                                                         key = 'account_collected_id'
837                                         else:
838                                                 if line.debit:
839                                                         field_base = ''
840                                                         key = 'account_collected_id'
841                                                 else:
842                                                         field_base = 'ref_'
843                                                         key = 'account_paid_id'
844                                         if line.account_id.tax_ids:
845                                                 code = amount = False
846                                                 for tax in line.account_id.tax_ids:
847                                                         if tax.tax_code_id:
848                                                                 acc = getattr(tax, key).id
849                                                                 account[acc] = (getattr(tax,
850                                                                         field_base + 'tax_code_id').id,
851                                                                         getattr(tax, field_base + 'tax_sign'))
852                                                                 account2[(acc,getattr(tax,
853                                                                         field_base + 'tax_code_id').id)] = (getattr(tax,
854                                                                                 field_base + 'tax_code_id').id,
855                                                                                 getattr(tax, field_base + 'tax_sign'))
856                                                                 code = getattr(tax, field_base + 'base_code_id').id
857                                                                 amount = getattr(tax, field_base+'base_sign') * \
858                                                                                 (line.debit + line.credit)
859                                                                 break
860                                                 if code and not (line.tax_code_id or line.tax_amount):
861                                                         self.pool.get('account.move.line').write(cr, uid,
862                                                                         [line.id], {
863                                                                 'tax_code_id': code,
864                                                                 'tax_amount': amount
865                                                         }, context=context, check=False)
866                                         else:
867                                                 todo.append(line)
868                                 for line in todo:
869                                         code = amount = 0
870                                         key = (line.account_id.id, line.tax_code_id.id)
871                                         if key in account2:
872                                                 code = account2[key][0]
873                                                 amount = account2[key][1] * (line.debit + line.credit)
874                                         elif line.account_id.id in account:
875                                                 code = account[line.account_id.id][0]
876                                                 amount = account[line.account_id.id][1] * (line.debit + line.credit)
877                                         if (code or amount) and not (line.tax_code_id or line.tax_amount):
878                                                 self.pool.get('account.move.line').write(cr, uid, [line.id], {
879                                                         'tax_code_id': code,
880                                                         'tax_amount': amount
881                                                 }, context, check=False)
882                                 #
883                                 # Compute VAT
884                                 #
885                                 continue
886                         if journal.centralisation:
887                                 self._centralise(cr, uid, move, 'debit')
888                                 self._centralise(cr, uid, move, 'credit')
889                                 self.pool.get('account.move.line').write(cr, uid, line_draft_ids, {
890                                         'state': 'valid'
891                                 }, context, check=False)
892                                 continue
893                         else:
894                                 self.pool.get('account.move.line').write(cr, uid, line_ids, {
895                                         'journal_id': move.journal_id.id,
896                                         'period_id': move.period_id.id,
897                                         #'tax_code_id': False,
898                                         #'tax_amount': False,
899                                         'state': 'draft'
900                                 }, context, check=False)
901                                 ok = False
902                 return ok
903 account_move()
904
905 class account_move_reconcile(osv.osv):
906         _name = "account.move.reconcile"
907         _description = "Account Reconciliation"
908         _columns = {
909                 'name': fields.char('Name', size=64, required=True),
910                 'type': fields.char('Type', size=16, required=True),
911                 'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry lines'),
912                 'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'),
913                 'create_date': fields.date('Creation date', readonly=True),
914         }
915         _defaults = {
916                 'name': lambda self,cr,uid,ctx={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile') or '/',
917         }
918         def reconcile_partial_check(self, cr, uid, ids, type='auto', context={}):
919                 for rec in self.pool.get('account.move.reconcile').browse(cr, uid, ids):
920                         total = 0.0
921                         for line in rec.line_partial_ids:
922                                 total += (line.debit or 0.0) - (line.credit or 0.0)
923                         if not total:
924                                 self.write(cr,uid, map(lambda x: x.id, rec.line_partial_ids), {'reconcile_id': rec.id })
925                                 for line in rec.line_partial_ids:
926                                         total += (line.debit or 0.0) - (line.credit or 0.0)
927                 return True
928         def name_get(self, cr, uid, ids, context=None):
929                 result = {}
930                 for r in self.browse(cr, uid, ids, context):
931                         total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0)
932                         if total:
933                                 result[r.id] = '%s (%.2f)' % (r.name, total)
934                         else:
935                                 result[r.id] = r.name
936                 return result
937 account_move_reconcile()
938
939 #----------------------------------------------------------
940 # Tax
941 #----------------------------------------------------------
942 """
943 a documenter
944 child_depend: la taxe depend des taxes filles
945 """
946 class account_tax_code(osv.osv):
947         """
948         A code for the tax object.
949
950         This code is used for some tax declarations.
951         """
952         def _sum(self, cr, uid, ids, name, args, context, where =''):
953                 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
954                 acc_set = ",".join(map(str, ids2))
955                 if context.get('based_on', 'invoices') == 'payments':
956                         cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
957                                         FROM account_move_line AS line, \
958                                                 account_move AS move \
959                                                 LEFT JOIN account_invoice invoice ON \
960                                                         (invoice.move_id = move.id) \
961                                         WHERE line.tax_code_id in ('+acc_set+') '+where+' \
962                                                 AND move.id = line.move_id \
963                                                 AND ((invoice.state = \'paid\') \
964                                                         OR (invoice.id IS NULL)) \
965                                         GROUP BY line.tax_code_id')
966                 else:
967                         cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \
968                                         FROM account_move_line AS line \
969                                         WHERE line.tax_code_id in ('+acc_set+') '+where+' \
970                                         GROUP BY line.tax_code_id')
971                 res=dict(cr.fetchall())
972                 for record in self.browse(cr, uid, ids, context):
973                         def _rec_get(record):
974                                 amount = res.get(record.id, 0.0)
975                                 for rec in record.child_ids:
976                                         amount += _rec_get(rec) * rec.sign
977                                 return amount
978                         res[record.id] = round(_rec_get(record), 2)
979                 return res
980
981         def _sum_period(self, cr, uid, ids, name, args, context):
982                 if not 'period_id' in context:
983                         period_id = self.pool.get('account.period').find(cr, uid)
984                         if not len(period_id):
985                                 return dict.fromkeys(ids, 0.0)
986                         period_id = period_id[0]
987                 else:
988                         period_id = context['period_id']
989                 return self._sum(cr, uid, ids, name, args, context,
990                                 where=' and line.period_id='+str(period_id))
991
992         _name = 'account.tax.code'
993         _description = 'Tax Code'
994         _columns = {
995                 'name': fields.char('Tax Case Name', size=64, required=True),
996                 'code': fields.char('Case Code', size=16),
997                 'info': fields.text('Description'),
998                 'sum': fields.function(_sum, method=True, string="Year Sum"),
999                 'sum_period': fields.function(_sum_period, method=True, string="Period Sum"),
1000                 'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True),
1001                 'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'),
1002                 'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'),
1003                 'company_id': fields.many2one('res.company', 'Company', required=True),
1004                 'sign': fields.float('Sign for parent', required=True),
1005         }
1006
1007         def name_get(self, cr, uid, ids, context=None):
1008                 if not len(ids):
1009                         return []
1010                 if isinstance(ids, (int, long)):
1011                         ids = [ids]
1012                 reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write')
1013                 return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \
1014                                 for x in reads]
1015
1016         def _default_company(self, cr, uid, context={}):
1017                 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1018                 if user.company_id:
1019                         return user.company_id.id
1020                 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1021         _defaults = {
1022                 'company_id': _default_company,
1023                 'sign': lambda *args: 1.0,
1024         }
1025         def _check_recursion(self, cr, uid, ids):
1026                 level = 100
1027                 while len(ids):
1028                         cr.execute('select distinct parent_id from account_tax_code where id in ('+','.join(map(str,ids))+')')
1029                         ids = filter(None, map(lambda x:x[0], cr.fetchall()))
1030                         if not level:
1031                                 return False
1032                         level -= 1
1033                 return True
1034
1035         _constraints = [
1036                 (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id'])
1037         ]
1038         _order = 'code,name'
1039 account_tax_code()
1040
1041 class account_tax(osv.osv):
1042         """
1043         A tax object.
1044
1045         Type: percent, fixed, none, code
1046                 PERCENT: tax = price * amount
1047                 FIXED: tax = price + amount
1048                 NONE: no tax line
1049                 CODE: execute python code. localcontext = {'price_unit':pu, 'address':address_object}
1050                         return result in the context
1051                         Ex: result=round(price_unit*0.21,4)
1052         """
1053         _name = 'account.tax'
1054         _description = 'Tax'
1055         _columns = {
1056                 'name': fields.char('Tax Name', size=64, required=True),
1057                 '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."),
1058                 'amount': fields.float('Amount', required=True, digits=(14,4)),
1059                 'active': fields.boolean('Active'),
1060                 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True),
1061                 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True),
1062                 '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."),
1063                 'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'),
1064                 'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'),
1065                 'parent_id':fields.many2one('account.tax', 'Parent Tax Account', select=True),
1066                 'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'),
1067                 '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."),
1068                 'python_compute':fields.text('Python Code'),
1069                 'python_compute_inv':fields.text('Python Code (reverse)'),
1070                 'python_applicable':fields.text('Python Code'),
1071                 '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."),
1072
1073                 #
1074                 # Fields used for the VAT declaration
1075                 #
1076                 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."),
1077                 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."),
1078                 'base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."),
1079                 'tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."),
1080
1081                 # Same fields for refund invoices
1082
1083                 'ref_base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."),
1084                 'ref_tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."),
1085                 'ref_base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."),
1086                 'ref_tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."),
1087                 '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"),
1088                 'company_id': fields.many2one('res.company', 'Company', required=True),
1089         }
1090
1091         def _default_company(self, cr, uid, context={}):
1092                 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
1093                 if user.company_id:
1094                         return user.company_id.id
1095                 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
1096         _defaults = {
1097                 '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''',
1098                 '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''',
1099                 'applicable_type': lambda *a: 'true',
1100                 'type': lambda *a: 'percent',
1101                 'amount': lambda *a: 0,
1102                 'active': lambda *a: 1,
1103                 'sequence': lambda *a: 1,
1104                 'tax_group': lambda *a: 'vat',
1105                 'ref_tax_sign': lambda *a: 1,
1106                 'ref_base_sign': lambda *a: 1,
1107                 'tax_sign': lambda *a: 1,
1108                 'base_sign': lambda *a: 1,
1109                 'include_base_amount': lambda *a: False,
1110                 'company_id': _default_company,
1111         }
1112         _order = 'sequence'
1113
1114         def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1115                 res = []
1116                 for tax in taxes:
1117                         if tax.applicable_type=='code':
1118                                 localdict = {'price_unit':price_unit, 'address':self.pool.get('res.partner.address').browse(cr, uid, address_id), 'product':product, 'partner':partner}
1119                                 exec tax.python_applicable in localdict
1120                                 if localdict.get('result', False):
1121                                         res.append(tax)
1122                         else:
1123                                 res.append(tax)
1124                 return res
1125
1126         def _unit_compute(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1127                 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1128
1129                 res = []
1130                 cur_price_unit=price_unit
1131                 for tax in taxes:
1132                         # we compute the amount for the current tax object and append it to the result
1133
1134                         if tax.type=='percent':
1135                                 amount = cur_price_unit * tax.amount
1136                                 res.append({'id':tax.id,
1137                                                         'name':tax.name,
1138                                                         'amount':amount,
1139                                                         'account_collected_id':tax.account_collected_id.id,
1140                                                         'account_paid_id':tax.account_paid_id.id,
1141                                                         'base_code_id': tax.base_code_id.id,
1142                                                         'ref_base_code_id': tax.ref_base_code_id.id,
1143                                                         'sequence': tax.sequence,
1144                                                         'base_sign': tax.base_sign,
1145                                                         'tax_sign': tax.tax_sign,
1146                                                         'ref_base_sign': tax.ref_base_sign,
1147                                                         'ref_tax_sign': tax.ref_tax_sign,
1148                                                         'price_unit': cur_price_unit,
1149                                                         'tax_code_id': tax.tax_code_id.id,
1150                                                         'ref_tax_code_id': tax.ref_tax_code_id.id,
1151                                                         })
1152
1153                         elif tax.type=='fixed':
1154                                 res.append({'id':tax.id,
1155                                                         'name':tax.name,
1156                                                         'amount':tax.amount,
1157                                                         'account_collected_id':tax.account_collected_id.id,
1158                                                         'account_paid_id':tax.account_paid_id.id,
1159                                                         'base_code_id': tax.base_code_id.id,
1160                                                         'ref_base_code_id': tax.ref_base_code_id.id,
1161                                                         'sequence': tax.sequence,
1162                                                         'base_sign': tax.base_sign,
1163                                                         'tax_sign': tax.tax_sign,
1164                                                         'ref_base_sign': tax.ref_base_sign,
1165                                                         'ref_tax_sign': tax.ref_tax_sign,
1166                                                         'price_unit': 1,
1167                                                         'tax_code_id': tax.tax_code_id.id,
1168                                                         'ref_tax_code_id': tax.ref_tax_code_id.id,})
1169                         elif tax.type=='code':
1170                                 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1171                                 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1172                                 exec tax.python_compute in localdict
1173                                 amount = localdict['result']
1174                                 res.append({
1175                                         'id': tax.id,
1176                                         'name': tax.name,
1177                                         'amount': amount,
1178                                         'account_collected_id': tax.account_collected_id.id,
1179                                         'account_paid_id': tax.account_paid_id.id,
1180                                         'base_code_id': tax.base_code_id.id,
1181                                         'ref_base_code_id': tax.ref_base_code_id.id,
1182                                         'sequence': tax.sequence,
1183                                         'base_sign': tax.base_sign,
1184                                         'tax_sign': tax.tax_sign,
1185                                         'ref_base_sign': tax.ref_base_sign,
1186                                         'ref_tax_sign': tax.ref_tax_sign,
1187                                         'price_unit': cur_price_unit,
1188                                         'tax_code_id': tax.tax_code_id.id,
1189                                         'ref_tax_code_id': tax.ref_tax_code_id.id,
1190                                 })
1191                         amount2 = res[-1]['amount']
1192                         if len(tax.child_ids):
1193                                 if tax.child_depend:
1194                                         del res[-1]
1195                                 amount = amount2
1196                                 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner)
1197                                 res.extend(child_tax)
1198                         if tax.include_base_amount:
1199                                 cur_price_unit+=amount2
1200                 return res
1201
1202         def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1203
1204                 """
1205                 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1206
1207                 RETURN:
1208                         [ tax ]
1209                         tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1210                         one tax for each tax id in IDS and their childs
1211                 """
1212                 res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner)
1213                 for r in res:
1214                         r['amount'] *= quantity
1215                 return res
1216
1217         def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
1218                 taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
1219
1220                 res = []
1221                 taxes.reverse()
1222                 cur_price_unit=price_unit
1223                 for tax in taxes:
1224                         # we compute the amount for the current tax object and append it to the result
1225
1226                         if tax.type=='percent':
1227                                 amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
1228                                 res.append({'id':tax.id,
1229                                                         'name':tax.name,
1230                                                         'amount':amount,
1231                                                         'account_collected_id':tax.account_collected_id.id,
1232                                                         'account_paid_id':tax.account_paid_id.id,
1233                                                         'base_code_id': tax.base_code_id.id,
1234                                                         'ref_base_code_id': tax.ref_base_code_id.id,
1235                                                         'sequence': tax.sequence,
1236                                                         'base_sign': tax.base_sign,
1237                                                         'tax_sign': tax.tax_sign,
1238                                                         'ref_base_sign': tax.ref_base_sign,
1239                                                         'ref_tax_sign': tax.ref_tax_sign,
1240                                                         'price_unit': cur_price_unit - amount,
1241                                                         'tax_code_id': tax.tax_code_id.id,
1242                                                         'ref_tax_code_id': tax.ref_tax_code_id.id,})
1243
1244                         elif tax.type=='fixed':
1245                                 res.append({'id':tax.id,
1246                                                         'name':tax.name,
1247                                                         'amount':tax.amount,
1248                                                         'account_collected_id':tax.account_collected_id.id,
1249                                                         'account_paid_id':tax.account_paid_id.id,
1250                                                         'base_code_id': tax.base_code_id.id,
1251                                                         'ref_base_code_id': tax.ref_base_code_id.id,
1252                                                         'sequence': tax.sequence,
1253                                                         'base_sign': tax.base_sign,
1254                                                         'tax_sign': tax.tax_sign,
1255                                                         'ref_base_sign': tax.ref_base_sign,
1256                                                         'ref_tax_sign': tax.ref_tax_sign,
1257                                                         'price_unit': 1,
1258                                                         'tax_code_id': tax.tax_code_id.id,
1259                                                         'ref_tax_code_id': tax.ref_tax_code_id.id,})
1260
1261                         elif tax.type=='code':
1262                                 address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
1263                                 localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
1264                                 exec tax.python_compute_inv in localdict
1265                                 amount = localdict['result']
1266                                 res.append({
1267                                         'id': tax.id,
1268                                         'name': tax.name,
1269                                         'amount': amount,
1270                                         'account_collected_id': tax.account_collected_id.id,
1271                                         'account_paid_id': tax.account_paid_id.id,
1272                                         'base_code_id': tax.base_code_id.id,
1273                                         'ref_base_code_id': tax.ref_base_code_id.id,
1274                                         'sequence': tax.sequence,
1275                                         'base_sign': tax.base_sign,
1276                                         'tax_sign': tax.tax_sign,
1277                                         'ref_base_sign': tax.ref_base_sign,
1278                                         'ref_tax_sign': tax.ref_tax_sign,
1279                                         'price_unit': cur_price_unit - amount,
1280                                         'tax_code_id': tax.tax_code_id.id,
1281                                         'ref_tax_code_id': tax.ref_tax_code_id.id,
1282                                 })
1283
1284                         amount2 = res[-1]['amount']
1285                         if len(tax.child_ids):
1286                                 if tax.child_depend:
1287                                         del res[-1]
1288                                         amount = price_unit
1289                                 else:
1290                                         amount = amount2
1291                         for t in tax.child_ids:
1292                                 parent_tax = self._unit_compute_inv(cr, uid, [t], amount, address_id, product, partner)
1293                                 res.extend(parent_tax)
1294                         if tax.include_base_amount:
1295                                 cur_price_unit-=amount
1296                 taxes.reverse()
1297                 return res
1298
1299         def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
1300                 """
1301                 Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
1302                 Price Unit is a VAT included price
1303
1304                 RETURN:
1305                         [ tax ]
1306                         tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
1307                         one tax for each tax id in IDS and their childs
1308                 """
1309                 res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
1310                 for r in res:
1311                         r['amount'] *= quantity
1312                 return res
1313 account_tax()
1314
1315 # ---------------------------------------------------------
1316 # Budgets
1317 # ---------------------------------------------------------
1318
1319 class account_budget_post(osv.osv):
1320         _name = 'account.budget.post'
1321         _description = 'Budget item'
1322         _columns = {
1323                 'code': fields.char('Code', size=64, required=True),
1324                 'name': fields.char('Name', size=256, required=True),
1325                 'dotation_ids': fields.one2many('account.budget.post.dotation', 'post_id', 'Expenses'),
1326                 'account_ids': fields.many2many('account.account', 'account_budget_rel', 'budget_id', 'account_id', 'Accounts'),
1327         }
1328         _defaults = {
1329         }
1330
1331         def spread(self, cr, uid, ids, fiscalyear_id=False, amount=0.0):
1332                 dobj = self.pool.get('account.budget.post.dotation')
1333                 for o in self.browse(cr, uid, ids):
1334                         # delete dotations for this post
1335                         dobj.unlink(cr, uid, dobj.search(cr, uid, [('post_id','=',o.id)]))
1336
1337                         # create one dotation per period in the fiscal year, and spread the total amount/quantity over those dotations
1338                         fy = self.pool.get('account.fiscalyear').browse(cr, uid, [fiscalyear_id])[0]
1339                         num = len(fy.period_ids)
1340                         for p in fy.period_ids:
1341                                 dobj.create(cr, uid, {'post_id': o.id, 'period_id': p.id, 'amount': amount/num})
1342                 return True
1343 account_budget_post()
1344
1345 class account_budget_post_dotation(osv.osv):
1346         _name = 'account.budget.post.dotation'
1347         _description = "Budget item endowment"
1348         _columns = {
1349                 'name': fields.char('Name', size=64),
1350                 'post_id': fields.many2one('account.budget.post', 'Item', select=True),
1351                 'period_id': fields.many2one('account.period', 'Period'),
1352 #               'quantity': fields.float('Quantity', digits=(16,2)),
1353                 'amount': fields.float('Amount', digits=(16,2)),
1354         }
1355 account_budget_post_dotation()
1356
1357
1358 # ---------------------------------------------------------
1359 # Account Entries Models
1360 # ---------------------------------------------------------
1361
1362 class account_model(osv.osv):
1363         _name = "account.model"
1364         _description = "Account Model"
1365         _columns = {
1366                 'name': fields.char('Model Name', size=64, required=True, help="This is a model for recurring accounting entries"),
1367                 'ref': fields.char('Ref', size=64),
1368                 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
1369                 'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'),
1370         }
1371         def generate(self, cr, uid, ids, datas={}, context={}):
1372                 move_ids = []
1373                 for model in self.browse(cr, uid, ids, context):
1374                         period_id = self.pool.get('account.period').find(cr,uid, context=context)
1375                         if not period_id:
1376                                 raise osv.except_osv(_('No period found !'), _('Unable to find a valid period !'))
1377                         period_id = period_id[0]
1378                         name = model.name
1379                         if model.journal_id.sequence_id:
1380                                 name = self.pool.get('ir.sequence').get_id(cr, uid, model.journal_id.sequence_id.id)
1381                         move_id = self.pool.get('account.move').create(cr, uid, {
1382                                 'name': name,
1383                                 'ref': model.ref,
1384                                 'period_id': period_id,
1385                                 'journal_id': model.journal_id.id,
1386                         })
1387                         move_ids.append(move_id)
1388                         for line in model.lines_id:
1389                                 val = {
1390                                         'move_id': move_id,
1391                                         'journal_id': model.journal_id.id,
1392                                         'period_id': period_id
1393                                 }
1394                                 val.update({
1395                                         'name': line.name,
1396                                         'quantity': line.quantity,
1397                                         'debit': line.debit,
1398                                         'credit': line.credit,
1399                                         'account_id': line.account_id.id,
1400                                         'move_id': move_id,
1401                                         'ref': line.ref,
1402                                         'partner_id': line.partner_id.id,
1403                                         'date': time.strftime('%Y-%m-%d'),
1404                                         'date_maturity': time.strftime('%Y-%m-%d')
1405                                 })
1406                                 c = context.copy()
1407                                 c.update({'journal_id': model.journal_id.id,'period_id': period_id})
1408                                 self.pool.get('account.move.line').create(cr, uid, val, context=c)
1409                 return move_ids
1410 account_model()
1411
1412 class account_model_line(osv.osv):
1413         _name = "account.model.line"
1414         _description = "Account Model Entries"
1415         _columns = {
1416                 'name': fields.char('Name', size=64, required=True),
1417                 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the resources from the lowest sequences to the higher ones"),
1418                 'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity on entries"),
1419                 'debit': fields.float('Debit', digits=(16,2)),
1420                 'credit': fields.float('Credit', digits=(16,2)),
1421
1422                 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"),
1423
1424                 'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True),
1425
1426                 'ref': fields.char('Ref.', size=16),
1427
1428                 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."),
1429                 'currency_id': fields.many2one('res.currency', 'Currency'),
1430
1431                 'partner_id': fields.many2one('res.partner', 'Partner Ref.'),
1432                 '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."),
1433                 'date': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Current Date', required=True, help="The date of the generated entries"),
1434         }
1435         _defaults = {
1436                 'date': lambda *a: 'today'
1437         }
1438         _order = 'sequence'
1439         _sql_constraints = [
1440                 ('credit_debit1', 'CHECK (credit*debit=0)',  'Wrong credit or debit value in model !'),
1441                 ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model !'),
1442         ]
1443 account_model_line()
1444
1445 # ---------------------------------------------------------
1446 # Account Subscription
1447 # ---------------------------------------------------------
1448
1449
1450 class account_subscription(osv.osv):
1451         _name = "account.subscription"
1452         _description = "Account Subscription"
1453         _columns = {
1454                 'name': fields.char('Name', size=64, required=True),
1455                 'ref': fields.char('Ref.', size=16),
1456                 'model_id': fields.many2one('account.model', 'Model', required=True),
1457
1458                 'date_start': fields.date('Starting date', required=True),
1459                 'period_total': fields.integer('Number of period', required=True),
1460                 'period_nbr': fields.integer('Period', required=True),
1461                 'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True),
1462                 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'State', required=True, readonly=True),
1463
1464                 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines')
1465         }
1466         _defaults = {
1467                 'date_start': lambda *a: time.strftime('%Y-%m-%d'),
1468                 'period_type': lambda *a: 'month',
1469                 'period_total': lambda *a: 12,
1470                 'period_nbr': lambda *a: 1,
1471                 'state': lambda *a: 'draft',
1472         }
1473         def state_draft(self, cr, uid, ids, context={}):
1474                 self.write(cr, uid, ids, {'state':'draft'})
1475                 return False
1476
1477         def check(self, cr, uid, ids, context={}):
1478                 todone = []
1479                 for sub in self.browse(cr, uid, ids, context):
1480                         ok = True
1481                         for line in sub.lines_id:
1482                                 if not line.move_id.id:
1483                                         ok = False
1484                                         break
1485                         if ok:
1486                                 todone.append(sub.id)
1487                 if len(todone):
1488                         self.write(cr, uid, todone, {'state':'done'})
1489                 return False
1490
1491         def remove_line(self, cr, uid, ids, context={}):
1492                 toremove = []
1493                 for sub in self.browse(cr, uid, ids, context):
1494                         for line in sub.lines_id:
1495                                 if not line.move_id.id:
1496                                         toremove.append(line.id)
1497                 if len(toremove):
1498                         self.pool.get('account.subscription.line').unlink(cr, uid, toremove)
1499                 self.write(cr, uid, ids, {'state':'draft'})
1500                 return False
1501
1502         def compute(self, cr, uid, ids, context={}):
1503                 for sub in self.browse(cr, uid, ids, context):
1504                         ds = sub.date_start
1505                         for i in range(sub.period_total):
1506                                 self.pool.get('account.subscription.line').create(cr, uid, {
1507                                         'date': ds,
1508                                         'subscription_id': sub.id,
1509                                 })
1510                                 if sub.period_type=='day':
1511                                         ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(days=sub.period_nbr)).strftime('%Y-%m-%d')
1512                                 if sub.period_type=='month':
1513                                         ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(months=sub.period_nbr)).strftime('%Y-%m-%d')
1514                                 if sub.period_type=='year':
1515                                         ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(years=sub.period_nbr)).strftime('%Y-%m-%d')
1516                 self.write(cr, uid, ids, {'state':'running'})
1517                 return True
1518 account_subscription()
1519
1520 class account_subscription_line(osv.osv):
1521         _name = "account.subscription.line"
1522         _description = "Account Subscription Line"
1523         _columns = {
1524                 'subscription_id': fields.many2one('account.subscription', 'Subscription', required=True, select=True),
1525                 'date': fields.date('Date', required=True),
1526                 'move_id': fields.many2one('account.move', 'Entry'),
1527         }
1528         _defaults = {
1529         }
1530         def move_create(self, cr, uid, ids, context={}):
1531                 tocheck = {}
1532                 for line in self.browse(cr, uid, ids, context):
1533                         datas = {
1534                                 'date': line.date,
1535                         }
1536                         ids = self.pool.get('account.model').generate(cr, uid, [line.subscription_id.model_id.id], datas, context)
1537                         tocheck[line.subscription_id.id] = True
1538                         self.write(cr, uid, [line.id], {'move_id':ids[0]})
1539                 if tocheck:
1540                         self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context)
1541                 return True
1542         _rec_name = 'date'
1543 account_subscription_line()
1544
1545
1546 class account_config_fiscalyear(osv.osv_memory):
1547     _name = 'account.config.fiscalyear'
1548     _columns = {
1549                 'name':fields.char('Name', required=True,size=64),
1550                 'code':fields.char('Code', required=True,size=64),
1551         'date1': fields.date('Start of period', required=True),
1552         'date2': fields.date('End of period', required=True),
1553     }
1554     _defaults = {
1555         'date1': lambda *a: time.strftime('%Y-01-01'),
1556         'date2': lambda *a: time.strftime('%Y-12-31'),
1557     }
1558     def action_cancel(self,cr,uid,ids,conect=None):
1559                 return {
1560                             'view_type': 'form',
1561                             "view_mode": 'form',
1562                                 'res_model': 'ir.module.module.configuration.wizard',
1563                                 'type': 'ir.actions.act_window',
1564                                 'target':'new',
1565          }
1566     def action_create(self, cr, uid,ids, context=None):
1567         res=self.read(cr,uid,ids)[0]
1568         if 'date1' in res and 'date2' in res:
1569                 res_obj = self.pool.get('account.fiscalyear')
1570                 start_date=res['date1']
1571                 end_date=res['date2']
1572                 name=res['name']#DateTime.strptime(start_date, '%Y-%m-%d').strftime('%m.%Y') + '-' + DateTime.strptime(end_date, '%Y-%m-%d').strftime('%m.%Y')
1573                 vals={
1574                   'name':name,
1575                   'code':name,
1576                   'date_start':start_date,
1577                   'date_stop':end_date,
1578                   }
1579                 new_id=res_obj.create(cr, uid, vals, context=context)
1580         return {
1581                 'view_type': 'form',
1582                 "view_mode": 'form',
1583                                 'res_model': 'ir.module.module.configuration.wizard',
1584                                 'type': 'ir.actions.act_window',
1585                                 'target':'new',
1586
1587          }
1588
1589 account_config_fiscalyear()
1590
1591
1592
1593
1594 class account_config_journal_bank_accounts(osv.osv_memory):
1595         _name='account.config.journal.bank.account'
1596         _columns = {
1597                 'name':fields.char('Journal Name', size=64),
1598                 'lines_id': fields.one2many('account.config.journal.bank.account.line', 'journal_id', 'Journal Lines'),
1599     }
1600
1601         def action_cancel(self,cr,uid,ids,conect=None):
1602                 return {
1603                             'view_type': 'form',
1604                             "view_mode": 'form',
1605                                 'res_model': 'ir.module.module.configuration.wizard',
1606                                 'type': 'ir.actions.act_window',
1607                                 'target':'new',
1608          }
1609
1610         def action_create(self, cr, uid, ids, context=None):
1611                 config_res=self.read(cr,uid,ids)[0]
1612                 res_obj = self.pool.get('account.journal')
1613                 line_obj=self.pool.get('account.config.journal.bank.account.line')
1614                 if 'lines_id' in config_res and config_res['lines_id']:
1615                         lines=line_obj.read(cr,uid,config_res['lines_id'])
1616                         for res in lines:
1617                                 sequence_ids=self.pool.get('ir.sequence').search(cr,uid,[('name','=','Account Journal')])
1618                                 if 'name' in res and 'bank_account_id' in res and 'view_id'  in res and sequence_ids and len(sequence_ids):
1619                                         vals={
1620                                                   'name':res['name'],
1621                                                   'type':'cash',
1622                                                   'view_id':res['view_id'],
1623                                                   'default_credit_account_id':res['bank_account_id'],
1624                                                   'default_debit_account_id':res['bank_account_id'],
1625                                                   'sequence_id':sequence_ids[0]
1626                                                   }
1627                                         res_obj.create(cr, uid, vals, context=context)
1628                 return {
1629                                 'view_type': 'form',
1630                                 "view_mode": 'form',
1631                             'res_model': 'ir.module.module.configuration.wizard',
1632                             'type': 'ir.actions.act_window',
1633                             'target':'new',
1634                         }
1635
1636 account_config_journal_bank_accounts()
1637
1638 class account_config_journal_bank_accounts_line(osv.osv_memory):
1639         _name='account.config.journal.bank.account.line'
1640         def _journal_view_get(self, cr, uid, context={}):
1641                 journal_obj = self.pool.get('account.journal.view')
1642                 ids = journal_obj.search(cr, uid, [])
1643                 res = journal_obj.read(cr, uid, ids, ['id', 'name'], context)
1644                 return [(r['id'], r['name']) for r in res]
1645         _columns = {
1646                 'name':fields.char('Journal Name', size=64,required=True),
1647                 'bank_account_id':fields.many2one('account.account', 'Bank Account', required=True, domain=[('type','=','cash')]),
1648                 'view_id':fields.selection(_journal_view_get, 'Journal View', required=True),
1649                 'journal_id':fields.many2one('account.config.journal.bank.account', 'Journal', required=True),
1650     }
1651 account_config_journal_bank_accounts_line()
1652