Merge lp:~therp-nl/banking-addons/6.1-dev-fixes_from_testing_iteration_2 into lp:~banking-addons-team/banking-addons/6.1-dev

Proposed by Stefan Rijnhart (Opener)
Status: Superseded
Proposed branch: lp:~therp-nl/banking-addons/6.1-dev-fixes_from_testing_iteration_2
Merge into: lp:~banking-addons-team/banking-addons/6.1-dev
Diff against target: 1285 lines (+413/-442)
7 files modified
account_banking/account_banking.py (+73/-24)
account_banking/account_banking_view.xml (+1/-1)
account_banking/banking_import_transaction.py (+309/-409)
account_banking/wizard/banking_transaction_wizard.py (+2/-3)
account_direct_debit/model/account_payment.py (+0/-1)
account_direct_debit/view/account_invoice.xml (+2/-2)
account_direct_debit/workflow/account_invoice.xml (+26/-2)
To merge this branch: bzr merge lp:~therp-nl/banking-addons/6.1-dev-fixes_from_testing_iteration_2
Reviewer Review Type Date Requested Status
Stefan Rijnhart (Opener) Needs Resubmitting
James Jesudason Pending
Review via email: mp+104384@code.launchpad.net

This proposal has been superseded by a proposal from 2012-05-03.

Description of the change

This branch should contain the necessary adaptations of the code associated with payment and direct debit orders to work nicely with the new voucher oriented reconciliation mechanism as well as some streamlining of the latter.

It also restores the ability to encode statements manually.

To post a comment you must log in.
132. By Stefan Rijnhart (Opener)

[FIX] Duplicate statement line does not show up red on statement due to
 color precendence
[FIX] Cannot cancel statement line

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Added two more fixes. Otherwise, tested succesfully.

review: Needs Resubmitting

Unmerged revisions

132. By Stefan Rijnhart (Opener)

[FIX] Duplicate statement line does not show up red on statement due to
 color precendence
[FIX] Cannot cancel statement line

131. By Stefan Rijnhart (Opener)

[FIX] Ability to encode statements manually,
 as well as compatibility with point_of_sale module (lp 992141)
[FIX] Post vouchers using workflow
[FIX] Remove redundant move_id fields on statement line.
 Use existing move_ids field instead. This restores
 the ability to cancel legacy (stateless) statement lines.

130. By Stefan Rijnhart (Opener)

[FIX] Selection of bugs and style issues found by pylint

129. By Stefan Rijnhart (Opener)

[RFR] Restore confirm and cancel logic for other match types
[RFR] Adapt direct debit workflow to 6.1

128. By Stefan Rijnhart (Opener)

[MRG] Merged with main development branch

127. By Stefan Rijnhart (Opener)

[ADD] Move voucher logic from statement line to the transaction level
     Preserve legacy methods for canceling reconciliation oriented transactions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'account_banking/account_banking.py'
