[IMP] Changed all module categories, limited number of categories
[odoo/odoo.git] / addons / account / account_cash_statement.py
1 # encoding: utf-8
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2008 PC Solutions (<http://pcsol.be>). All Rights Reserved
6 #    $Id$
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation, either version 3 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 import time
24
25 from osv import osv, fields
26 from tools.translate import _
27 import decimal_precision as dp
28
29 class account_cashbox_line(osv.osv):
30
31     """ Cash Box Details """
32
33     _name = 'account.cashbox.line'
34     _description = 'CashBox Line'
35
36     def _sub_total(self, cr, uid, ids, name, arg, context=None):
37
38         """ Calculates Sub total
39         @param name: Names of fields.
40         @param arg: User defined arguments
41         @return: Dictionary of values.
42         """
43         res = {}
44         for obj in self.browse(cr, uid, ids, context=context):
45             res[obj.id] = obj.pieces * obj.number
46         return res
47
48     def on_change_sub(self, cr, uid, ids, pieces, number, *a):
49
50         """ Calculates Sub total on change of number
51         @param pieces: Names of fields.
52         @param number:
53         """
54         sub = pieces * number
55         return {'value': {'subtotal': sub or 0.0}}
56
57     _columns = {
58         'pieces': fields.float('Values', digits_compute=dp.get_precision('Account')),
59         'number': fields.integer('Number'),
60         'subtotal': fields.function(_sub_total, method=True, string='Sub Total', type='float', digits_compute=dp.get_precision('Account')),
61         'starting_id': fields.many2one('account.bank.statement', ondelete='cascade'),
62         'ending_id': fields.many2one('account.bank.statement', ondelete='cascade'),
63      }
64
65 account_cashbox_line()
66
67 class account_cash_statement(osv.osv):
68
69     _inherit = 'account.bank.statement'
70
71     def _get_starting_balance(self, cr, uid, ids, context=None):
72
73         """ Find starting balance
74         @param name: Names of fields.
75         @param arg: User defined arguments
76         @return: Dictionary of values.
77         """
78         res = {}
79         for statement in self.browse(cr, uid, ids, context=context):
80             amount_total = 0.0
81
82             if statement.journal_id.type not in('cash'):
83                 continue
84
85             for line in statement.starting_details_ids:
86                 amount_total+= line.pieces * line.number
87             res[statement.id] = {
88                 'balance_start': amount_total
89             }
90         return res
91
92     def _balance_end_cash(self, cr, uid, ids, name, arg, context=None):
93         """ Find ending balance  "
94         @param name: Names of fields.
95         @param arg: User defined arguments
96         @return: Dictionary of values.
97         """
98         res = {}
99         for statement in self.browse(cr, uid, ids, context=context):
100             amount_total = 0.0
101             for line in statement.ending_details_ids:
102                 amount_total += line.pieces * line.number
103             res[statement.id] = amount_total
104         return res
105
106     def _get_sum_entry_encoding(self, cr, uid, ids, name, arg, context=None):
107
108         """ Find encoding total of statements "
109         @param name: Names of fields.
110         @param arg: User defined arguments
111         @return: Dictionary of values.
112         """
113         res2 = {}
114         for statement in self.browse(cr, uid, ids, context=context):
115             encoding_total=0.0
116             for line in statement.line_ids:
117                encoding_total += line.amount
118             res2[statement.id] = encoding_total
119         return res2
120
121     def _end_balance(self, cursor, user, ids, name, attr, context=None):
122         res_currency_obj = self.pool.get('res.currency')
123         res_users_obj = self.pool.get('res.users')
124         res = {}
125
126         company_currency_id = res_users_obj.browse(cursor, user, user,
127                 context=context).company_id.currency_id.id
128
129         statements = self.browse(cursor, user, ids, context=context)
130         for statement in statements:
131             res[statement.id] = statement.balance_start
132             currency_id = statement.currency.id
133             for line in statement.move_line_ids:
134                 if line.debit > 0:
135                     if line.account_id.id == \
136                             statement.journal_id.default_debit_account_id.id:
137                         res[statement.id] += res_currency_obj.compute(cursor,
138                                 user, company_currency_id, currency_id,
139                                 line.debit, context=context)
140                 else:
141                     if line.account_id.id == \
142                             statement.journal_id.default_credit_account_id.id:
143                         res[statement.id] -= res_currency_obj.compute(cursor,
144                                 user, company_currency_id, currency_id,
145                                 line.credit, context=context)
146
147             if statement.state in ('draft', 'open'):
148                 for line in statement.line_ids:
149                     res[statement.id] += line.amount
150         for r in res:
151             res[r] = round(res[r], 2)
152         return res
153
154     def _get_company(self, cr, uid, context=None):
155         user_pool = self.pool.get('res.users')
156         company_pool = self.pool.get('res.company')
157         user = user_pool.browse(cr, uid, uid, context=context)
158         company_id = user.company_id
159         if not company_id:
160             company_id = company_pool.search(cr, uid, [])
161         return company_id and company_id[0] or False
162
163     def _get_cash_open_box_lines(self, cr, uid, context=None):
164         res = []
165         curr = [1, 2, 5, 10, 20, 50, 100, 500]
166         for rs in curr:
167             dct = {
168                 'pieces': rs,
169                 'number': 0
170             }
171             res.append(dct)
172         journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'cash')], context=context)
173         if journal_ids:
174             results = self.search(cr, uid, [('journal_id', 'in', journal_ids),('state', '=', 'confirm')], context=context)
175             if results:
176                 cash_st = self.browse(cr, uid, results, context=context)[0]
177                 for cash_line in cash_st.ending_details_ids:
178                     for r in res:
179                         if cash_line.pieces == r['pieces']:
180                             r['number'] = cash_line.number
181         return res
182
183     def _get_default_cash_close_box_lines(self, cr, uid, context=None):
184         res = []
185         curr = [1, 2, 5, 10, 20, 50, 100, 500]
186         for rs in curr:
187             dct = {
188                 'pieces': rs,
189                 'number': 0
190             }
191             res.append(dct)
192         return res
193
194     def _get_cash_close_box_lines(self, cr, uid, context=None):
195         res = []
196         curr = [1, 2, 5, 10, 20, 50, 100, 500]
197         for rs in curr:
198             dct = {
199                 'pieces': rs,
200                 'number': 0
201             }
202             res.append((0, 0, dct))
203         return res
204
205     def _get_cash_open_close_box_lines(self, cr, uid, context=None):
206         res = {}
207         start_l = []
208         end_l = []
209         starting_details = self._get_cash_open_box_lines(cr, uid, context=context)
210         ending_details = self._get_default_cash_close_box_lines(cr, uid, context)
211         for start in starting_details:
212             start_l.append((0, 0, start))
213         for end in ending_details:
214             end_l.append((0, 0, end))
215         res['start'] = start_l
216         res['end'] = end_l
217         return res
218
219     _columns = {
220         'balance_end_real': fields.float('Closing Balance', digits_compute=dp.get_precision('Account'), states={'confirm': [('readonly', True)]}, help="closing balance entered by the cashbox verifier"),
221         'state': fields.selection(
222             [('draft', 'Draft'),
223             ('confirm', 'Closed'),
224             ('open','Open')], 'State', required=True, states={'confirm': [('readonly', True)]}, readonly="1"),
225         'total_entry_encoding': fields.function(_get_sum_entry_encoding, method=True, store=True, string="Cash Transaction", help="Total cash transactions"),
226         'closing_date': fields.datetime("Closed On"),
227         'balance_end': fields.function(_end_balance, method=True, store=True, string='Balance', help="Closing balance based on Starting Balance and Cash Transactions"),
228         'balance_end_cash': fields.function(_balance_end_cash, method=True, store=True, string='Balance', help="Closing balance based on cashBox"),
229         'starting_details_ids': fields.one2many('account.cashbox.line', 'starting_id', string='Opening Cashbox'),
230         'ending_details_ids': fields.one2many('account.cashbox.line', 'ending_id', string='Closing Cashbox'),
231         'name': fields.char('Name', size=64, required=True, states={'draft': [('readonly', False)]}, readonly=True, help='if you give the Name other then /, its created Accounting Entries Move will be with same name as statement name. This allows the statement entries to have the same references than the statement itself'),
232         'user_id': fields.many2one('res.users', 'Responsible', required=False),
233     }
234     _defaults = {
235         'state': 'draft',
236         'date': lambda *a: time.strftime("%Y-%m-%d %H:%M:%S"),
237         'user_id': lambda self, cr, uid, context=None: uid,
238         'starting_details_ids': _get_cash_open_box_lines,
239         'ending_details_ids': _get_default_cash_close_box_lines
240      }
241
242     def create(self, cr, uid, vals, context=None):
243         sql = [
244                 ('journal_id', '=', vals.get('journal_id', False)),
245                 ('state', '=', 'open')
246         ]
247         open_jrnl = self.search(cr, uid, sql)
248         if open_jrnl:
249             raise osv.except_osv(_('Error'), _('You can not have two open register for the same journal'))
250
251         if self.pool.get('account.journal').browse(cr, uid, vals['journal_id'], context=context).type == 'cash':
252             open_close = self._get_cash_open_close_box_lines(cr, uid, context)
253             if vals.get('starting_details_ids', False):
254                 for start in vals.get('starting_details_ids'):
255                     dict_val = start[2]
256                     for end in open_close['end']:
257                        if end[2]['pieces'] == dict_val['pieces']:
258                            end[2]['number'] += dict_val['number']
259             vals.update({
260 #                'ending_details_ids': open_close['start'],
261                 'starting_details_ids': open_close['end']
262             })
263         else:
264             vals.update({
265                 'ending_details_ids': False,
266                 'starting_details_ids': False
267             })
268         res_id = super(account_cash_statement, self).create(cr, uid, vals, context=context)
269         self.write(cr, uid, [res_id], {})
270         return res_id
271
272     def write(self, cr, uid, ids, vals, context=None):
273         """
274         Update redord(s) comes in {ids}, with new value comes as {vals}
275         return True on success, False otherwise
276
277         @param cr: cursor to database
278         @param user: id of current user
279         @param ids: list of record ids to be update
280         @param vals: dict of new values to be set
281         @param context: context arguments, like lang, time zone
282
283         @return: True on success, False otherwise
284         """
285
286         super(account_cash_statement, self).write(cr, uid, ids, vals, context=context)
287         res = self._get_starting_balance(cr, uid, ids)
288         for rs in res:
289             super(account_cash_statement, self).write(cr, uid, [rs], res.get(rs))
290         return True
291
292     def onchange_journal_id(self, cr, uid, statement_id, journal_id, context=None):
293         """ Changes balance start and starting details if journal_id changes"
294         @param statement_id: Changed statement_id
295         @param journal_id: Changed journal_id
296         @return:  Dictionary of changed values
297         """
298         res = {}
299         balance_start = 0.0
300         if not journal_id:
301             res.update({
302                 'balance_start': balance_start
303             })
304             return res
305         return super(account_cash_statement, self).onchange_journal_id(cr, uid, statement_id, journal_id, context=context)
306
307     def _equal_balance(self, cr, uid, cash_id, context=None):
308         statement = self.browse(cr, uid, cash_id, context=context)
309         self.write(cr, uid, [cash_id], {'balance_end_real': statement.balance_end})
310         statement.balance_end_real = statement.balance_end
311         if statement.balance_end != statement.balance_end_cash:
312             return False
313         return True
314
315     def _user_allow(self, cr, uid, statement_id, context=None):
316         return True
317
318     def button_open(self, cr, uid, ids, context=None):
319         """ Changes statement state to Running.
320         @return: True
321         """
322         if context is None:
323             context = {}
324         statement_pool = self.pool.get('account.bank.statement')
325         for statement in statement_pool.browse(cr, uid, ids, context=context):
326             vals = {}
327             if not self._user_allow(cr, uid, statement.id, context=context):
328                 raise osv.except_osv(_('Error !'), (_('User %s does not have rights to access %s journal !') % (statement.user_id.name, statement.journal_id.name)))
329
330             if statement.name and statement.name == '/':
331                 number = self.pool.get('ir.sequence').get(cr, uid, 'account.cash.statement')
332                 vals.update({
333                     'name': number
334                 })
335
336             vals.update({
337                 'date': time.strftime("%Y-%m-%d %H:%M:%S"),
338                 'state': 'open',
339
340             })
341             self.write(cr, uid, [statement.id], vals, context=context)
342         return True
343
344     def balance_check(self, cr, uid, cash_id, journal_type='bank', context=None):
345         if journal_type == 'bank':
346             return super(account_cash_statement, self).balance_check(cr, uid, cash_id, journal_type, context)
347         if not self._equal_balance(cr, uid, cash_id, context):
348             raise osv.except_osv(_('Error !'), _('CashBox Balance is not matching with Calculated Balance !'))
349         return True
350
351     def statement_close(self, cr, uid, ids, journal_type='bank', context=None):
352         if journal_type == 'bank':
353             return super(account_cash_statement, self).statement_close(cr, uid, ids, journal_type, context)
354         vals = {
355             'state':'confirm',
356             'closing_date': time.strftime("%Y-%m-%d %H:%M:%S")
357         }
358         return self.write(cr, uid, ids, vals, context=context)
359
360     def check_status_condition(self, cr, uid, state, journal_type='bank'):
361         if journal_type == 'bank':
362             return super(account_cash_statement, self).check_status_condition(cr, uid, state, journal_type)
363         return state=='open'
364
365     def button_confirm_cash(self, cr, uid, ids, context=None):
366         super(account_cash_statement, self).button_confirm_bank(cr, uid, ids, context=context)
367         return self.write(cr, uid, ids, {'closing_date': time.strftime("%Y-%m-%d %H:%M:%S")}, context=context)
368
369     def button_cancel(self, cr, uid, ids, context=None):
370         cash_box_line_pool = self.pool.get('account.cashbox.line')
371         super(account_cash_statement, self).button_cancel(cr, uid, ids, context=context)
372         for st in self.browse(cr, uid, ids, context):
373             for end in st.ending_details_ids:
374                 cash_box_line_pool.write(cr, uid, [end.id], {'number': 0})
375         return True
376
377 account_cash_statement()
378
379 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: