165
183
class payment_line(osv.osv):
166
184
_inherit = 'payment.line'
168
def debit_storno(self, cr, uid, payment_line_id, storno_move_line_id, context=None):
170
Process a payment line from a direct debit order which has
171
been canceled by the bank or by the user:
172
- Undo the reconciliation of the payment line with the move
173
line that it originated from, and re-reconciliated with
174
the credit payment in the bank journal of the same amount and
176
- Mark the payment line for being reversed.
178
:param payment_line_id: the single id of the canceled payment line
179
:param storno_move_line_id: the credit payment in the bank journal
182
if isinstance(payment_line_id, (list, tuple)):
183
payment_line_id = payment_line_id[0]
186
def debit_storno(self, cr, uid, payment_line_id, amount,
187
currency_id, storno_retry=True, context=None):
189
The processing of a storno is triggered by a debit
190
transfer on one of the company's bank accounts.
191
This method offers to re-reconcile the original debit
192
payment. For this purpose, we have registered that
193
payment move on the payment line.
195
Return the (now incomplete) reconcile id. The caller MUST
196
re-reconcile this reconcile with the bank transfer and
197
re-open the associated invoice.
199
:param payment_line_id: the single payment line id
200
:param amount: the (signed) amount debited from the bank account
201
:param currency_id: the bank account's currency id
202
:param boolean storno_retry: when True, attempt to reopen the invoice, \
203
set the invoice to 'Debit denied' otherwise.
204
:return: an incomplete reconcile for the caller to fill
205
:rtype: database id of an account.move.reconcile resource.
208
move_line_obj = self.pool.get('account.move.line')
184
209
reconcile_obj = self.pool.get('account.move.reconcile')
185
move_line_obj = self.pool.get('account.move.line')
186
payment_line = self.browse(cr, uid, payment_line_id, context=context)
188
debit_move_line = payment_line.debit_move_line_id
189
if (not debit_move_line):
190
raise osv.except_osv(
191
_('Can not process storno'),
192
_('No move line for line %s') % payment_line.name)
193
if payment_line.storno:
194
raise osv.except_osv(
195
_('Can not process storno'),
196
_('Cancelation of payment line \'%s\' has already been ' +
197
'processed') % payment_line.name)
200
return self.pool.get('res.currency').is_zero(
201
cr, uid, debit_move_line.company_id.currency_id, total)
203
# check validity of the proposed move line
204
torec_move_line = move_line_obj.browse(
205
cr, uid, storno_move_line_id, context=context)
206
if not (is_zero(torec_move_line.debit - debit_move_line.debit) and
207
is_zero(torec_move_line.credit - debit_move_line.credit) and
208
torec_move_line.account_id.id == debit_move_line.account_id.id):
209
raise osv.except_osv(
210
_('Can not process storno'),
211
_('%s is not a drop-in replacement for %s') % (
212
torec_move_line.name, debit_move_line.name))
213
if payment_line.storno:
214
raise osv.except_osv(
215
_('Can not process storno'),
216
_('Debit order line %s has already been cancelled') % (
219
# replace move line in reconciliation
210
line = self.browse(cr, uid, payment_line_id)
220
211
reconcile_id = False
221
if (payment_line.move_line_id.reconcile_partial_id and
222
debit_move_line_id.id in
223
payment_line.move_line_id.reconcile_partial_id.line_partial_ids):
224
reconcile_id = payment_line.move_line_id.reconcile_partial_id
227
[(3, debit_move_line_id.id), (4, torec_move_line.id)],
229
elif (payment_line.move_line_id.reconcile_id and
230
debit_move_line_id.id in
231
payment_line.move_line_id.reconcile_id.line_id):
232
reconcile_id = payment_line.move_line_id.reconcile_id
235
[(3, debit_move_line_id.id), (4, torec_move_line.id)]
238
raise osv.except_osv(
239
_('Can not perform storno'),
240
_('Debit order line %s does not occur in the list of '
241
'reconciliation move lines of its origin') %
242
debit_move_line_id.name)
243
reconcile_obj.write(cr, uid, reconcile_id, vals, context=context)
244
self.write(cr, uid, payment_line_id, {'storno': True}, context=context)
245
#for line_id in line_ids:
246
# netsvc.LocalService("workflow").trg_trigger(
247
# uid, 'account.move.line', line_id, cr)
212
if (line.debit_move_line_id and not line.storno and
213
self.pool.get('res.currency').is_zero(
214
cr, uid, currency_id, (
215
(line.debit_move_line_id.credit or 0.0) -
216
(line.debit_move_line_id.debit or 0.0) + amount))):
217
# Two different cases, full and partial
218
# Both cases differ subtly in the procedure to follow
219
# Needs refractoring, but why is this not in the OpenERP API?
220
if line.debit_move_line_id.reconcile_partial_id:
221
reconcile_id = line.debit_move_line_id.reconcile_partial_id.id
222
attribute = 'reconcile_partial_id'
223
if len(line.debit_move_line_id.reconcile_id.line_partial_ids) == 2:
224
# reuse the simple reconcile for the storno transfer
226
cr, uid, reconcile_id, {
227
'line_id': [(6, 0, line.debit_move_line_id.id)],
228
'line_partial_ids': [(6, 0, [])],
231
# split up the original reconcile in a partial one
232
# and a new one for reconciling the storno transfer
234
cr, uid, reconcile_id, {
235
'line_partial_ids': [(3, line.debit_move_line_id.id)],
237
reconcile_id = reconcile_obj.create(
240
'line_id': [(6, 0, line.debit_move_line_id.id)],
242
elif line.debit_move_line_id.reconcile_id:
243
reconcile_id = line.debit_move_line_id.reconcile_id.id
244
if len(line.debit_move_line_id.reconcile_id.line_id) == 2:
245
# reuse the simple reconcile for the storno transfer
247
cr, uid, reconcile_id, {
248
'line_id': [(6, 0, [line.debit_move_line_id.id])]
251
# split up the original reconcile in a partial one
252
# and a new one for reconciling the storno transfer
254
x.id for x in line.debit_move_line_id.reconcile_id.line_id
255
if x.id != line.debit_move_line_id.id
258
cr, uid, reconcile_id, {
259
'line_partial_ids': [(6, 0, partial_ids)],
260
'line_id': [(6, 0, [])],
262
reconcile_id = reconcile_obj.create(
265
'line_id': [(6, 0, line.debit_move_line_id.id)],
267
# mark the payment line for storno processed
269
self.write(cr, uid, [payment_line_id],
270
{'storno': True}, context=context)
271
# put forth the invoice workflow
272
if line.move_line_id.invoice:
273
activity = (storno_retry and 'open_test'
274
or 'invoice_debit_denied')
275
netsvc.LocalService("workflow").trg_validate(
276
uid, 'account.invoice', line.move_line_id.invoice.id,
249
280
def debit_reconcile(self, cr, uid, payment_line_id, context=None):
356
387
payment = self.pool.get('payment.order').browse(cr, uid, context['active_id'], context=context)
357
388
# Search for move line to pay:
358
389
if payment.payment_order_type == 'debit':
359
domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'receivable'), ('amount_to_receive', '>', 0)]
391
('reconcile_id', '=', False),
392
('account_id.type', '=', 'receivable'),
393
('amount_to_receive', '>', 0),
395
# cannot filter on properties of (searchable)
396
# function fields. Needs work in expression.expression.parse()
397
# Currently gives an SQL error.
398
# # apply payment term filter
399
# if payment.mode.payment_term_ids:
400
# term_ids = [term.id for term in payment.mode.payment_term_ids]
401
# domain = domain + [
402
# '|', ('invoice', '=', False),
403
# ('invoice.payment_term', 'in', term_ids),
361
domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_to_pay', '>', 0)]
407
('reconcile_id', '=', False),
408
('account_id.type', '=', 'payable'),
409
('amount_to_pay', '>', 0)
411
if payment.mode.reference_filter:
412
domain.append(('ref', 'ilike', payment.mode.reference_filter))
362
413
# domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_to_pay', '>', 0)]
363
414
### end account_direct_debit ###