2--- account_banking/account_banking.py 2012-04-14 09:16:54 +0000
3+++ account_banking/account_banking.py 2012-05-02 14:33:21 +0000
4@@ -58,13 +58,11 @@
5 Rejected payments from the bank receive on import the status 'rejected'.
6 '''
7 import time
8-import sys
9 import sepa
10 from osv import osv, fields
11 from tools.translate import _
12 from wizard.banktools import get_or_create_bank
13 import decimal_precision as dp
14-import pooler
15 import netsvc
16 from openerp import SUPERUSER_ID
17
18@@ -322,6 +320,26 @@
19 # 'currency': _currency,
20 }
21
22+ def _check_company_id(self, cr, uid, ids, context=None):
23+ """
24+ Adapt this constraint method from the account module to reflect the
25+ move of period_id to the statement line
26+ """
27+ for statement in self.browse(cr, uid, ids, context=context):
28+ if (statement.period_id and
29+ statement.company_id.id != statement.period_id.company_id.id):
30+ return False
31+ for line in statement.line_ids:
32+ if (line.period_id and
33+ statement.company_id.id != line.period_id.company_id.id):
34+ return False
35+ return True
36+
37+ # Redefine the constraint, or it still refer to the original method
38+ _constraints = [
39+ (_check_company_id, 'The journal and period chosen have to belong to the same company.', ['journal_id','period_id']),
40+ ]
41+
42 def _get_period(self, cursor, uid, date, context=None):
43 '''
44 Find matching period for date, not meant for _defaults.
45@@ -341,10 +359,12 @@
46 context=None):
47 # This is largely a copy of the original code in account
48 # Modifications are marked with AB
49+ # Modifications by account_voucher are merged below.
50 # As there is no valid inheritance mechanism for large actions, this
51 # is the only option to add functionality to existing actions.
52 # WARNING: when the original code changes, this trigger has to be
53 # updated in sync.
54+
55 if context is None:
56 context = {}
57 res_currency_obj = self.pool.get('res.currency')
58@@ -352,8 +372,36 @@
59 account_move_line_obj = self.pool.get('account.move.line')
60 account_bank_statement_line_obj = self.pool.get(
61 'account.bank.statement.line')
62- st_line = account_bank_statement_line_obj.browse(cr, uid, st_line_id,
63- context=context)
64+ st_line = account_bank_statement_line_obj.browse(
65+ cr, uid, st_line_id, context=context)
66+ # Start account voucher
67+ # Post the voucher and update links between statement and moves
68+ if st_line.voucher_id:
69+ voucher_pool = self.pool.get('account.voucher')
70+ wf_service = netsvc.LocalService("workflow")
71+ voucher_pool.write(
72+ cr, uid, [st_line.voucher_id.id], {'number': st_line_number}, context=context)
73+ if st_line.voucher_id.state == 'cancel':
74+ voucher_pool.action_cancel_draft(
75+ cr, uid, [st_line.voucher_id.id], context=context)
76+ wf_service.trg_validate(
77+ uid, 'account.voucher', st_line.voucher_id.id, 'proforma_voucher', cr)
78+ v = voucher_pool.browse(
79+ cr, uid, st_line.voucher_id.id, context=context)
80+ account_bank_statement_line_obj.write(cr, uid, [st_line_id], {
81+ 'move_ids': [(4, v.move_id.id, False)]
82+ })
83+ account_move_line_obj.write(
84+ cr, uid, [x.id for x in v.move_ids],
85+ {'statement_id': st_line.statement_id.id}, context=context)
86+ # End of account_voucher
87+ st_line.refresh()
88+
89+ # AB: The voucher journal isn't automatically posted, so post it (if needed)
90+ if not st_line.voucher_id.journal_id.entry_posted:
91+ account_move_obj.post(cr, uid, [st_line.voucher_id.move_id.id], context={})
92+ return True
93+
94 st = st_line.statement_id
95
96 context.update({'date': st_line.date})
97@@ -457,22 +505,23 @@
98 # Bank statements will not consider boolean on journal entry_posted
99 account_move_obj.post(cr, uid, [move_id], context=context)
100
101- # Shouldn't now be needed as payment and reconciliation of invoices
102- # is done through account_voucher
103- #"""
104- #Account-banking:
105- #- Write stored reconcile_id
106- #- Pay invoices through workflow
107- #"""
108- #if st_line.reconcile_id:
109- # account_move_line_obj.write(cr, uid, torec, {
110- # (st_line.reconcile_id.line_partial_ids and
111- # 'reconcile_partial_id' or 'reconcile_id'):
112- # st_line.reconcile_id.id }, context=context)
113- # for move_line in (st_line.reconcile_id.line_id or []) + (
114- # st_line.reconcile_id.line_partial_ids or []):
115- # netsvc.LocalService("workflow").trg_trigger(
116- # uid, 'account.move.line', move_line.id, cr)
117+ """
118+ Account-banking:
119+ - Write stored reconcile_id
120+ - Pay invoices through workflow
121+
122+ Does not apply to voucher integration, but only to
123+ payments and payment orders
124+ """
125+ if st_line.reconcile_id:
126+ account_move_line_obj.write(cr, uid, torec, {
127+ (st_line.reconcile_id.line_partial_ids and
128+ 'reconcile_partial_id' or 'reconcile_id'):
129+ st_line.reconcile_id.id }, context=context)
130+ for move_line in (st_line.reconcile_id.line_id or []) + (
131+ st_line.reconcile_id.line_partial_ids or []):
132+ netsvc.LocalService("workflow").trg_trigger(
133+ uid, 'account.move.line', move_line.id, cr)
134 #""" End account-banking """
135
136 return move_id
137@@ -935,7 +984,7 @@
138 Previously (pre-v6) in account_payment/wizard/wizard_pay.py
139 """
140 if context == None:
141- context={}
142+ context = {}
143 result = {}
144 orders = self.browse(cr, uid, ids, context)
145 order = orders[0]
146@@ -1134,7 +1183,7 @@
147 '''
148 Create dual function IBAN account for SEPA countries
149 '''
150- if vals['state'] == 'iban':
151+ if vals.get('state') == 'iban':
152 iban = vals.get('acc_number',False) or vals.get('acc_number_domestic',False)
153 vals['acc_number'], vals['acc_number_domestic'] = (
154 self._correct_IBAN(iban))
155@@ -1236,7 +1285,7 @@
156 fields.append('state')
157 records = self._founder.read(cr, uid, ids, fields, context, load)
158 is_list = True
159- if not isinstance(records, list):
160+ if not isinstance(records, list):
161 records = [records,]
162 is_list = False
163 for record in records:
164@@ -1529,7 +1578,7 @@
165 """
166 total = 0.0
167 if not ids:
168- total
169+ return total
170 for line in self.read(
171 cr, uid, ids, ['debit', 'credit'], context=context):
172 total += (line['debit'] or 0.0) - (line['credit'] or 0.0)
173
174=== modified file 'account_banking/account_banking_view.xml'
175--- account_banking/account_banking_view.xml 2012-03-08 10:18:13 +0000
176+++ account_banking/account_banking_view.xml 2012-05-02 14:33:21 +0000
177@@ -192,7 +192,7 @@
178 <data>
179 <field name="period_id" position="replace"/>
180 <xpath expr="/form/notebook/page[@string='Transaction']/field/tree" position="attributes">
181- <attribute name="colors">black:state == 'confirmed';darkmagenta:match_multi == True;grey:state=='draft';crimson:duplicate == True;</attribute>
182+ <attribute name="colors">black:state == 'confirmed';darkmagenta:match_multi == True;crimson:duplicate == True;grey:state == 'draft';</attribute>
183 </xpath>
184 <xpath expr="/form/notebook/page[@string='Transaction']/field/tree/field[@name='name']" position="replace">
185 <field name="name" required="1"/>
186
187=== modified file 'account_banking/banking_import_transaction.py'
188--- account_banking/banking_import_transaction.py 2012-04-17 10:58:38 +0000
189+++ account_banking/banking_import_transaction.py 2012-05-02 14:33:21 +0000
190@@ -88,7 +88,7 @@
191 elif not invoice_ids:
192 # create supplier invoice
193 partner_obj = self.pool.get('res.partner')
194- invoice_lines = [(0,0,dict(
195+ invoice_lines = [(0, 0, dict(
196 amount = 1,
197 price_unit = amount,
198 name = trans.message or trans.reference,
199@@ -247,8 +247,8 @@
200 # the interactive wizard
201 return False
202
203- '''Check if the move_line has been cached'''
204- return move_line.id in linked_invoices
205+ #'''Check if the move_line has been cached'''
206+ #return move_line.id in linked_invoices
207
208 def _cache(move_line, remaining=0.0):
209 '''Cache the move_line'''
210@@ -430,113 +430,18 @@
211
212 return trans, False, False
213
214- def _do_move_reconcile(
215- self, cr, uid, move_line_ids, currency, amount, context=None):
216- """
217- Prepare a reconciliation for a bank transaction of the given
218- amount. The caller MUST add the move line associated with the
219- bank transaction to the returned reconciliation resource.
220-
221- If adding the amount does not make the total add up,
222- prepare a partial reconciliation. An existing reconciliation on
223- the move lines will be taken into account.
224-
225- :param move_line_ids: List of ids. This will usually be the move
226- line of an associated invoice or payment, plus optionally the
227- move line of a writeoff.
228- :param currency: A res.currency *browse* object to perform math
229- operations on the amounts.
230- :param amount: the amount of the bank transaction. Amount < 0 in
231- case of a credit move on the bank account.
232- """
233- move_line_obj = self.pool.get('account.move.line')
234- reconcile_obj = self.pool.get('account.move.reconcile')
235- is_zero = lambda amount: self.pool.get('res.currency').is_zero(
236- cr, uid, currency, amount)
237- move_lines = move_line_obj.browse(cr, uid, move_line_ids, context=context)
238- reconcile = False
239- for move_line in move_lines:
240- if move_line.reconcile_id:
241- raise osv.except_osv(
242- _('Entry is already reconciled'),
243- _("You cannot reconcile the bank transaction with this entry, " +
244- "it is already reconciled")
245- )
246- if move_line.reconcile_partial_id:
247- if reconcile and reconcile.id != move_line.reconcile_partial_id.id:
248- raise osv.except_osv(
249- _('Cannot reconcile'),
250- _('Move lines are already partially reconciled, ' +
251- 'but not with each other.'))
252- reconcile = move_line.reconcile_partial_id
253- line_ids = list(set(move_line_ids + (
254- [x.id for x in reconcile and ( # reconcile.line_id or
255- reconcile.line_partial_ids) or []])))
256- if not reconcile:
257- reconcile_id = reconcile_obj.create(
258- cr, uid, {'type': 'auto' }, context=context)
259- reconcile = reconcile_obj.browse(cr, uid, reconcile_id, context=context)
260- full = is_zero(
261- move_line_obj.get_balance(cr, uid, line_ids) - amount)
262- # we should not have to check whether there is a surplus writeoff
263- # as any surplus amount *should* have been split off in the matching routine
264- if full:
265- line_partial_ids = []
266- else:
267- line_partial_ids = line_ids[:]
268- line_ids = []
269- reconcile_obj.write(
270- cr, uid, reconcile.id,
271- { 'line_id': [(6, 0, line_ids)],
272- 'line_partial_ids': [(6, 0, line_partial_ids)],
273- }, context=context)
274- return reconcile.id
275-
276- def _do_move_unreconcile(self, cr, uid, move_line_ids, currency, context=None):
277- """
278- Undo a reconciliation, removing the given move line ids. If no
279- meaningful (partial) reconciliation remains, delete it.
280-
281- :param move_line_ids: List of ids. This will usually be the move
282- line of an associated invoice or payment, plus optionally the
283- move line of a writeoff.
284- :param currency: A res.currency *browse* object to perform math
285- operations on the amounts.
286- """
287-
288- move_line_obj = self.pool.get('account.move.line')
289- reconcile_obj = self.pool.get('account.move.reconcile')
290- is_zero = lambda amount: self.pool.get('res.currency').is_zero(
291- cr, uid, currency, amount)
292- move_lines = move_line_obj.browse(cr, uid, move_line_ids, context=context)
293- reconcile = move_lines[0].reconcile_id or move_lines[0].reconcile_partial_id
294- line_ids = [x.id for x in reconcile.line_id or reconcile.line_partial_ids]
295- for move_line_id in move_line_ids:
296- line_ids.remove(move_line_id)
297- if len(line_ids) > 1:
298- full = is_zero(move_line_obj.get_balance(cr, uid, line_ids))
299- if full:
300- line_partial_ids = []
301- else:
302- line_partial_ids = line_ids.copy()
303- line_ids = []
304- reconcile_obj.write(
305- cr, uid, reconcile.id,
306- { 'line_partial_ids': [(6, 0, line_ids)],
307- 'line_id': [(6, 0, line_partial_ids)],
308- }, context=context)
309- else:
310- reconcile_obj.unlink(cr, uid, reconcile.id, context=context)
311- for move_line in move_lines:
312- if move_line.invoice:
313- # reopening the invoice
314- netsvc.LocalService('workflow').trg_validate(
315- uid, 'account.invoice', move_line.invoice.id, 'undo_paid', cr)
316- return True
317-
318- def _reconcile_move(
319- self, cr, uid, transaction_id, context=None):
320- transaction = self.browse(cr, uid, transaction_id, context=context)
321+ def _confirm_move(self, cr, uid, transaction_id, context=None):
322+ """
323+ The line is matched against a move (invoice), so generate a payment
324+ voucher with the write-off settings that the user requested. The move
325+ lines will be generated by the voucher, handling rounding and currency
326+ conversion.
327+ """
328+ if context is None:
329+ context = {}
330+
331+ statement_line_pool = self.pool.get('account.bank.statement.line')
332+ transaction = self.browse(cr, uid, transaction_id, context)
333 if not transaction.move_line_id:
334 if transaction.match_type == 'invoice':
335 raise osv.except_osv(
336@@ -558,42 +463,119 @@
337 transaction.statement_line_id.statement_id.name,
338 transaction.statement_line_id.name
339 )))
340- currency = transaction.statement_line_id.statement_id.currency
341- line_ids = [transaction.move_line_id.id]
342- if transaction.writeoff_move_line_id:
343- line_ids.append(transaction.writeoff_move_line_id.id)
344- reconcile_id = self._do_move_reconcile(
345- cr, uid, line_ids, currency,
346- transaction.transferred_amount, context=context)
347- return reconcile_id
348-
349- def _reconcile_storno(
350+
351+ st_line = transaction.statement_line_id
352+ journal = st_line.statement_id.journal_id
353+ if st_line.amount < 0.0:
354+ voucher_type = 'payment'
355+ account_id = (journal.default_debit_account_id and
356+ journal.default_debit_account_id.id or False)
357+ else:
358+ voucher_type = 'receipt'
359+ account_id = (journal.default_credit_account_id and
360+ journal.default_credit_account_id.id or False)
361+
362+ # Use the statement line's date determine the period
363+ ctxt = context.copy()
364+ ctxt['company_id'] = st_line.company_id.id
365+ if 'period_id' in ctxt:
366+ del ctxt['period_id']
367+ period_id = self.pool.get('account.period').find(
368+ cr, uid, st_line.date, context=ctxt)[0]
369+
370+ # Convert the move line amount to the journal currency
371+ move_line_amount = transaction.move_line_id.amount_residual_currency
372+ to_curr_id = (st_line.statement_id.journal_id.currency and
373+ st_line.statement_id.journal_id.currency.id or
374+ st_line.statement_id.company_id.currency_id.id)
375+ from_curr_id = (transaction.move_line_id.currency_id and
376+ transaction.move_line_id.currency_id.id or
377+ st_line.statement_id.company_id.currency_id.id)
378+ if from_curr_id != to_curr_id:
379+ amount_currency = statement_line_pool._convert_currency(
380+ cr, uid, from_curr_id, to_curr_id, move_line_amount,
381+ round=True, date=time.strftime('%Y-%m-%d'),
382+ context=context)
383+ else:
384+ amount_currency = move_line_amount
385+
386+ # Check whether this is a full or partial reconciliation
387+ if transaction.payment_option == 'with_writeoff':
388+ writeoff = abs(st_line.amount) - abs(amount_currency)
389+ line_amount = abs(amount_currency)
390+ else:
391+ writeoff = 0.0
392+ line_amount = abs(st_line.amount)
393+
394+ # Define the voucher
395+ voucher = {
396+ 'journal_id': st_line.statement_id.journal_id.id,
397+ 'partner_id': st_line.partner_id and st_line.partner_id.id or False,
398+ 'company_id': st_line.company_id.id,
399+ 'type':voucher_type,
400+ 'company_id': st_line.company_id.id,
401+ 'account_id': account_id,
402+ 'amount': abs(st_line.amount),
403+ 'writeoff_amount': writeoff,
404+ 'payment_option': transaction.payment_option,
405+ 'writeoff_acc_id': transaction.writeoff_account_id.id,
406+ 'analytic_id': transaction.writeoff_analytic_id.id,
407+ 'date': st_line.date,
408+ 'date_due': st_line.date,
409+ 'period_id': period_id,
410+ 'payment_rate_currency_id':to_curr_id,
411+ }
412+
413+ # Define the voucher line
414+ vch_line = {
415+ #'voucher_id': v_id,
416+ 'move_line_id': transaction.move_line_id.id,
417+ 'reconcile': True,
418+ 'amount': line_amount,
419+ 'account_id': transaction.move_line_id.account_id.id,
420+ 'type': transaction.move_line_id.credit and 'dr' or 'cr',
421+ }
422+ voucher['line_ids'] = [(0, 0, vch_line)]
423+ voucher_id = self.pool.get('account.voucher').create(
424+ cr, uid, voucher, context=context)
425+ statement_line_pool.write(
426+ cr, uid, st_line.id,
427+ {'voucher_id': voucher_id}, context=context)
428+ transaction.refresh()
429+
430+ def _confirm_storno(
431 self, cr, uid, transaction_id, context=None):
432 """
433 Creation of the reconciliation has been delegated to
434 *a* direct debit module, to allow for various direct debit styles
435 """
436- payment_line_obj = self.pool.get('payment.line')
437+ payment_line_pool = self.pool.get('payment.line')
438+ statement_line_pool = self.pool.get('account.bank.statement.line')
439 transaction = self.browse(cr, uid, transaction_id, context=context)
440 if not transaction.payment_line_id:
441 raise osv.except_osv(
442 _("Cannot link with storno"),
443 _("No direct debit order item"))
444- return payment_line_obj.debit_storno(
445+ reconcile_id = payment_line_pool.debit_storno(
446 cr, uid,
447 transaction.payment_line_id.id,
448 transaction.statement_line_id.amount,
449 transaction.statement_line_id.currency,
450 transaction.storno_retry,
451 context=context)
452+ statement_line_pool.write(
453+ cr, uid, transaction.statement_line_id.id,
454+ {'reconcile_id': reconcile_id}, context=context)
455+ transaction.refresh()
456
457- def _reconcile_payment_order(
458+ def _confirm_payment_order(
459 self, cr, uid, transaction_id, context=None):
460 """
461 Creation of the reconciliation has been delegated to
462 *a* direct debit module, to allow for various direct debit styles
463 """
464 payment_order_obj = self.pool.get('payment.order')
465+ statement_line_pool = self.pool.get('account.bank.statement.line')
466 transaction = self.browse(cr, uid, transaction_id, context=context)
467 if not transaction.payment_order_id:
468 raise osv.except_osv(
469@@ -603,14 +585,17 @@
470 raise osv.except_osv(
471 _("Cannot reconcile"),
472 _("Reconcile payment order not implemented"))
473- return payment_order_obj.debit_reconcile_transfer(
474+ reconcile_id = payment_order_obj.debit_reconcile_transfer(
475 cr, uid,
476 transaction.payment_order_id.id,
477 transaction.statement_line_id.amount,
478 transaction.statement_line_id.currency,
479 context=context)
480+ statement_line_pool.write(
481+ cr, uid, transaction.statement_line_id.id,
482+ {'reconcile_id': reconcile_id}, context=context)
483
484- def _reconcile_payment(
485+ def _confirm_payment(
486 self, cr, uid, transaction_id, context=None):
487 """
488 Do some housekeeping on the payment line
489@@ -624,7 +609,7 @@
490 'date_done': transaction.effective_date,
491 }
492 )
493- return self._reconcile_move(cr, uid, transaction_id, context=context)
494+ self._confirm_move(cr, uid, transaction_id, context=context)
495
496 def _cancel_payment(
497 self, cr, uid, transaction_id, context=None):
498@@ -653,20 +638,107 @@
499 transaction.statement_line_id.amount,
500 transaction.statement_line_id.currency)
501
502- def _cancel_move(
503- self, cr, uid, transaction_id, context=None):
504- statement_line_obj = self.pool.get('account.bank.statement.line')
505- transaction = self.browse(cr, uid, transaction_id, context=context)
506- move_line_id = transaction.move_line_id.id
507+ def _legacy_do_move_unreconcile(self, cr, uid, move_line_ids, currency, context=None):
508+ """
509+ Legacy method. Allow for canceling bank statement lines that
510+ were confirmed using earlier versions of the interactive wizard branch.
511+
512+ Undo a reconciliation, removing the given move line ids. If no
513+ meaningful (partial) reconciliation remains, delete it.
514+
515+ :param move_line_ids: List of ids. This will usually be the move
516+ line of an associated invoice or payment, plus optionally the
517+ move line of a writeoff.
518+ :param currency: A res.currency *browse* object to perform math
519+ operations on the amounts.
520+ """
521+ move_line_obj = self.pool.get('account.move.line')
522+ reconcile_obj = self.pool.get('account.move.reconcile')
523+ is_zero = lambda amount: self.pool.get('res.currency').is_zero(
524+ cr, uid, currency, amount)
525+ move_lines = move_line_obj.browse(cr, uid, move_line_ids, context=context)
526+ reconcile = move_lines[0].reconcile_id or move_lines[0].reconcile_partial_id
527+ line_ids = [x.id for x in reconcile.line_id or reconcile.line_partial_ids]
528+ for move_line_id in move_line_ids:
529+ line_ids.remove(move_line_id)
530+ if len(line_ids) > 1:
531+ full = is_zero(move_line_obj.get_balance(cr, uid, line_ids))
532+ if full:
533+ line_partial_ids = []
534+ else:
535+ line_partial_ids = list(line_ids)
536+ line_ids = []
537+ reconcile_obj.write(
538+ cr, uid, reconcile.id,
539+ { 'line_partial_ids': [(6, 0, line_partial_ids)],
540+ 'line_id': [(6, 0, line_ids)],
541+ }, context=context)
542+ else:
543+ reconcile_obj.unlink(cr, uid, reconcile.id, context=context)
544+ for move_line in move_lines:
545+ if move_line.invoice:
546+ # reopening the invoice
547+ netsvc.LocalService('workflow').trg_validate(
548+ uid, 'account.invoice', move_line.invoice.id, 'undo_paid', cr)
549+ return True
550+
551+ def _legacy_clear_up_writeoff(self, cr, uid, transaction, context=None):
552+ """
553+ Legacy method to support upgrades older installations of the
554+ interactive wizard branch. To be removed after 6.2
555+ clear up the writeoff move
556+ """
557+ if transaction.writeoff_move_line_id:
558+ move_pool = self.pool.get('account.move')
559+ move_pool.button_cancel(
560+ cr, uid, [transaction.writeoff_move_line_id.move_id.id],
561+ context=context)
562+ move_pool.unlink(
563+ cr, uid, [transaction.writeoff_move_line_id.move_id.id],
564+ context=context)
565+ return True
566+
567+ def _legacy_cancel_move(
568+ self, cr, uid, transaction, context=None):
569+ """
570+ Legacy method to support upgrades from older installations
571+ of the interactive wizard branch.
572+ """
573 currency = transaction.statement_line_id.statement_id.currency
574 line_ids = [transaction.move_line_id.id]
575+ statement_line_obj = self.pool.get('account.bank.statement.line')
576 if transaction.writeoff_move_line_id:
577 line_ids.append(transaction.writeoff_move_line_id.id)
578- self._do_move_unreconcile(
579+ self._legacy_do_move_unreconcile(
580 cr, uid, line_ids, currency, context=context)
581 statement_line_obj.write(
582 cr, uid, transaction.statement_line_id.id,
583 {'reconcile_id': False}, context=context)
584+
585+ def _cancel_voucher(
586+ self, cr, uid, transaction_id, context=None):
587+ voucher_pool = self.pool.get('account.voucher')
588+ transaction = self.browse(cr, uid, transaction_id, context=context)
589+ st_line = transaction.statement_line_id
590+ if transaction.match_type:
591+ if st_line.voucher_id:
592+ # Although vouchers can be associated with statement lines
593+ # in standard OpenERP, we consider ourselves owner of the voucher
594+ # if the line has an associated transaction
595+ # Upon canceling of the statement line/transaction,
596+ # we cancel and delete the vouchers.
597+ # Otherwise, the statement line will leave the voucher
598+ # unless the statement line itself is deleted.
599+ voucher_pool.cancel_voucher(
600+ cr, uid, [st_line.voucher_id.id], context=context)
601+ voucher_pool.action_cancel_draft(
602+ cr, uid, [st_line.voucher_id.id], context=context)
603+ voucher_pool.unlink(
604+ cr, uid, [st_line.voucher_id.id], context=context)
605+ # Allow canceling of legacy entries
606+ if not st_line.voucher_id and st_line.reconcile_id:
607+ self._legacy_cancel_move(cr, uid, transaction, context=context)
608+
609 return True
610
611 def _cancel_storno(
612@@ -694,11 +766,15 @@
613 else:
614 account_id = journal.default_debit_account_id.id
615 cancel_line = False
616- for line in transaction.statement_line_id.move_id.line_id:
617+ move_lines = []
618+ for move in transaction.statement_line_id.move_ids:
619+ # There should usually be just one move, I think
620+ move_lines += move.line_id
621+ for line in move_lines:
622 if line.account_id.id != account_id:
623 cancel_line = line
624 break
625- if not cancel_line: # debug
626+ if not cancel_line:
627 raise osv.except_osv(
628 _("Cannot cancel link with storno"),
629 _("Line id not found"))
630@@ -724,16 +800,16 @@
631
632 cancel_map = {
633 'storno': _cancel_storno,
634- 'invoice': _cancel_move,
635- 'manual': _cancel_move,
636- 'move': _cancel_move,
637+ 'invoice': _cancel_voucher,
638+ 'manual': _cancel_voucher,
639+ 'move': _cancel_voucher,
640 'payment_order': _cancel_payment_order,
641 'payment': _cancel_payment,
642 }
643+
644 def cancel(self, cr, uid, ids, context=None):
645 if ids and isinstance(ids, (int, float)):
646 ids = [ids]
647- move_obj = self.pool.get('account.move')
648 for transaction in self.browse(cr, uid, ids, context):
649 if not transaction.match_type:
650 continue
651@@ -743,31 +819,25 @@
652 _("No method found to cancel this type"))
653 self.cancel_map[transaction.match_type](
654 self, cr, uid, transaction.id, context)
655- # clear up the writeoff move
656- if transaction.writeoff_move_line_id:
657- move_obj.button_cancel(
658- cr, uid, [transaction.writeoff_move_line_id.move_id.id],
659- context=context)
660- move_obj.unlink(
661- cr, uid, [transaction.writeoff_move_line_id.move_id.id],
662- context=context)
663+ self._legacy_clear_up_writeoff(cr, uid, transaction, context=context)
664 return True
665
666- reconcile_map = {
667- 'storno': _reconcile_storno,
668- 'invoice': _reconcile_move,
669- 'manual': _reconcile_move,
670- 'payment_order': _reconcile_payment_order,
671- 'payment': _reconcile_payment,
672- 'move': _reconcile_move,
673+ confirm_map = {
674+ 'storno': _confirm_storno,
675+ 'invoice': _confirm_move,
676+ 'manual': _confirm_move,
677+ 'payment_order': _confirm_payment_order,
678+ 'payment': _confirm_payment,
679+ 'move': _confirm_move,
680 }
681- def reconcile(self, cr, uid, ids, context=None):
682+
683+ def confirm(self, cr, uid, ids, context=None):
684 if ids and isinstance(ids, (int, float)):
685 ids = [ids]
686 for transaction in self.browse(cr, uid, ids, context):
687 if not transaction.match_type:
688 continue
689- if transaction.match_type not in self.reconcile_map:
690+ if transaction.match_type not in self.confirm_map:
691 raise osv.except_osv(
692 _("Cannot reconcile"),
693 _("Cannot reconcile type %s. No method found to " +
694@@ -782,19 +852,11 @@
695 "this match type.") %
696 transaction.statement_line_id.name
697 )
698- self._generate_writeoff_move(
699- cr, uid, transaction.id, context=context)
700- # run the method that is appropriate for this match type
701- reconcile_id = self.reconcile_map[transaction.match_type](
702+ # Generalize this bit and move to the confirmation
703+ # methods that actually do create a voucher?
704+ self.confirm_map[transaction.match_type](
705 self, cr, uid, transaction.id, context)
706- self.pool.get('account.bank.statement.line').write(
707- cr, uid, transaction.statement_line_id.id,
708- {'reconcile_id': reconcile_id}, context=context)
709
710- # TODO
711- # update the statement line bank account reference
712- # as follows (from _match_invoice)
713-
714 """
715 account_ids = [
716 x.id for x in bank_account_ids
717@@ -802,64 +864,7 @@
718 ][0]
719 """
720 return True
721-
722-
723- def _generate_writeoff_move(self, cr, uid, ids, context):
724- if ids and isinstance(ids, (int, float)):
725- ids = [ids]
726- move_line_obj = self.pool.get('account.move.line')
727- move_obj = self.pool.get('account.move')
728- for trans in self.browse(cr, uid, ids, context=context):
729- # Get the period for the company and date
730- ctxt = context.copy()
731- ctxt['company_id'] = trans.company_id.id
732- periods = self.pool.get('account.period').find(
733- cr, uid, trans.statement_line_id.date, context=ctxt)
734- period_id = periods and periods[0] or False
735-
736- move_id = move_obj.create(cr, uid, {
737- 'journal_id': trans.writeoff_journal_id.id,
738- 'period_id': period_id,
739- 'date': trans.statement_line_id.date,
740- 'name': '(write-off) %s' % (
741- trans.move_line_id.move_id.name or ''),
742- }, context=context)
743- if trans.residual > 0:
744- writeoff_debit = trans.residual
745- writeoff_credit = False
746- else:
747- writeoff_debit = False
748- writeoff_credit = - trans.residual
749- vals = {
750- 'name': trans.statement_line_id.name,
751- 'date': trans.statement_line_id.date,
752- 'ref': trans.statement_line_id.ref,
753- 'move_id': move_id,
754- 'partner_id': (trans.statement_line_id.partner_id and
755- trans.statement_line_id.partner_id.id or False),
756- 'account_id': trans.statement_line_id.account_id.id,
757- 'credit': writeoff_debit,
758- 'debit': writeoff_credit,
759- 'journal_id': trans.writeoff_journal_id.id,
760- 'period_id': period_id,
761- 'currency_id': trans.statement_line_id.statement_id.currency.id,
762- 'analytic_account_id': trans.writeoff_analytic_id.id,
763- }
764- move_line_id = move_line_obj.create(
765- cr, uid, vals, context=context)
766- self.write(
767- cr, uid, trans.id,
768- {'writeoff_move_line_id': move_line_id}, context=context)
769- vals.update({
770- 'account_id': trans.writeoff_account_id.id,
771- 'credit': writeoff_credit,
772- 'debit': writeoff_debit,
773- })
774- move_line_obj.create(
775- cr, uid, vals, context=context)
776- move_obj.post(
777- cr, uid, [move_id], context=context)
778-
779+
780 def _match_storno(
781 self, cr, uid, trans, log, context=None):
782 payment_line_obj = self.pool.get('payment.line')
783@@ -1231,7 +1236,7 @@
784 provision_costs_description = False,
785 ), context=context)
786 # rebrowse the current record after writing
787- transaction=self.browse(cr, uid, transaction.id, context=context)
788+ transaction = self.browse(cr, uid, transaction.id, context=context)
789 # Match full direct debit orders
790 if transaction.type == bt.DIRECT_DEBIT:
791 move_info = self._match_debit_order(
792@@ -1456,10 +1461,15 @@
793 """
794 if not ids:
795 return {}
796- res = {}
797+ res = dict([(x, False) for x in ids])
798 for transaction in self.browse(cr, uid, ids, context):
799- res[transaction.id] = abs(transaction.transferred_amount) - abs(transaction.move_currency_amount)
800-
801+ res[transaction.id] = (
802+ not(transaction.move_currency_amount is False)
803+ and (
804+ transaction.transferred_amount -
805+ transaction.move_currency_amount
806+ )
807+ or False)
808 return res
809
810 def _get_match_multi(self, cr, uid, ids, name, args, context=None):
811@@ -1510,12 +1520,11 @@
812 This will be used to calculate the write-off amount (in statement currency).
813 """
814 if not ids:
815- return {}
816+ return {}
817+ res = dict([(x, False) for x in ids])
818
819 stline_pool = self.pool.get('account.bank.statement.line')
820
821- res = {}
822-
823 for transaction in self.browse(cr, uid, ids, context):
824
825 if transaction.move_line_id:
826@@ -1612,15 +1621,25 @@
827 'invoice_id', 'transaction_id', 'Matching invoices'),
828 'invoice_id': fields.many2one(
829 'account.invoice', 'Invoice to reconcile'),
830- 'payment_line_id': fields.many2one('payment.line', 'Payment line'),
831 'residual': fields.function(
832 _get_residual, method=True, string='Residual', type='float'),
833 'writeoff_account_id': fields.many2one(
834 'account.account', 'Write-off account',
835 domain=[('type', '!=', 'view')]),
836- 'payment_option':fields.selection([('without_writeoff', 'Keep Open'),('with_writeoff', 'Reconcile Payment Balance')], 'Payment Difference',
837- required=True, help="This field helps you to choose what you want to do with the eventual difference between the paid amount and the sum of allocated amounts. You can either choose to keep open this difference on the partner's account, or reconcile it with the payment(s)"),
838+ 'payment_option':fields.selection(
839+ [
840+ ('without_writeoff', 'Keep Open'),
841+ ('with_writeoff', 'Reconcile Payment Balance')
842+ ], 'Payment Difference',
843+ required=True,
844+ help=("This field helps you to choose what you want to do with "
845+ "the eventual difference between the paid amount and the "
846+ "sum of allocated amounts. You can either choose to keep "
847+ "open this difference on the partner's account, "
848+ "or reconcile it with the payment(s)"),
849+ ),
850 'writeoff_amount': fields.float('Difference Amount'),
851+ # Legacy field: to be removed after 6.2
852 'writeoff_move_line_id': fields.many2one(
853 'account.move.line', 'Write off move line'),
854 'writeoff_analytic_id': fields.many2one(
855@@ -1648,8 +1667,9 @@
856 'import_transaction_id', 'match_multi', type='boolean',
857 string='Multi match', readonly=True),
858 'residual': fields.related(
859- 'import_transaction_id', 'residual', type='float',
860- string='Residual'),
861+ 'import_transaction_id', 'residual', type='float',
862+ string='Residual', readonly=True,
863+ ),
864 'duplicate': fields.related(
865 'import_transaction_id', 'duplicate', type='boolean',
866 string='Possible duplicate import', readonly=True),
867@@ -1660,16 +1680,9 @@
868 ('payment_order', 'Payment order'),
869 ('storno', 'Storno')],
870 string='Match type', readonly=True,),
871- 'residual': fields.related(
872- 'import_transaction_id', 'residual', type='float',
873- string='Residual', readonly=True,
874- ),
875 'state': fields.selection(
876 [('draft', 'Draft'), ('confirmed', 'Confirmed')], 'State',
877 readonly=True, required=True),
878- 'move_id': fields.many2one(
879- 'account.move', 'Move', readonly=True,
880- help="The accounting move associated with this line"),
881 }
882
883 _defaults = {
884@@ -1690,8 +1703,9 @@
885 res = wizard_obj.create_act_window(cr, uid, res_id, context=context)
886 return res
887
888-
889- def _convert_currency(self, cr, uid, from_curr_id, to_curr_id, from_amount, round=False, date=None, context=None):
890+ def _convert_currency(
891+ self, cr, uid, from_curr_id, to_curr_id, from_amount,
892+ round=False, date=None, context=None):
893 """Convert currency amount using the company rate on a specific date"""
894 curr_obj = self.pool.get('res.currency')
895 if context:
896@@ -1701,10 +1715,11 @@
897 if date:
898 ctxt["date"] = date
899
900- amount = curr_obj.compute(cr, uid, from_curr_id, to_curr_id, from_amount, round=round, context=ctxt)
901+ amount = curr_obj.compute(
902+ cr, uid, from_curr_id, to_curr_id, from_amount,
903+ round=round, context=ctxt)
904 return amount
905
906-
907 def confirm(self, cr, uid, ids, context=None):
908 """
909 Create (or update) a voucher for each statement line, and then generate
910@@ -1712,10 +1727,10 @@
911 If a line does not have a move line against it, but has an account, then
912 generate a journal entry that moves the line amount to the specified account.
913 """
914- voucher_pool = self.pool.get('account.voucher')
915 statement_pool = self.pool.get('account.bank.statement')
916 obj_seq = self.pool.get('ir.sequence')
917 move_pool = self.pool.get('account.move')
918+ import_transaction_obj = self.pool.get('banking.import.transaction')
919
920 for st_line in self.browse(cr, uid, ids, context):
921 if st_line.state != 'draft':
922@@ -1734,157 +1749,47 @@
923 "journal!") % (st_line.statement_id.journal_id.name,))
924 if not st_line.amount:
925 continue
926-
927+ if not st_line.period_id:
928+ self.write(
929+ cr, uid, [st_line.id], {
930+ 'period_id': self._get_period(
931+ cr, uid, {'date': st_line.date})
932+ })
933+ st_line.refresh()
934 # Generate the statement number, if it is not already done
935 st = st_line.statement_id
936 if not st.name == '/':
937 st_number = st.name
938 else:
939 if st.journal_id.sequence_id:
940- c = {'fiscalyear_id': st.period_id.fiscalyear_id.id}
941+ period = st.period_id or st_line.period_id
942+ c = {'fiscalyear_id': period.fiscalyear_id.id}
943 st_number = obj_seq.next_by_id(cr, uid, st.journal_id.sequence_id.id, context=c)
944 else:
945 st_number = obj_seq.next_by_code(cr, uid, 'account.bank.statement')
946- statement_obj.write(cr, uid, [st.id], {'name': st_number}, context=context)
947-
948- # Check if this line has been matched against a journal item
949- if st_line.import_transaction_id.move_line_id:
950- # Line has been matched, so post it via a voucher
951- self._post_with_voucher(cr, uid, ids, st_line, voucher_pool, move_pool, context=context)
952-
953- else:
954- # Check to see if the line has an account that can be used to generate a journal entry
955- if st_line.account_id:
956- # Generate a journal for the entry using the standard bank statement code
957- self._create_move(cr, uid, st_line, statement_pool, st, st_number, context=context)
958- else:
959- raise osv.except_osv(
960- _('Statement line has no account'),
961- _("You cannot confirm a bank statement line that has no account against it (%s.%s)") %
962- (st_line.statement_id.name, st_line.name,))
963-
964+ statement_pool.write(cr, uid, [st.id], {'name': st_number}, context=context)
965+
966+ if st_line.import_transaction_id:
967+ import_transaction_obj.confirm(
968+ cr, uid, st_line.import_transaction_id.id, context)
969+ st_line.refresh()
970+ st_line_number = statement_pool.get_next_st_line_number(
971+ cr, uid, st_number, st_line, context)
972+ company_currency_id = st.journal_id.company_id.currency_id.id
973+ statement_pool.create_move_from_st_line(
974+ cr, uid, st_line.id, company_currency_id, st_line_number, context)
975+ self.write(
976+ cr, uid, st_line.id, {'state': 'confirmed'}, context)
977 return True
978
979- def _create_move(self, cr, uid, st_line, statement_pool, st, st_number, context):
980- """
981- The line is not matched against a move, but the account has been defined for the line.
982- Generate a journal entry for the statement line that transfers the full amount against the account.
983- """
984- st_line_number = statement_pool.get_next_st_line_number(cr, uid, st_number, st_line, context)
985- company_currency_id = st.journal_id.company_id.currency_id.id
986- move_id = statement_pool.create_move_from_st_line(cr, uid, st_line.id, company_currency_id, st_line_number, context)
987- self.write(cr, uid, st_line.id, {'state': 'confirmed', 'move_id': move_id}, context)
988-
989-
990- def _post_with_voucher(self, cr, uid, ids, st_line, voucher_pool, move_pool, context):
991- # Check if a voucher has already been created
992- if st_line.voucher_id:
993- # There's an existing voucher on the statement line which was probably created
994- # manually. This may have been done because it was a single payment for multiple
995- # invoices. Just get the voucher ID.
996- voucher_id = st_line.voucher_id.id
997- else:
998- # Create a voucher and store the ID on the statement line
999- voucher_id = self._create_voucher(cr, uid, ids, st_line, context=context)
1000- self.pool.get('account.bank.statement.line').write(cr,uid,st_line.id,{'voucher_id':voucher_id} , context=context)
1001-
1002- # If the voucher is in draft mode, then post it
1003- voucher = voucher_pool.browse(cr, uid, voucher_id, context=context)
1004- if voucher.state in ('draft','proforma'):
1005- voucher.action_move_line_create()
1006-
1007- # Update the statement line to indicate that it has been posted
1008- # ... no longer need to set the move_id on the voucher?
1009- self.write(cr, uid, st_line.id, {'state': 'confirmed'}, context)
1010-
1011- # The voucher journal isn't automatically posted, so post it (if needed)
1012- if not voucher.journal_id.entry_posted:
1013- voucher = voucher_pool.browse(cr, uid, voucher_id, context=context)
1014- move_pool.post(cr, uid, [voucher.move_id.id], context={})
1015-
1016-
1017- def _create_voucher(self, cr, uid, ids, st_line, context):
1018- """
1019- The line is matched against a move (invoice), so generate a payment voucher with the write-off settings that the
1020- user requested. The move lines will be generated by the voucher, handling rounding and currency conversion.
1021- """
1022- journal = st_line.statement_id.journal_id
1023- if st_line.amount < 0.0:
1024- voucher_type = 'payment'
1025- account_id = journal.default_debit_account_id and journal.default_debit_account_id.id or False
1026- else:
1027- voucher_type = 'receipt'
1028- account_id = journal.default_credit_account_id and journal.default_credit_account_id.id or False
1029-
1030- # Use the statement line's date determine the period
1031- ctxt = context.copy()
1032- ctxt['company_id'] = st_line.company_id.id
1033- if 'period_id' in ctxt:
1034- del ctxt['period_id']
1035- period_id = self.pool.get('account.period').find(cr, uid, st_line.date, context=ctxt)[0]
1036-
1037- # Convert the move line amount to the journal currency
1038- move_line_amount = st_line.import_transaction_id.move_line_id.amount_residual_currency
1039- to_curr_id = st_line.statement_id.journal_id.currency and st_line.statement_id.journal_id.currency.id or st_line.statement_id.company_id.currency_id.id
1040- from_curr_id = st_line.import_transaction_id.move_line_id.currency_id and st_line.import_transaction_id.move_line_id.currency_id.id or st_line.statement_id.company_id.currency_id.id
1041- if from_curr_id != to_curr_id:
1042- amount_currency = self._convert_currency(cr, uid, from_curr_id, to_curr_id, move_line_amount, round=True,
1043- date=time.strftime('%Y-%m-%d'), context=context)
1044- else:
1045- amount_currency = move_line_amount
1046-
1047- # Check whether this is a full or partial reconciliation
1048- if st_line.import_transaction_id.payment_option=='with_writeoff':
1049- writeoff = abs(st_line.amount)-abs(amount_currency)
1050- line_amount = abs(amount_currency)
1051- else:
1052- writeoff = 0.0
1053- line_amount = abs(st_line.amount)
1054-
1055- # Define the voucher
1056- voucher = {
1057- 'journal_id': st_line.statement_id.journal_id.id,
1058- 'partner_id': st_line.partner_id and st_line.partner_id.id or False,
1059- 'company_id': st_line.company_id.id,
1060- 'type':voucher_type,
1061- 'company_id': st_line.company_id.id,
1062- 'account_id': account_id,
1063- 'amount': abs(st_line.amount),
1064- 'writeoff_amount': writeoff,
1065- 'payment_option': st_line.import_transaction_id.payment_option,
1066- 'writeoff_acc_id': st_line.import_transaction_id.writeoff_account_id.id,
1067- 'analytic_id': st_line.import_transaction_id.writeoff_analytic_id.id,
1068- 'date': st_line.date,
1069- 'date_due': st_line.date,
1070- 'period_id': period_id,
1071- 'payment_rate_currency_id':to_curr_id,
1072- }
1073-
1074- # Define the voucher line
1075- vch_line = {
1076- #'voucher_id': v_id,
1077- 'move_line_id': st_line.import_transaction_id.move_line_id.id,
1078- 'reconcile': True,
1079- 'amount': line_amount,
1080- 'account_id': st_line.import_transaction_id.move_line_id.account_id.id,
1081- 'type': st_line.import_transaction_id.move_line_id.credit and 'dr' or 'cr',
1082- }
1083- voucher['line_ids'] = [(0,0,vch_line)]
1084- v_id = self.pool.get('account.voucher').create(cr, uid, voucher, context=context)
1085-
1086- return v_id
1087-
1088-
1089 def cancel(self, cr, uid, ids, context=None):
1090 if ids and isinstance(ids, (int, float)):
1091 ids = [ids]
1092- account_move_obj = self.pool.get('account.move')
1093 import_transaction_obj = self.pool.get('banking.import.transaction')
1094- voucher_pool = self.pool.get('account.voucher')
1095+ move_pool = self.pool.get('account.move')
1096 transaction_cancel_ids = []
1097- voucher_cancel_ids = []
1098+ set_draft_ids = []
1099 move_unlink_ids = []
1100- set_draft_ids = []
1101 # harvest ids for various actions
1102 for st_line in self.browse(cr, uid, ids, context):
1103 if st_line.state != 'confirmed':
1104@@ -1895,32 +1800,23 @@
1105 _("The bank statement that this transaction belongs to has "
1106 "already been confirmed"))
1107
1108- # Check if the transaction has a voucher
1109- if st_line.voucher_id:
1110- voucher_cancel_ids.append(st_line.voucher_id.id)
1111- else:
1112- if st_line.import_transaction_id:
1113- transaction_cancel_ids.append(st_line.import_transaction_id.id)
1114- if st_line.move_id:
1115- move_unlink_ids.append(st_line.move_id.id)
1116- else:
1117- raise osv.except_osv(
1118- _("Cannot cancel bank transaction"),
1119- _("Cannot cancel this bank transaction. The information "
1120- "needed to undo the accounting entries has not been "
1121- "recorded"))
1122+ if st_line.import_transaction_id:
1123+ # Cancel transaction immediately.
1124+ # If it has voucher, this will clean up
1125+ # the moves on the st_line.
1126+ import_transaction_obj.cancel(
1127+ cr, uid, [st_line.import_transaction_id.id], context=context)
1128+ st_line.refresh()
1129+ for line in st_line.move_ids:
1130+ # We allow for people canceling and removing
1131+ # the associated payments, which can lead to confirmed
1132+ # statement lines without an associated move
1133+ move_unlink_ids.append(line.id)
1134 set_draft_ids.append(st_line.id)
1135
1136- # Cancel and delete the vouchers
1137- voucher_pool.cancel_voucher(cr, uid, voucher_cancel_ids, context=context)
1138- voucher_pool.action_cancel_draft(cr, uid, voucher_cancel_ids, context=context)
1139- voucher_pool.unlink(cr, uid, voucher_cancel_ids, context=context)
1140-
1141- # Perform actions
1142- import_transaction_obj.cancel(
1143- cr, uid, transaction_cancel_ids, context=context)
1144- account_move_obj.button_cancel(cr, uid, move_unlink_ids, context)
1145- account_move_obj.unlink(cr, uid, move_unlink_ids, context)
1146+ move_pool.button_cancel(
1147+ cr, uid, move_unlink_ids, context=context)
1148+ move_pool.unlink(cr, uid, move_unlink_ids, context=context)
1149 self.write(
1150 cr, uid, set_draft_ids, {'state': 'draft'}, context=context)
1151 return True
1152@@ -1934,8 +1830,12 @@
1153 ids = [ids]
1154 for line in self.browse(cr, uid, ids, context=context):
1155 if line.state == 'confirmed':
1156- raise osv.except_osv(_('Confirmed Statement Line'), _("You cannot delete a confirmed Statement Line: '%s'" % line.name))
1157- return super(account_bank_statement_line,self).unlink(cr, uid, ids, context=context)
1158+ raise osv.except_osv(
1159+ _('Confirmed Statement Line'),
1160+ _("You cannot delete a confirmed Statement Line"
1161+ ": '%s'" % line.name))
1162+ return super(account_bank_statement_line, self).unlink(
1163+ cr, uid, ids, context=context)
1164
1165 account_bank_statement_line()
1166
1167@@ -1980,7 +1880,7 @@
1168
1169 # protect against misguided manual changes
1170 for line in st.move_line_ids:
1171- if line.state <> 'valid':
1172+ if line.state != 'valid':
1173 raise osv.except_osv(_('Error !'),
1174 _('The account entries lines are not in valid state.'))
1175
1176
1177=== modified file 'account_banking/wizard/banking_transaction_wizard.py'
1178--- account_banking/wizard/banking_transaction_wizard.py 2012-03-07 23:02:52 +0000
1179+++ account_banking/wizard/banking_transaction_wizard.py 2012-05-02 14:33:21 +0000
1180@@ -21,6 +21,7 @@
1181 #
1182 ##############################################################################
1183 from osv import osv, fields
1184+from openerp.tools.translate import _
1185
1186 """
1187
1188@@ -38,7 +39,7 @@
1189 """
1190 Return a popup window for this model
1191 """
1192- if isinstance(ids, (int,long)):
1193+ if isinstance(ids, (int, long)):
1194 ids = [ids]
1195 return {
1196 'name': self._description,
1197@@ -160,9 +161,7 @@
1198 statement_line_obj = self.pool.get('account.bank.statement.line')
1199 for wiz in self.browse(
1200 cr, uid, ids, context=context):
1201- invoice_ids = False
1202 move_line_id = False
1203- move_line_ids = False
1204 invoice_id = manual_invoice_id
1205 if invoice_id:
1206 invoice = invoice_obj.browse(
1207
1208=== modified file 'account_direct_debit/model/account_payment.py'
1209--- account_direct_debit/model/account_payment.py 2012-01-12 10:58:49 +0000
1210+++ account_direct_debit/model/account_payment.py 2012-05-02 14:33:21 +0000
1211@@ -104,7 +104,6 @@
1212 _("Cannot unreconcile"),
1213 _("Cannot unreconcile debit order: "+
1214 "Workflow will not allow it."))
1215-
1216 return True
1217
1218 def test_undo_done(self, cr, uid, ids, context=None):
1219
1220=== modified file 'account_direct_debit/view/account_invoice.xml'
1221--- account_direct_debit/view/account_invoice.xml 2011-12-21 13:06:26 +0000
1222+++ account_direct_debit/view/account_invoice.xml 2012-05-02 14:33:21 +0000
1223@@ -13,9 +13,9 @@
1224 Maybe apply trick in fields_view_get instead, for
1225 better compatibility with other modules?
1226 -->
1227- <button name="invoice_open" position="attributes">
1228+ <!-- button name="invoice_open" position="attributes">
1229 <attribute name="states">draft,proforma2,debit_denied</attribute>
1230- </button>
1231+ </button -->
1232 <button string='Re-Open' position="attributes">
1233 <attribute name="states">paid,debit_denied</attribute>
1234 <!--
1235
1236=== modified file 'account_direct_debit/workflow/account_invoice.xml'
1237--- account_direct_debit/workflow/account_invoice.xml 2011-12-21 13:06:26 +0000
1238+++ account_direct_debit/workflow/account_invoice.xml 2012-05-02 14:33:21 +0000
1239@@ -8,20 +8,44 @@
1240 <field name="kind">function</field>
1241 </record>
1242 <record id="paid_to_debit_denied" model="workflow.transition">
1243+ <!--
1244+ Set an invoice to state debit denied, either manually
1245+ or by confirming a bank statement line that constitutes
1246+ a fatal storno
1247+ -->
1248 <field name="act_from" ref="account.act_paid"/>
1249 <field name="act_to" ref="act_debit_denied"/>
1250 <field name="signal">invoice_debit_denied</field>
1251 </record>
1252+ <record id="open_test_to_debit_denied" model="workflow.transition">
1253+ <!--
1254+ A storno leads to unreconciling the move line, which
1255+ reopens the invoice. We need to allow a transition from
1256+ this state to the debit denied state if the storno is fatal.
1257+ -->
1258+ <field name="act_from" ref="account.act_open_test"/>
1259+ <field name="act_to" ref="act_debit_denied"/>
1260+ <field name="signal">invoice_debit_denied</field>
1261+ </record>
1262 <record id="debit_denied_to_paid" model="workflow.transition">
1263+ <!--
1264+ Cancel a bank statement line that constitutes a fatal
1265+ storno
1266+ -->
1267 <field name="act_from" ref="act_debit_denied"/>
1268 <field name="act_to" ref="account.act_paid"/>
1269 <field name="condition">test_undo_debit_denied()</field>
1270 <field name="signal">undo_debit_denied</field>
1271 </record>
1272 <record id="debit_denied_to_open" model="workflow.transition">
1273+ <!--
1274+ Allow the user to manually reset a debit denied status
1275+ on a paid invoice (but only after manually unreconciling
1276+ the invoice)
1277+ -->
1278 <field name="act_from" ref="act_debit_denied"/>
1279- <field name="act_to" ref="account.act_open"/>
1280- <field name="signal">invoice_open</field>
1281+ <field name="act_to" ref="account.act_open_test"/>
1282+ <field name="signal">open_test</field>
1283 </record>
1284 </data>
1285 </openerp>

Subscribers

People subscribed via source and target branches