1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# This module copyright (C) 2012 Therp BV (<http://therp.nl>).
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU Affero General Public License as
9
# published by the Free Software Foundation, either version 3 of the
10
# License, or (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Affero General Public License for more details.
17
# You should have received a copy of the GNU Affero General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
##############################################################################
22
from openerp.osv import osv, fields
23
from openerp.tools.translate import _
24
from openerp.addons.decimal_precision import decimal_precision as dp
27
class instant_voucher(osv.TransientModel):
28
_name = 'account.voucher.instant'
29
_description = 'Instant Voucher'
31
def cancel(self, cr, uid, ids, context=None):
33
Delete the voucher and close window
35
assert len(ids) == 1, "Will only take one resource id"
36
instant = self.browse(cr, uid, ids[0], context=context)
37
if instant.voucher_id:
38
self.pool.get('account.voucher').cancel_voucher(
39
cr, uid, [instant.voucher_id.id], context=context)
40
self.pool.get('account.voucher').unlink(
41
cr, uid, [instant.voucher_id.id], context=context)
42
return {'type': 'ir.actions.act_window_close'}
44
def get_voucher_defaults(
45
self, cr, uid, vals, context=None):
47
Gather conditional defaults based on given key, value pairs
49
:param vals: dictionary of key, value pairs
50
:returns: dictionary of default values for fields not in vals
52
values_pool = self.pool.get('ir.values')
53
voucher_pool = self.pool.get('account.voucher')
55
for (key, val) in vals.iteritems():
56
if val and voucher_pool._all_columns[key].column.change_default:
57
for default in values_pool.get_defaults(
58
cr, uid, 'account.voucher', '%s=%s' % (key, val)):
59
if default[1] not in vals:
60
res[default[1]] = default[2]
63
def create_voucher(self, cr, uid, ids, context=None):
65
Create a fully fledged voucher counterpart for the
66
statement line. User only needs to process taxes and may
67
adapt cost/income account.
69
assert len(ids) == 1, "Will only take one resource id"
70
voucher_pool = self.pool.get('account.voucher')
71
period_pool = self.pool.get('account.period')
72
instant = self.browse(cr, uid, ids[0], context=context)
73
line = instant.statement_line_id
74
voucher_type = line.amount < 0 and 'purchase' or 'sale'
75
journal_ids = self.pool.get('account.journal').search(
76
cr, uid, [('company_id', '=', line.company_id.id),
77
('type', '=', voucher_type)])
81
_('No %s journal defined') % voucher_type)
83
journal = self.pool.get('account.journal').browse(
84
cr, uid, journal_ids[0], context=context)
85
if journal.type in ('sale', 'sale_refund'):
86
line_account_id = (journal.default_credit_account_id and
87
journal.default_credit_account_id.id or False)
88
elif journal.type in ('purchase', 'expense', 'purchase_refund'):
89
line_account_id = (journal.default_debit_account_id and
90
journal.default_debit_account_id.id or False)
92
'name': _('Voucher for statement line %s.%s') % (line.statement_id.name, line.name),
93
'reference': line.ref or False,
94
'company_id': line.company_id.id,
95
'partner_id': instant.partner_id.id,
96
'date': line.date or res.get('line.date', False),
97
'account_id': line.account_id.id,
99
'line_ids': [(0, 0, {'amount': abs(line.amount),
100
'account_id': line_account_id,
101
'type': line.amount < 0 and 'dr' or 'cr',
102
'name': line.ref or False,
104
'amount': line.amount and abs(line.amount) or res.get('amount', False),
105
'journal_id': journal_ids[0],
108
period_ids = period_pool.find(cr, uid, vals['date'], context=context)
110
vals['period_id'] = period_ids[0]
111
vals.update(self.get_voucher_defaults(cr, uid, vals, context=context))
113
voucher_id = voucher_pool.create(
114
cr, uid, vals, context=context)
117
{'voucher_id': voucher_id,
119
'type': voucher_type,
122
'name': self._description,
125
'res_model': self._name,
128
'type': 'ir.actions.act_window',
134
def dummy(self, cr, uid, ids, context=None):
136
'name': self._description,
139
'res_model': self._name,
142
'type': 'ir.actions.act_window',
148
def default_get(self, cr, uid, fields_list, context=None):
150
Gather sane default values from the originating statement line
152
res = super(instant_voucher, self).default_get(
153
cr, uid, fields_list, context=context)
154
if 'statement_line_id' in fields_list:
155
res['statement_line_id'] = (
156
context.get('active_id') or
157
context.get('active_ids') and context.get('active_ids')[0])
158
if not res['statement_line_id']:
159
raise osv.except_osv(
161
_('Cannot determine statement line'))
162
line = self.pool.get('account.bank.statement.line').browse(
163
cr, uid, res['statement_line_id'], context=context)
164
if 'balance' in fields_list:
165
res['balance'] = line.amount
166
if 'ref' in fields_list:
167
res['ref'] = line.ref
168
if 'partner_id' in fields_list:
170
res['partner_id'] = line.partner_id.id
173
def _get_balance(self, cr, uid, ids, field_name, args, context=None):
175
Compute the expected residual
176
TODO: currency conversion
179
for instant in self.browse(cr, uid, ids, context=context):
180
if instant.voucher_id and instant.voucher_id.state == 'posted':
181
amount = instant.statement_line_id.amount
183
for line in instant.voucher_id.move_ids:
184
if line.account_id.id == instant.statement_line_id.account_id.id:
185
counteramount = line.debit - line.credit
186
for line in instant.voucher_id.move_ids:
187
if line.account_id.id == instant.statement_line_id.account_id.id:
188
counteramount = line.debit - line.credit
190
amount = abs(instant.statement_line_id.amount)
191
counteramount = abs(instant.voucher_id and instant.voucher_id.amount or 0.0)
192
res[instant.id] = amount - counteramount
195
def confirm(self, cr, uid, ids, context=None):
197
Post the voucher if necessary
198
Post the voucher's move lines if necessary
199
Sanity checks on currency and residual = 0.0
201
If the account_banking module is installed, perform matching
202
and reconciliation. If not, the user is left to manual
203
reconciliation of OpenERP.
205
assert len(ids) == 1, "Will only take one resource id"
206
statement_line_obj = self.pool.get('account.bank.statement.line')
207
voucher_obj = self.pool.get('account.voucher')
208
move_obj = self.pool.get('account.move')
209
instant = self.browse(cr, uid, ids[0], context=context)
210
voucher_currency = (instant.voucher_id.currency_id and
211
instant.voucher_id.currency_id or
212
instant.voucher_id.company_id.currency_id)
213
if (instant.statement_line_id.statement_id.currency.id !=
214
voucher_currency.id):
215
raise osv.except_osv(
217
_("Currency on the bank statement line needs to be the "
218
"same as on the voucher. Currency conversion is not yet "
220
if instant.voucher_id.state != 'posted':
221
voucher_obj.proforma_voucher(
222
cr, uid, [instant.voucher_id.id], context=context)
224
if instant.voucher_id.state != 'posted':
225
raise osv.except_osv(
227
_("The voucher could not be posted."))
228
if instant.voucher_id.move_id.state != 'posted':
230
cr, uid, [instant.voucher_id.move_id.id], context=context)
232
if instant.voucher_id.move_id.state != 'posted':
233
raise osv.except_osv(
235
_("The voucher's move line could not be posted."))
236
if not self.pool.get('res.currency').is_zero(
237
cr, uid, voucher_currency, instant.balance):
238
raise osv.except_osv(
240
_("The amount on the bank statement line needs to be the "
241
"same as on the voucher. Write-off is not yet "
243
# Banking Addons integration:
244
# Gather the info needed to match the bank statement line
245
# and trigger its posting and reconciliation.
246
if 'import_transaction_id' in statement_line_obj._columns:
247
if instant.statement_line_id.state == 'confirmed':
248
raise osv.except_osv(
250
_("Cannot match a confirmed statement line"))
251
if not instant.statement_line_id.import_transaction_id:
252
statement_line_obj.create_instant_transaction(
253
cr, uid, instant.statement_line_id.id, context=context)
254
instant.statement_line_id.refresh()
255
for line in instant.voucher_id.move_ids:
256
if line.account_id.id == instant.statement_line_id.account_id.id:
257
self.pool.get('banking.import.transaction').write(
258
cr, uid, instant.statement_line_id.import_transaction_id.id,
260
'move_line_id': line.id,
261
'move_line_ids': [(6, 0, [line.id])],
262
'match_type': 'move',
264
'invoice_ids': [(6, 0, [])],
267
statement_line_obj.confirm(
268
cr, uid, [instant.statement_line_id.id], context=context)
270
return {'type': 'ir.actions.act_window_close'}
273
'balance': fields.function(
276
digits_compute=dp.get_precision('Account'),
278
'partner_id': fields.many2one(
282
'statement_line_id': fields.many2one(
283
'account.bank.statement.line',
284
'Bank statement line',
286
'ref': fields.related(
287
'statement_line_id', 'ref',
288
type="char", size="48",
291
'voucher_id': fields.many2one(
295
'state': fields.selection(
298
('confirm', 'confirm')],
300
'type': fields.selection(
302
('purchase', 'Purchase')],
306
_defaults = {'state': 'init'}