[TYPO] Set the right category for the Point Of Sale
[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 = 'pieces'
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] = {
47                 'subtotal_opening' : obj.pieces * obj.number_opening,
48                 'subtotal_closing' : obj.pieces * obj.number_closing,
49             }
50         return res
51
52     def on_change_sub_opening(self, cr, uid, ids, pieces, number, *a):
53         """ Compute the subtotal for the opening """
54         return {'value' : {'subtotal_opening' : (pieces * number) or 0.0 }}
55
56     def on_change_sub_closing(self, cr, uid, ids, pieces, number, *a):
57         """ Compute the subtotal for the closing """
58         return {'value' : {'subtotal_closing' : (pieces * number) or 0.0 }}
59
60     _columns = {
61         'pieces': fields.float('Unit of Currency', digits_compute=dp.get_precision('Account')),
62         'number_opening' : fields.integer('Number of Units', help='Opening Unit Numbers'),
63         'number_closing' : fields.integer('Number of Units', help='Closing Unit Numbers'),
64         'subtotal_opening': fields.function(_sub_total, string='Opening Subtotal', type='float', digits_compute=dp.get_precision('Account'), multi='subtotal'),
65         'subtotal_closing': fields.function(_sub_total, string='Closing Subtotal', type='float', digits_compute=dp.get_precision('Account'), multi='subtotal'),
66         'bank_statement_id' : fields.many2one('account.bank.statement', ondelete='cascade'),
67      }
68
69 account_cashbox_line()
70
71 class account_cash_statement(osv.osv):
72
73     _inherit = 'account.bank.statement'
74
75     def _update_balances(self, cr, uid, ids, context=None):
76         """
77             Set starting and ending balances according to pieces count
78         """
79         res = {}
80         for statement in self.browse(cr, uid, ids, context=context):
81             if statement.journal_id.type not in ('cash',):
82                 continue
83             start = end = 0
84             for line in statement.details_ids:
85                 start += line.subtotal_opening
86                 end += line.subtotal_closing
87             data = {
88                 'balance_start': start,
89                 'balance_end_real': end,
90             }
91             res[statement.id] = data
92             super(account_cash_statement, self).write(cr, uid, [statement.id], data, context=context)
93         return res
94
95     def _get_sum_entry_encoding(self, cr, uid, ids, name, arg, context=None):
96
97         """ Find encoding total of statements "
98         @param name: Names of fields.
99         @param arg: User defined arguments
100         @return: Dictionary of values.
101         """
102         res = {}
103         for statement in self.browse(cr, uid, ids, context=context):
104             res[statement.id] = sum((line.amount for line in statement.line_ids), 0.0)
105         return res
106
107     def _get_company(self, cr, uid, context=None):
108         user_pool = self.pool.get('res.users')
109         company_pool = self.pool.get('res.company')
110         user = user_pool.browse(cr, uid, uid, context=context)
111         company_id = user.company_id
112         if not company_id:
113             company_id = company_pool.search(cr, uid, [])
114         return company_id and company_id[0] or False
115
116     def _get_statement_from_line(self, cr, uid, ids, context=None):
117         result = {}
118         for line in self.pool.get('account.bank.statement.line').browse(cr, uid, ids, context=context):
119             result[line.statement_id.id] = True
120         return result.keys()
121
122     def _compute_difference(self, cr, uid, ids, fieldnames, args, context=None):
123         result =  dict.fromkeys(ids, 0.0)
124
125         for obj in self.browse(cr, uid, ids, context=context):
126             result[obj.id] = obj.balance_end_real - obj.balance_end
127
128         return result
129
130     def _compute_last_closing_balance(self, cr, uid, ids, fieldnames, args, context=None):
131         result = dict.fromkeys(ids, 0.0)
132
133         for obj in self.browse(cr, uid, ids, context=context):
134             if obj.state == 'draft':
135                 statement_ids = self.search(cr, uid,
136                     [('journal_id', '=', obj.journal_id.id),('state', '=', 'confirm')],
137                     order='create_date desc',
138                     limit=1,
139                     context=context
140                 )
141
142                 if not statement_ids:
143                     continue
144                 else:
145                     st = self.browse(cr, uid, statement_ids[0], context=context)
146                     result[obj.id] = st.balance_end_real
147
148         return result
149
150     def onchange_journal_id(self, cr, uid, ids, journal_id, context=None):
151         result = super(account_cash_statement, self).onchange_journal_id(cr, uid, ids, journal_id)
152
153         if not journal_id:
154             return result
155
156         statement_ids = self.search(cr, uid,
157                 [('journal_id', '=', journal_id),('state', '=', 'confirm')],
158                 order='create_date desc',
159                 limit=1,
160                 context=context
161         )
162
163         if not statement_ids:
164             return result
165
166         st = self.browse(cr, uid, statement_ids[0], context=context)
167         result.setdefault('value', {}).update({'last_closing_balance' : st.balance_end_real})
168
169         return result
170
171     _columns = {
172         'total_entry_encoding': fields.function(_get_sum_entry_encoding, string="Total Cash Transactions",
173             store = {
174                 'account.bank.statement': (lambda self, cr, uid, ids, context=None: ids, ['line_ids','move_line_ids'], 10),
175                 'account.bank.statement.line': (_get_statement_from_line, ['amount'], 10),
176             }),
177         'closing_date': fields.datetime("Closed On"),
178         'details_ids' : fields.one2many('account.cashbox.line', 'bank_statement_id', string='CashBox Lines'),
179         'opening_details_ids' : fields.one2many('account.cashbox.line', 'bank_statement_id', string='Opening Cashbox Lines'),
180         'closing_details_ids' : fields.one2many('account.cashbox.line', 'bank_statement_id', string='Closing Cashbox Lines'),
181         'user_id': fields.many2one('res.users', 'Responsible', required=False),
182         'difference' : fields.function(_compute_difference, method=True, string="Difference", type="float"),
183         'last_closing_balance' : fields.function(_compute_last_closing_balance, method=True, string='Last Closing Balance', type='float'),
184     }
185     _defaults = {
186         'state': 'draft',
187         'date': lambda self, cr, uid, context={}: context.get('date', time.strftime("%Y-%m-%d %H:%M:%S")),
188         'user_id': lambda self, cr, uid, context=None: uid,
189     }
190
191     def create(self, cr, uid, vals, context=None):
192         journal = False
193         if vals.get('journal_id'):
194             journal = self.pool.get('account.journal').browse(cr, uid, vals['journal_id'], context=context)
195         if journal and (journal.type == 'cash') and not vals.get('details_ids'):
196             vals['details_ids'] = []
197             for value in journal.cashbox_line_ids:
198                 nested_values = {
199                     'number_closing' : 0,
200                     'number_opening' : 0,
201                     'pieces' : value.pieces
202                 }
203                 vals['details_ids'].append([0, False, nested_values])
204
205         res_id = super(account_cash_statement, self).create(cr, uid, vals, context=context)
206         self._update_balances(cr, uid, [res_id], context)
207         return res_id
208
209     def write(self, cr, uid, ids, vals, context=None):
210         """
211         Update redord(s) comes in {ids}, with new value comes as {vals}
212         return True on success, False otherwise
213
214         @param cr: cursor to database
215         @param user: id of current user
216         @param ids: list of record ids to be update
217         @param vals: dict of new values to be set
218         @param context: context arguments, like lang, time zone
219
220         @return: True on success, False otherwise
221         """
222
223         res = super(account_cash_statement, self).write(cr, uid, ids, vals, context=context)
224         self._update_balances(cr, uid, ids, context)
225         return res
226
227     def _user_allow(self, cr, uid, statement_id, context=None):
228         return True
229
230     def button_open(self, cr, uid, ids, context=None):
231         """ Changes statement state to Running.
232         @return: True
233         """
234         obj_seq = self.pool.get('ir.sequence')
235         if context is None:
236             context = {}
237         statement_pool = self.pool.get('account.bank.statement')
238         for statement in statement_pool.browse(cr, uid, ids, context=context):
239             vals = {}
240             if not self._user_allow(cr, uid, statement.id, context=context):
241                 raise osv.except_osv(_('Error !'), (_('You do not have rights to open this %s journal !') % (statement.journal_id.name, )))
242
243             if statement.name and statement.name == '/':
244                 c = {'fiscalyear_id': statement.period_id.fiscalyear_id.id}
245                 if statement.journal_id.sequence_id:
246                     st_number = obj_seq.next_by_id(cr, uid, statement.journal_id.sequence_id.id, context=c)
247                 else:
248                     st_number = obj_seq.next_by_code(cr, uid, 'account.cash.statement', context=c)
249                 vals.update({
250                     'name': st_number
251                 })
252
253             vals.update({
254                 'state': 'open',
255             })
256             self.write(cr, uid, [statement.id], vals, context=context)
257         return True
258
259     def statement_close(self, cr, uid, ids, journal_type='bank', context=None):
260         if journal_type == 'bank':
261             return super(account_cash_statement, self).statement_close(cr, uid, ids, journal_type, context)
262         vals = {
263             'state':'confirm',
264             'closing_date': time.strftime("%Y-%m-%d %H:%M:%S")
265         }
266         return self.write(cr, uid, ids, vals, context=context)
267
268     def check_status_condition(self, cr, uid, state, journal_type='bank'):
269         if journal_type == 'bank':
270             return super(account_cash_statement, self).check_status_condition(cr, uid, state, journal_type)
271         return state=='open'
272
273     def button_confirm_cash(self, cr, uid, ids, context=None):
274         super(account_cash_statement, self).button_confirm_bank(cr, uid, ids, context=context)
275         absl_proxy = self.pool.get('account.bank.statement.line')
276
277         TABLES = (('Profit', 'profit_account_id'), ('Loss', 'loss_account_id'),)
278
279         for obj in self.browse(cr, uid, ids, context=context):
280             if obj.difference == 0.0:
281                 continue
282
283             for item_label, item_account in TALBES:
284                 if getattr(obj.journal_id, item_account):
285                     raise osv.except_osv(_('Error !'), 
286                                          _('There is no %s Account on the Journal %s') % (item_label, obj.journal_id.name,))
287
288             is_profit = obj.difference < 0.0
289
290             account = getattr(obj.journal_id, TABLES[is_profit][1])
291
292             values = {
293                 'statement_id' : obj.id,
294                 'journal_id' : obj.journal_id.id,
295                 'account_id' : account.id,
296                 'amount' : obj.difference,
297                 'name' : 'Exceptional %s' % TABLES[is_profit][0],
298             }
299
300             absl_proxy.create(cr, uid, values, context=context)
301
302         return self.write(cr, uid, ids, {'closing_date': time.strftime("%Y-%m-%d %H:%M:%S")}, context=context)
303
304 account_cash_statement()
305
306 class account_journal(osv.osv):
307     _inherit = 'account.journal'
308
309     def _default_cashbox_line_ids(self, cr, uid, context=None):
310         # Return a list of coins in Euros.
311         result = [
312             dict(pieces=value) for value in [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500]
313         ]
314         return result
315
316     _columns = {
317         'cashbox_line_ids' : fields.one2many('account.journal.cashbox.line', 'journal_id', 'CashBox'),
318     }
319
320     _defaults = {
321         'cashbox_line_ids' : _default_cashbox_line_ids,
322     }
323
324 account_journal()
325
326 class account_journal_cashbox_line(osv.osv):
327     _name = 'account.journal.cashbox.line'
328     _rec_name = 'pieces'
329     _columns = {
330         'pieces': fields.float('Values', digits_compute=dp.get_precision('Account')),
331         'journal_id' : fields.many2one('account.journal', 'Journal', required=True, select=1),
332     }
333
334     _order = 'pieces asc'
335
336 account_journal_cashbox_line()
337
338 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: