[MERGE] lp881356
[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     _rec_name = 'number'
36
37     def _sub_total(self, cr, uid, ids, name, arg, context=None):
38
39         """ Calculates Sub total
40         @param name: Names of fields.
41         @param arg: User defined arguments
42         @return: Dictionary of values.
43         """
44         res = {}
45         for obj in self.browse(cr, uid, ids, context=context):
46             res[obj.id] = obj.pieces * obj.number
47         return res
48
49     def on_change_sub(self, cr, uid, ids, pieces, number, *a):
50
51         """ Calculates Sub total on change of number
52         @param pieces: Names of fields.
53         @param number:
54         """
55         sub = pieces * number
56         return {'value': {'subtotal': sub or 0.0}}
57
58     _columns = {
59         'pieces': fields.float('Values', digits_compute=dp.get_precision('Account')),
60         'number': fields.integer('Number'),
61         'subtotal': fields.function(_sub_total, string='Sub Total', type='float', digits_compute=dp.get_precision('Account')),
62         'starting_id': fields.many2one('account.bank.statement', ondelete='cascade'),
63         'ending_id': fields.many2one('account.bank.statement', ondelete='cascade'),
64      }
65
66 account_cashbox_line()
67
68 class account_cash_statement(osv.osv):
69
70     _inherit = 'account.bank.statement'
71
72     def _get_starting_balance(self, cr, uid, ids, context=None):
73
74         """ Find starting balance
75         @param name: Names of fields.
76         @param arg: User defined arguments
77         @return: Dictionary of values.
78         """
79         res = {}
80         for statement in self.browse(cr, uid, ids, context=context):
81             amount_total = 0.0
82
83             if statement.journal_id.type not in('cash'):
84                 continue
85
86             for line in statement.starting_details_ids:
87                 amount_total+= line.pieces * line.number
88             res[statement.id] = {
89                 'balance_start': amount_total
90             }
91         return res
92
93     def _balance_end_cash(self, cr, uid, ids, name, arg, context=None):
94         """ Find ending balance  "
95         @param name: Names of fields.
96         @param arg: User defined arguments
97         @return: Dictionary of values.
98         """
99         res = {}
100         for statement in self.browse(cr, uid, ids, context=context):
101             amount_total = 0.0
102             for line in statement.ending_details_ids:
103                 amount_total += line.pieces * line.number
104             res[statement.id] = amount_total
105         return res
106
107     def _get_sum_entry_encoding(self, cr, uid, ids, name, arg, context=None):
108
109         """ Find encoding total of statements "
110         @param name: Names of fields.
111         @param arg: User defined arguments
112         @return: Dictionary of values.
113         """
114         res2 = {}
115         for statement in self.browse(cr, uid, ids, context=context):
116             encoding_total=0.0
117             for line in statement.line_ids:
118                encoding_total += line.amount
119             res2[statement.id] = encoding_total
120         return res2
121
122     def _get_company(self, cr, uid, context=None):
123         user_pool = self.pool.get('res.users')
124         company_pool = self.pool.get('res.company')
125         user = user_pool.browse(cr, uid, uid, context=context)
126         company_id = user.company_id
127         if not company_id:
128             company_id = company_pool.search(cr, uid, [])
129         return company_id and company_id[0] or False
130
131     def _get_cash_open_box_lines(self, cr, uid, context=None):
132         res = []
133         curr = [1, 2, 5, 10, 20, 50, 100, 500]
134         for rs in curr:
135             dct = {
136                 'pieces': rs,
137                 'number': 0
138             }
139             res.append(dct)
140         journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'cash')], context=context)
141         if journal_ids:
142             results = self.search(cr, uid, [('journal_id', 'in', journal_ids),('state', '=', 'confirm')], context=context)
143             if results:
144                 cash_st = self.browse(cr, uid, results, context=context)[0]
145                 for cash_line in cash_st.ending_details_ids:
146                     for r in res:
147                         if cash_line.pieces == r['pieces']:
148                             r['number'] = cash_line.number
149         return res
150
151     def _get_default_cash_close_box_lines(self, cr, uid, context=None):
152         res = []
153         curr = [1, 2, 5, 10, 20, 50, 100, 500]
154         for rs in curr:
155             dct = {
156                 'pieces': rs,
157                 'number': 0
158             }
159             res.append(dct)
160         return res
161
162     def _get_cash_close_box_lines(self, cr, uid, context=None):
163         res = []
164         curr = [1, 2, 5, 10, 20, 50, 100, 500]
165         for rs in curr:
166             dct = {
167                 'pieces': rs,
168                 'number': 0
169             }
170             res.append((0, 0, dct))
171         return res
172
173     def _get_cash_open_close_box_lines(self, cr, uid, context=None):
174         res = {}
175         start_l = []
176         end_l = []
177         starting_details = self._get_cash_open_box_lines(cr, uid, context=context)
178         ending_details = self._get_default_cash_close_box_lines(cr, uid, context)
179         for start in starting_details:
180             start_l.append((0, 0, start))
181         for end in ending_details:
182             end_l.append((0, 0, end))
183         res['start'] = start_l
184         res['end'] = end_l
185         return res
186
187     def _get_statement(self, cr, uid, ids, context=None):
188         result = {}
189         for line in self.pool.get('account.bank.statement.line').browse(cr, uid, ids, context=context):
190             result[line.statement_id.id] = True
191         return result.keys()
192
193     _columns = {
194         'total_entry_encoding': fields.function(_get_sum_entry_encoding, string="Cash Transaction", help="Total cash transactions",
195             store = {
196                 'account.bank.statement': (lambda self, cr, uid, ids, c={}: ids, ['line_ids','move_line_ids'], 10),
197                 'account.bank.statement.line': (_get_statement, ['amount'], 10),
198             }),
199         'closing_date': fields.datetime("Closed On"),
200         'balance_end_cash': fields.function(_balance_end_cash, store=True, string='Closing Balance', help="Closing balance based on cashBox"),
201         'starting_details_ids': fields.one2many('account.cashbox.line', 'starting_id', string='Opening Cashbox'),
202         'ending_details_ids': fields.one2many('account.cashbox.line', 'ending_id', string='Closing Cashbox'),
203         'user_id': fields.many2one('res.users', 'Responsible', required=False),
204     }
205     _defaults = {
206         'state': 'draft',
207         'date': lambda self,cr,uid,context={}: context.get('date', time.strftime("%Y-%m-%d %H:%M:%S")),
208         'user_id': lambda self, cr, uid, context=None: uid,
209         'starting_details_ids': _get_cash_open_box_lines,
210         'ending_details_ids': _get_default_cash_close_box_lines
211      }
212
213     def create(self, cr, uid, vals, context=None):
214         if self.pool.get('account.journal').browse(cr, uid, vals['journal_id'], context=context).type == 'cash':
215             open_close = self._get_cash_open_close_box_lines(cr, uid, context)
216             if vals.get('starting_details_ids', False):
217                 for start in vals.get('starting_details_ids'):
218                     dict_val = start[2]
219                     for end in open_close['end']:
220                        if end[2]['pieces'] == dict_val['pieces']:
221                            end[2]['number'] += dict_val['number']
222             vals.update({
223 #                'ending_details_ids': open_close['start'],
224                 'starting_details_ids': open_close['end']
225             })
226         else:
227             vals.update({
228                 'ending_details_ids': False,
229                 'starting_details_ids': False
230             })
231         res_id = super(account_cash_statement, self).create(cr, uid, vals, context=context)
232         self.write(cr, uid, [res_id], {})
233         return res_id
234
235     def write(self, cr, uid, ids, vals, context=None):
236         """
237         Update redord(s) comes in {ids}, with new value comes as {vals}
238         return True on success, False otherwise
239
240         @param cr: cursor to database
241         @param user: id of current user
242         @param ids: list of record ids to be update
243         @param vals: dict of new values to be set
244         @param context: context arguments, like lang, time zone
245
246         @return: True on success, False otherwise
247         """
248
249         super(account_cash_statement, self).write(cr, uid, ids, vals, context=context)
250         res = self._get_starting_balance(cr, uid, ids)
251         for rs in res:
252             super(account_cash_statement, self).write(cr, uid, [rs], res.get(rs))
253         return True
254
255     def onchange_journal_id(self, cr, uid, statement_id, journal_id, context=None):
256         """ Changes balance start and starting details if journal_id changes"
257         @param statement_id: Changed statement_id
258         @param journal_id: Changed journal_id
259         @return:  Dictionary of changed values
260         """
261         res = {}
262         balance_start = 0.0
263         if not journal_id:
264             res.update({
265                 'balance_start': balance_start
266             })
267             return res
268         return super(account_cash_statement, self).onchange_journal_id(cr, uid, statement_id, journal_id, context=context)
269
270     def _equal_balance(self, cr, uid, cash_id, context=None):
271         statement = self.browse(cr, uid, cash_id, context=context)
272         self.write(cr, uid, [cash_id], {'balance_end_real': statement.balance_end})
273         statement.balance_end_real = statement.balance_end
274         if statement.balance_end != statement.balance_end_cash:
275             return False
276         return True
277
278     def _user_allow(self, cr, uid, statement_id, context=None):
279         return True
280
281     def button_open(self, cr, uid, ids, context=None):
282         """ Changes statement state to Running.
283         @return: True
284         """
285         obj_seq = self.pool.get('ir.sequence')
286         if context is None:
287             context = {}
288         statement_pool = self.pool.get('account.bank.statement')
289         for statement in statement_pool.browse(cr, uid, ids, context=context):
290             vals = {}
291             if not self._user_allow(cr, uid, statement.id, context=context):
292                 raise osv.except_osv(_('Error !'), (_('User %s does not have rights to access %s journal !') % (statement.user_id.name, statement.journal_id.name)))
293
294             if statement.name and statement.name == '/':
295                 if statement.journal_id.sequence_id:
296                     c = {'fiscalyear_id': statement.period_id.fiscalyear_id.id}
297                     st_number = obj_seq.next_by_id(cr, uid, statement.journal_id.sequence_id.id, context=c)
298                 else:
299                     st_number = obj_seq.next_by_code(cr, uid, 'account.cash.statement')
300                 vals.update({
301                     'name': st_number
302                 })
303
304             vals.update({
305                 'state': 'open',
306             })
307             self.write(cr, uid, [statement.id], vals, context=context)
308         return True
309
310     def balance_check(self, cr, uid, cash_id, journal_type='bank', context=None):
311         if journal_type == 'bank':
312             return super(account_cash_statement, self).balance_check(cr, uid, cash_id, journal_type, context)
313         if not self._equal_balance(cr, uid, cash_id, context):
314             raise osv.except_osv(_('Error !'), _('The closing balance should be the same than the computed balance!'))
315         return True
316
317     def statement_close(self, cr, uid, ids, journal_type='bank', context=None):
318         if journal_type == 'bank':
319             return super(account_cash_statement, self).statement_close(cr, uid, ids, journal_type, context)
320         vals = {
321             'state':'confirm',
322             'closing_date': time.strftime("%Y-%m-%d %H:%M:%S")
323         }
324         return self.write(cr, uid, ids, vals, context=context)
325
326     def check_status_condition(self, cr, uid, state, journal_type='bank'):
327         if journal_type == 'bank':
328             return super(account_cash_statement, self).check_status_condition(cr, uid, state, journal_type)
329         return state=='open'
330
331     def button_confirm_cash(self, cr, uid, ids, context=None):
332         super(account_cash_statement, self).button_confirm_bank(cr, uid, ids, context=context)
333         return self.write(cr, uid, ids, {'closing_date': time.strftime("%Y-%m-%d %H:%M:%S")}, context=context)
334
335     def button_cancel(self, cr, uid, ids, context=None):
336         cash_box_line_pool = self.pool.get('account.cashbox.line')
337         super(account_cash_statement, self).button_cancel(cr, uid, ids, context=context)
338         for st in self.browse(cr, uid, ids, context):
339             for end in st.ending_details_ids:
340                 cash_box_line_pool.write(cr, uid, [end.id], {'number': 0})
341         return True
342
343 account_cash_statement()
344
345 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: