2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2008 PC Solutions (<http://pcsol.be>). All Rights Reserved
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
25 from openerp.osv import fields, osv
26 from openerp.tools.translate import _
27 import openerp.addons.decimal_precision as dp
29 class account_cashbox_line(osv.osv):
31 """ Cash Box Details """
33 _name = 'account.cashbox.line'
34 _description = 'CashBox Line'
37 def _sub_total(self, cr, uid, ids, name, arg, context=None):
39 """ Calculates Sub total
40 @param name: Names of fields.
41 @param arg: User defined arguments
42 @return: Dictionary of values.
45 for obj in self.browse(cr, uid, ids, context=context):
47 'subtotal_opening' : obj.pieces * obj.number_opening,
48 'subtotal_closing' : obj.pieces * obj.number_closing,
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 }}
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 }}
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'),
69 account_cashbox_line()
71 class account_cash_statement(osv.osv):
73 _inherit = 'account.bank.statement'
75 def _update_balances(self, cr, uid, ids, context=None):
77 Set starting and ending balances according to pieces count
80 for statement in self.browse(cr, uid, ids, context=context):
81 if (statement.journal_id.type not in ('cash',)) or (not statement.journal_id.cash_control):
84 for line in statement.details_ids:
85 start += line.subtotal_opening
86 end += line.subtotal_closing
88 'balance_start': start,
89 'balance_end_real': end,
91 res[statement.id] = data
92 super(account_cash_statement, self).write(cr, uid, [statement.id], data, context=context)
95 def _get_sum_entry_encoding(self, cr, uid, ids, name, arg, context=None):
97 """ Find encoding total of statements "
98 @param name: Names of fields.
99 @param arg: User defined arguments
100 @return: Dictionary of values.
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)
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
113 company_id = company_pool.search(cr, uid, [])
114 return company_id and company_id[0] or False
116 def _get_statement_from_line(self, cr, uid, ids, context=None):
118 for line in self.pool.get('account.bank.statement.line').browse(cr, uid, ids, context=context):
119 result[line.statement_id.id] = True
122 def _compute_difference(self, cr, uid, ids, fieldnames, args, context=None):
123 result = dict.fromkeys(ids, 0.0)
125 for obj in self.browse(cr, uid, ids, context=context):
126 result[obj.id] = obj.balance_end_real - obj.balance_end
130 def _compute_last_closing_balance(self, cr, uid, ids, fieldnames, args, context=None):
131 result = dict.fromkeys(ids, 0.0)
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',
142 if not statement_ids:
145 st = self.browse(cr, uid, statement_ids[0], context=context)
146 result[obj.id] = st.balance_end_real
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)
156 statement_ids = self.search(cr, uid,
157 [('journal_id', '=', journal_id),('state', '=', 'confirm')],
158 order='create_date desc',
163 if not statement_ids:
166 st = self.browse(cr, uid, statement_ids[0], context=context)
167 result.setdefault('value', {}).update({'last_closing_balance' : st.balance_end_real})
172 'total_entry_encoding': fields.function(_get_sum_entry_encoding, string="Total Transactions",
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),
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'),
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,
191 def create(self, cr, uid, vals, context=None):
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'] = []
200 if journal.with_last_closing_balance == True:
201 domain = [('journal_id', '=', journal.id),
202 ('state', '=', 'confirm')]
203 last_bank_statement_ids = self.search(cr, uid, domain, limit=1, order='create_date desc', context=context)
204 if last_bank_statement_ids:
205 last_bank_statement = self.browse(cr, uid, last_bank_statement_ids[0], context=context)
208 (line.pieces, line.number_closing) for line in last_bank_statement.details_ids
211 for value in journal.cashbox_line_ids:
213 'number_closing' : 0,
214 'number_opening' : last_pieces.get(value.pieces, 0) if isinstance(last_pieces, dict) else 0,
215 'pieces' : value.pieces
218 vals['details_ids'].append([0, False, nested_values])
220 res_id = super(account_cash_statement, self).create(cr, uid, vals, context=context)
221 self._update_balances(cr, uid, [res_id], context)
224 def write(self, cr, uid, ids, vals, context=None):
226 Update redord(s) comes in {ids}, with new value comes as {vals}
227 return True on success, False otherwise
229 @param cr: cursor to database
230 @param user: id of current user
231 @param ids: list of record ids to be update
232 @param vals: dict of new values to be set
233 @param context: context arguments, like lang, time zone
235 @return: True on success, False otherwise
238 res = super(account_cash_statement, self).write(cr, uid, ids, vals, context=context)
239 self._update_balances(cr, uid, ids, context)
242 def _user_allow(self, cr, uid, statement_id, context=None):
245 def button_open(self, cr, uid, ids, context=None):
246 """ Changes statement state to Running.
249 obj_seq = self.pool.get('ir.sequence')
252 statement_pool = self.pool.get('account.bank.statement')
253 for statement in statement_pool.browse(cr, uid, ids, context=context):
255 if not self._user_allow(cr, uid, statement.id, context=context):
256 raise osv.except_osv(_('Error!'), (_('You do not have rights to open this %s journal!') % (statement.journal_id.name, )))
258 if statement.name and statement.name == '/':
259 c = {'fiscalyear_id': statement.period_id.fiscalyear_id.id}
260 if statement.journal_id.sequence_id:
261 st_number = obj_seq.next_by_id(cr, uid, statement.journal_id.sequence_id.id, context=c)
263 st_number = obj_seq.next_by_code(cr, uid, 'account.cash.statement', context=c)
271 self.write(cr, uid, [statement.id], vals, context=context)
274 def statement_close(self, cr, uid, ids, journal_type='bank', context=None):
275 if journal_type == 'bank':
276 return super(account_cash_statement, self).statement_close(cr, uid, ids, journal_type, context)
279 'closing_date': time.strftime("%Y-%m-%d %H:%M:%S")
281 return self.write(cr, uid, ids, vals, context=context)
283 def check_status_condition(self, cr, uid, state, journal_type='bank'):
284 if journal_type == 'bank':
285 return super(account_cash_statement, self).check_status_condition(cr, uid, state, journal_type)
288 def button_confirm_cash(self, cr, uid, ids, context=None):
289 super(account_cash_statement, self).button_confirm_bank(cr, uid, ids, context=context)
290 absl_proxy = self.pool.get('account.bank.statement.line')
292 TABLES = ((_('Profit'), 'profit_account_id'), (_('Loss'), 'loss_account_id'),)
294 for obj in self.browse(cr, uid, ids, context=context):
295 if obj.difference == 0.0:
298 for item_label, item_account in TABLES:
299 if getattr(obj.journal_id, item_account):
300 raise osv.except_osv(_('Error!'),
301 _('There is no %s Account on the journal %s.') % (item_label, obj.journal_id.name,))
303 is_profit = obj.difference < 0.0
305 account = getattr(obj.journal_id, TABLES[is_profit][1])
308 'statement_id' : obj.id,
309 'journal_id' : obj.journal_id.id,
310 'account_id' : account.id,
311 'amount' : obj.difference,
312 'name' : 'Exceptional %s' % TABLES[is_profit][0],
315 absl_proxy.create(cr, uid, values, context=context)
317 return self.write(cr, uid, ids, {'closing_date': time.strftime("%Y-%m-%d %H:%M:%S")}, context=context)
319 account_cash_statement()
321 class account_journal(osv.osv):
322 _inherit = 'account.journal'
324 def _default_cashbox_line_ids(self, cr, uid, context=None):
325 # Return a list of coins in Euros.
327 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]
332 'cashbox_line_ids' : fields.one2many('account.journal.cashbox.line', 'journal_id', 'CashBox'),
336 'cashbox_line_ids' : _default_cashbox_line_ids,
341 class account_journal_cashbox_line(osv.osv):
342 _name = 'account.journal.cashbox.line'
345 'pieces': fields.float('Values', digits_compute=dp.get_precision('Account')),
346 'journal_id' : fields.many2one('account.journal', 'Journal', required=True, select=1, ondelete="cascade"),
349 _order = 'pieces asc'
351 account_journal_cashbox_line()
353 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: