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

Proposed by Stefan Rijnhart (Opener)
Status: Merged
Merged at revision: 129
Proposed branch: lp:~therp-nl/banking-addons/6.1-dev-fixes_from_testing_iteration_2
Merge into: lp:banking-addons/6.1
Diff against target: 1203 lines (+423/-330) (has conflicts)
7 files modified
account_banking/account_banking.py (+73/-24)
account_banking/account_banking_view.xml (+1/-1)
account_banking/banking_import_transaction.py (+319/-297)
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)
Text conflict in account_banking/banking_import_transaction.py
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
James Jesudason Pending
Stefan Rijnhart (Opener) Pending
Review via email: mp+104535@code.launchpad.net

This proposal supersedes a proposal from 2012-05-02.

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.
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote : Posted in a previous version of this proposal

Added two more fixes. Otherwise, tested succesfully.

review: Needs Resubmitting

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-03 11:53:24 +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-03 11:53:24 +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-05-03 11:50:29 +0000
189+++ account_banking/banking_import_transaction.py 2012-05-03 11:53:24 +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,6 +430,7 @@
211
212 return trans, False, False
213
214+<<<<<<< TREE
215 def _do_move_reconcile(
216 self, cr, uid, move_line_ids, currency, amount, context=None):
217 """
218@@ -537,6 +538,20 @@
219 def _reconcile_move(
220 self, cr, uid, transaction_id, context=None):
221 transaction = self.browse(cr, uid, transaction_id, context=context)
222+=======
223+ def _confirm_move(self, cr, uid, transaction_id, context=None):
224+ """
225+ The line is matched against a move (invoice), so generate a payment
226+ voucher with the write-off settings that the user requested. The move
227+ lines will be generated by the voucher, handling rounding and currency
228+ conversion.
229+ """
230+ if context is None:
231+ context = {}
232+
233+ statement_line_pool = self.pool.get('account.bank.statement.line')
234+ transaction = self.browse(cr, uid, transaction_id, context)
235+>>>>>>> MERGE-SOURCE
236 if not transaction.move_line_id:
237 if transaction.match_type == 'invoice':
238 raise osv.except_osv(
239@@ -558,42 +573,119 @@
240 transaction.statement_line_id.statement_id.name,
241 transaction.statement_line_id.name
242 )))
243- currency = transaction.statement_line_id.statement_id.currency
244- line_ids = [transaction.move_line_id.id]
245- if transaction.writeoff_move_line_id:
246- line_ids.append(transaction.writeoff_move_line_id.id)
247- reconcile_id = self._do_move_reconcile(
248- cr, uid, line_ids, currency,
249- transaction.transferred_amount, context=context)
250- return reconcile_id
251-
252- def _reconcile_storno(
253+
254+ st_line = transaction.statement_line_id
255+ journal = st_line.statement_id.journal_id
256+ if st_line.amount < 0.0:
257+ voucher_type = 'payment'
258+ account_id = (journal.default_debit_account_id and
259+ journal.default_debit_account_id.id or False)
260+ else:
261+ voucher_type = 'receipt'
262+ account_id = (journal.default_credit_account_id and
263+ journal.default_credit_account_id.id or False)
264+
265+ # Use the statement line's date determine the period
266+ ctxt = context.copy()
267+ ctxt['company_id'] = st_line.company_id.id
268+ if 'period_id' in ctxt:
269+ del ctxt['period_id']
270+ period_id = self.pool.get('account.period').find(
271+ cr, uid, st_line.date, context=ctxt)[0]
272+
273+ # Convert the move line amount to the journal currency
274+ move_line_amount = transaction.move_line_id.amount_residual_currency
275+ to_curr_id = (st_line.statement_id.journal_id.currency and
276+ st_line.statement_id.journal_id.currency.id or
277+ st_line.statement_id.company_id.currency_id.id)
278+ from_curr_id = (transaction.move_line_id.currency_id and
279+ transaction.move_line_id.currency_id.id or
280+ st_line.statement_id.company_id.currency_id.id)
281+ if from_curr_id != to_curr_id:
282+ amount_currency = statement_line_pool._convert_currency(
283+ cr, uid, from_curr_id, to_curr_id, move_line_amount,
284+ round=True, date=time.strftime('%Y-%m-%d'),
285+ context=context)
286+ else:
287+ amount_currency = move_line_amount
288+
289+ # Check whether this is a full or partial reconciliation
290+ if transaction.payment_option == 'with_writeoff':
291+ writeoff = abs(st_line.amount) - abs(amount_currency)
292+ line_amount = abs(amount_currency)
293+ else:
294+ writeoff = 0.0
295+ line_amount = abs(st_line.amount)
296+
297+ # Define the voucher
298+ voucher = {
299+ 'journal_id': st_line.statement_id.journal_id.id,
300+ 'partner_id': st_line.partner_id and st_line.partner_id.id or False,
301+ 'company_id': st_line.company_id.id,
302+ 'type':voucher_type,
303+ 'company_id': st_line.company_id.id,
304+ 'account_id': account_id,
305+ 'amount': abs(st_line.amount),
306+ 'writeoff_amount': writeoff,
307+ 'payment_option': transaction.payment_option,
308+ 'writeoff_acc_id': transaction.writeoff_account_id.id,
309+ 'analytic_id': transaction.writeoff_analytic_id.id,
310+ 'date': st_line.date,
311+ 'date_due': st_line.date,
312+ 'period_id': period_id,
313+ 'payment_rate_currency_id':to_curr_id,
314+ }
315+
316+ # Define the voucher line
317+ vch_line = {
318+ #'voucher_id': v_id,
319+ 'move_line_id': transaction.move_line_id.id,
320+ 'reconcile': True,
321+ 'amount': line_amount,
322+ 'account_id': transaction.move_line_id.account_id.id,
323+ 'type': transaction.move_line_id.credit and 'dr' or 'cr',
324+ }
325+ voucher['line_ids'] = [(0, 0, vch_line)]
326+ voucher_id = self.pool.get('account.voucher').create(
327+ cr, uid, voucher, context=context)
328+ statement_line_pool.write(
329+ cr, uid, st_line.id,
330+ {'voucher_id': voucher_id}, context=context)
331+ transaction.refresh()
332+
333+ def _confirm_storno(
334 self, cr, uid, transaction_id, context=None):
335 """
336 Creation of the reconciliation has been delegated to
337 *a* direct debit module, to allow for various direct debit styles
338 """
339- payment_line_obj = self.pool.get('payment.line')
340+ payment_line_pool = self.pool.get('payment.line')
341+ statement_line_pool = self.pool.get('account.bank.statement.line')
342 transaction = self.browse(cr, uid, transaction_id, context=context)
343 if not transaction.payment_line_id:
344 raise osv.except_osv(
345 _("Cannot link with storno"),
346 _("No direct debit order item"))
347- return payment_line_obj.debit_storno(
348+ reconcile_id = payment_line_pool.debit_storno(
349 cr, uid,
350 transaction.payment_line_id.id,
351 transaction.statement_line_id.amount,
352 transaction.statement_line_id.currency,
353 transaction.storno_retry,
354 context=context)
355+ statement_line_pool.write(
356+ cr, uid, transaction.statement_line_id.id,
357+ {'reconcile_id': reconcile_id}, context=context)
358+ transaction.refresh()
359
360- def _reconcile_payment_order(
361+ def _confirm_payment_order(
362 self, cr, uid, transaction_id, context=None):
363 """
364 Creation of the reconciliation has been delegated to
365 *a* direct debit module, to allow for various direct debit styles
366 """
367 payment_order_obj = self.pool.get('payment.order')
368+ statement_line_pool = self.pool.get('account.bank.statement.line')
369 transaction = self.browse(cr, uid, transaction_id, context=context)
370 if not transaction.payment_order_id:
371 raise osv.except_osv(
372@@ -603,14 +695,17 @@
373 raise osv.except_osv(
374 _("Cannot reconcile"),
375 _("Reconcile payment order not implemented"))
376- return payment_order_obj.debit_reconcile_transfer(
377+ reconcile_id = payment_order_obj.debit_reconcile_transfer(
378 cr, uid,
379 transaction.payment_order_id.id,
380 transaction.statement_line_id.amount,
381 transaction.statement_line_id.currency,
382 context=context)
383+ statement_line_pool.write(
384+ cr, uid, transaction.statement_line_id.id,
385+ {'reconcile_id': reconcile_id}, context=context)
386
387- def _reconcile_payment(
388+ def _confirm_payment(
389 self, cr, uid, transaction_id, context=None):
390 """
391 Do some housekeeping on the payment line
392@@ -624,7 +719,7 @@
393 'date_done': transaction.effective_date,
394 }
395 )
396- return self._reconcile_move(cr, uid, transaction_id, context=context)
397+ self._confirm_move(cr, uid, transaction_id, context=context)
398
399 def _cancel_payment(
400 self, cr, uid, transaction_id, context=None):
401@@ -653,6 +748,7 @@
402 transaction.statement_line_id.amount,
403 transaction.statement_line_id.currency)
404
405+<<<<<<< TREE
406 def _cancel_move(
407 self, cr, uid, transaction_id, context=None):
408 """
409@@ -666,7 +762,76 @@
410 """
411 statement_line_obj = self.pool.get('account.bank.statement.line')
412 transaction = self.browse(cr, uid, transaction_id, context=context)
413+=======
414+ def _legacy_do_move_unreconcile(self, cr, uid, move_line_ids, currency, context=None):
415+ """
416+ Legacy method. Allow for canceling bank statement lines that
417+ were confirmed using earlier versions of the interactive wizard branch.
418+
419+ Undo a reconciliation, removing the given move line ids. If no
420+ meaningful (partial) reconciliation remains, delete it.
421+
422+ :param move_line_ids: List of ids. This will usually be the move
423+ line of an associated invoice or payment, plus optionally the
424+ move line of a writeoff.
425+ :param currency: A res.currency *browse* object to perform math
426+ operations on the amounts.
427+ """
428+ move_line_obj = self.pool.get('account.move.line')
429+ reconcile_obj = self.pool.get('account.move.reconcile')
430+ is_zero = lambda amount: self.pool.get('res.currency').is_zero(
431+ cr, uid, currency, amount)
432+ move_lines = move_line_obj.browse(cr, uid, move_line_ids, context=context)
433+ reconcile = move_lines[0].reconcile_id or move_lines[0].reconcile_partial_id
434+ line_ids = [x.id for x in reconcile.line_id or reconcile.line_partial_ids]
435+ for move_line_id in move_line_ids:
436+ line_ids.remove(move_line_id)
437+ if len(line_ids) > 1:
438+ full = is_zero(move_line_obj.get_balance(cr, uid, line_ids))
439+ if full:
440+ line_partial_ids = []
441+ else:
442+ line_partial_ids = list(line_ids)
443+ line_ids = []
444+ reconcile_obj.write(
445+ cr, uid, reconcile.id,
446+ { 'line_partial_ids': [(6, 0, line_partial_ids)],
447+ 'line_id': [(6, 0, line_ids)],
448+ }, context=context)
449+ else:
450+ reconcile_obj.unlink(cr, uid, reconcile.id, context=context)
451+ for move_line in move_lines:
452+ if move_line.invoice:
453+ # reopening the invoice
454+ netsvc.LocalService('workflow').trg_validate(
455+ uid, 'account.invoice', move_line.invoice.id, 'undo_paid', cr)
456+ return True
457+
458+ def _legacy_clear_up_writeoff(self, cr, uid, transaction, context=None):
459+ """
460+ Legacy method to support upgrades older installations of the
461+ interactive wizard branch. To be removed after 6.2
462+ clear up the writeoff move
463+ """
464+ if transaction.writeoff_move_line_id:
465+ move_pool = self.pool.get('account.move')
466+ move_pool.button_cancel(
467+ cr, uid, [transaction.writeoff_move_line_id.move_id.id],
468+ context=context)
469+ move_pool.unlink(
470+ cr, uid, [transaction.writeoff_move_line_id.move_id.id],
471+ context=context)
472+ return True
473+
474+ def _legacy_cancel_move(
475+ self, cr, uid, transaction, context=None):
476+ """
477+ Legacy method to support upgrades from older installations
478+ of the interactive wizard branch.
479+ """
480+>>>>>>> MERGE-SOURCE
481 currency = transaction.statement_line_id.statement_id.currency
482+<<<<<<< TREE
483 reconcile_id = (
484 transaction.move_line_id.reconcile_id and
485 transaction.move_line_id.reconcile_id.id or
486@@ -680,13 +845,42 @@
487 break
488 line_ids = [st_line_line.id]
489 # Add the write off line
490+=======
491+ line_ids = [transaction.move_line_id.id]
492+ statement_line_obj = self.pool.get('account.bank.statement.line')
493+>>>>>>> MERGE-SOURCE
494 if transaction.writeoff_move_line_id:
495 line_ids.append(transaction.writeoff_move_line_id.id)
496- self._do_move_unreconcile(
497+ self._legacy_do_move_unreconcile(
498 cr, uid, line_ids, currency, context=context)
499 statement_line_obj.write(
500 cr, uid, transaction.statement_line_id.id,
501 {'reconcile_id': False}, context=context)
502+
503+ def _cancel_voucher(
504+ self, cr, uid, transaction_id, context=None):
505+ voucher_pool = self.pool.get('account.voucher')
506+ transaction = self.browse(cr, uid, transaction_id, context=context)
507+ st_line = transaction.statement_line_id
508+ if transaction.match_type:
509+ if st_line.voucher_id:
510+ # Although vouchers can be associated with statement lines
511+ # in standard OpenERP, we consider ourselves owner of the voucher
512+ # if the line has an associated transaction
513+ # Upon canceling of the statement line/transaction,
514+ # we cancel and delete the vouchers.
515+ # Otherwise, the statement line will leave the voucher
516+ # unless the statement line itself is deleted.
517+ voucher_pool.cancel_voucher(
518+ cr, uid, [st_line.voucher_id.id], context=context)
519+ voucher_pool.action_cancel_draft(
520+ cr, uid, [st_line.voucher_id.id], context=context)
521+ voucher_pool.unlink(
522+ cr, uid, [st_line.voucher_id.id], context=context)
523+ # Allow canceling of legacy entries
524+ if not st_line.voucher_id and st_line.reconcile_id:
525+ self._legacy_cancel_move(cr, uid, transaction, context=context)
526+
527 return True
528
529 def _cancel_storno(
530@@ -714,11 +908,15 @@
531 else:
532 account_id = journal.default_debit_account_id.id
533 cancel_line = False
534- for line in transaction.statement_line_id.move_id.line_id:
535+ move_lines = []
536+ for move in transaction.statement_line_id.move_ids:
537+ # There should usually be just one move, I think
538+ move_lines += move.line_id
539+ for line in move_lines:
540 if line.account_id.id != account_id:
541 cancel_line = line
542 break
543- if not cancel_line: # debug
544+ if not cancel_line:
545 raise osv.except_osv(
546 _("Cannot cancel link with storno"),
547 _("Line id not found"))
548@@ -744,16 +942,16 @@
549
550 cancel_map = {
551 'storno': _cancel_storno,
552- 'invoice': _cancel_move,
553- 'manual': _cancel_move,
554- 'move': _cancel_move,
555+ 'invoice': _cancel_voucher,
556+ 'manual': _cancel_voucher,
557+ 'move': _cancel_voucher,
558 'payment_order': _cancel_payment_order,
559 'payment': _cancel_payment,
560 }
561+
562 def cancel(self, cr, uid, ids, context=None):
563 if ids and isinstance(ids, (int, float)):
564 ids = [ids]
565- move_obj = self.pool.get('account.move')
566 for transaction in self.browse(cr, uid, ids, context):
567 if not transaction.match_type:
568 continue
569@@ -763,31 +961,25 @@
570 _("No method found to cancel this type"))
571 self.cancel_map[transaction.match_type](
572 self, cr, uid, transaction.id, context)
573- # clear up the writeoff move
574- if transaction.writeoff_move_line_id:
575- move_obj.button_cancel(
576- cr, uid, [transaction.writeoff_move_line_id.move_id.id],
577- context=context)
578- move_obj.unlink(
579- cr, uid, [transaction.writeoff_move_line_id.move_id.id],
580- context=context)
581+ self._legacy_clear_up_writeoff(cr, uid, transaction, context=context)
582 return True
583
584- reconcile_map = {
585- 'storno': _reconcile_storno,
586- 'invoice': _reconcile_move,
587- 'manual': _reconcile_move,
588- 'payment_order': _reconcile_payment_order,
589- 'payment': _reconcile_payment,
590- 'move': _reconcile_move,
591+ confirm_map = {
592+ 'storno': _confirm_storno,
593+ 'invoice': _confirm_move,
594+ 'manual': _confirm_move,
595+ 'payment_order': _confirm_payment_order,
596+ 'payment': _confirm_payment,
597+ 'move': _confirm_move,
598 }
599- def reconcile(self, cr, uid, ids, context=None):
600+
601+ def confirm(self, cr, uid, ids, context=None):
602 if ids and isinstance(ids, (int, float)):
603 ids = [ids]
604 for transaction in self.browse(cr, uid, ids, context):
605 if not transaction.match_type:
606 continue
607- if transaction.match_type not in self.reconcile_map:
608+ if transaction.match_type not in self.confirm_map:
609 raise osv.except_osv(
610 _("Cannot reconcile"),
611 _("Cannot reconcile type %s. No method found to " +
612@@ -802,19 +994,11 @@
613 "this match type.") %
614 transaction.statement_line_id.name
615 )
616- self._generate_writeoff_move(
617- cr, uid, transaction.id, context=context)
618- # run the method that is appropriate for this match type
619- reconcile_id = self.reconcile_map[transaction.match_type](
620+ # Generalize this bit and move to the confirmation
621+ # methods that actually do create a voucher?
622+ self.confirm_map[transaction.match_type](
623 self, cr, uid, transaction.id, context)
624- self.pool.get('account.bank.statement.line').write(
625- cr, uid, transaction.statement_line_id.id,
626- {'reconcile_id': reconcile_id}, context=context)
627
628- # TODO
629- # update the statement line bank account reference
630- # as follows (from _match_invoice)
631-
632 """
633 account_ids = [
634 x.id for x in bank_account_ids
635@@ -822,64 +1006,7 @@
636 ][0]
637 """
638 return True
639-
640-
641- def _generate_writeoff_move(self, cr, uid, ids, context):
642- if ids and isinstance(ids, (int, float)):
643- ids = [ids]
644- move_line_obj = self.pool.get('account.move.line')
645- move_obj = self.pool.get('account.move')
646- for trans in self.browse(cr, uid, ids, context=context):
647- # Get the period for the company and date
648- ctxt = context.copy()
649- ctxt['company_id'] = trans.company_id.id
650- periods = self.pool.get('account.period').find(
651- cr, uid, trans.statement_line_id.date, context=ctxt)
652- period_id = periods and periods[0] or False
653-
654- move_id = move_obj.create(cr, uid, {
655- 'journal_id': trans.writeoff_journal_id.id,
656- 'period_id': period_id,
657- 'date': trans.statement_line_id.date,
658- 'name': '(write-off) %s' % (
659- trans.move_line_id.move_id.name or ''),
660- }, context=context)
661- if trans.residual > 0:
662- writeoff_debit = trans.residual
663- writeoff_credit = False
664- else:
665- writeoff_debit = False
666- writeoff_credit = - trans.residual
667- vals = {
668- 'name': trans.statement_line_id.name,
669- 'date': trans.statement_line_id.date,
670- 'ref': trans.statement_line_id.ref,
671- 'move_id': move_id,
672- 'partner_id': (trans.statement_line_id.partner_id and
673- trans.statement_line_id.partner_id.id or False),
674- 'account_id': trans.statement_line_id.account_id.id,
675- 'credit': writeoff_debit,
676- 'debit': writeoff_credit,
677- 'journal_id': trans.writeoff_journal_id.id,
678- 'period_id': period_id,
679- 'currency_id': trans.statement_line_id.statement_id.currency.id,
680- 'analytic_account_id': trans.writeoff_analytic_id.id,
681- }
682- move_line_id = move_line_obj.create(
683- cr, uid, vals, context=context)
684- self.write(
685- cr, uid, trans.id,
686- {'writeoff_move_line_id': move_line_id}, context=context)
687- vals.update({
688- 'account_id': trans.writeoff_account_id.id,
689- 'credit': writeoff_credit,
690- 'debit': writeoff_debit,
691- })
692- move_line_obj.create(
693- cr, uid, vals, context=context)
694- move_obj.post(
695- cr, uid, [move_id], context=context)
696-
697+
698 def _match_storno(
699 self, cr, uid, trans, log, context=None):
700 payment_line_obj = self.pool.get('payment.line')
701@@ -1251,7 +1378,7 @@
702 provision_costs_description = False,
703 ), context=context)
704 # rebrowse the current record after writing
705- transaction=self.browse(cr, uid, transaction.id, context=context)
706+ transaction = self.browse(cr, uid, transaction.id, context=context)
707 # Match full direct debit orders
708 if transaction.type == bt.DIRECT_DEBIT:
709 move_info = self._match_debit_order(
710@@ -1476,10 +1603,15 @@
711 """
712 if not ids:
713 return {}
714- res = {}
715+ res = dict([(x, False) for x in ids])
716 for transaction in self.browse(cr, uid, ids, context):
717- res[transaction.id] = abs(transaction.transferred_amount) - abs(transaction.move_currency_amount)
718-
719+ res[transaction.id] = (
720+ not(transaction.move_currency_amount is False)
721+ and (
722+ transaction.transferred_amount -
723+ transaction.move_currency_amount
724+ )
725+ or False)
726 return res
727
728 def _get_match_multi(self, cr, uid, ids, name, args, context=None):
729@@ -1530,12 +1662,11 @@
730 This will be used to calculate the write-off amount (in statement currency).
731 """
732 if not ids:
733- return {}
734+ return {}
735+ res = dict([(x, False) for x in ids])
736
737 stline_pool = self.pool.get('account.bank.statement.line')
738
739- res = {}
740-
741 for transaction in self.browse(cr, uid, ids, context):
742
743 if transaction.move_line_id:
744@@ -1632,15 +1763,25 @@
745 'invoice_id', 'transaction_id', 'Matching invoices'),
746 'invoice_id': fields.many2one(
747 'account.invoice', 'Invoice to reconcile'),
748- 'payment_line_id': fields.many2one('payment.line', 'Payment line'),
749 'residual': fields.function(
750 _get_residual, method=True, string='Residual', type='float'),
751 'writeoff_account_id': fields.many2one(
752 'account.account', 'Write-off account',
753 domain=[('type', '!=', 'view')]),
754- 'payment_option':fields.selection([('without_writeoff', 'Keep Open'),('with_writeoff', 'Reconcile Payment Balance')], 'Payment Difference',
755- 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)"),
756+ 'payment_option':fields.selection(
757+ [
758+ ('without_writeoff', 'Keep Open'),
759+ ('with_writeoff', 'Reconcile Payment Balance')
760+ ], 'Payment Difference',
761+ required=True,
762+ help=("This field helps you to choose what you want to do with "
763+ "the eventual difference between the paid amount and the "
764+ "sum of allocated amounts. You can either choose to keep "
765+ "open this difference on the partner's account, "
766+ "or reconcile it with the payment(s)"),
767+ ),
768 'writeoff_amount': fields.float('Difference Amount'),
769+ # Legacy field: to be removed after 6.2
770 'writeoff_move_line_id': fields.many2one(
771 'account.move.line', 'Write off move line'),
772 'writeoff_analytic_id': fields.many2one(
773@@ -1668,8 +1809,9 @@
774 'import_transaction_id', 'match_multi', type='boolean',
775 string='Multi match', readonly=True),
776 'residual': fields.related(
777- 'import_transaction_id', 'residual', type='float',
778- string='Residual'),
779+ 'import_transaction_id', 'residual', type='float',
780+ string='Residual', readonly=True,
781+ ),
782 'duplicate': fields.related(
783 'import_transaction_id', 'duplicate', type='boolean',
784 string='Possible duplicate import', readonly=True),
785@@ -1680,16 +1822,9 @@
786 ('payment_order', 'Payment order'),
787 ('storno', 'Storno')],
788 string='Match type', readonly=True,),
789- 'residual': fields.related(
790- 'import_transaction_id', 'residual', type='float',
791- string='Residual', readonly=True,
792- ),
793 'state': fields.selection(
794 [('draft', 'Draft'), ('confirmed', 'Confirmed')], 'State',
795 readonly=True, required=True),
796- 'move_id': fields.many2one(
797- 'account.move', 'Move', readonly=True,
798- help="The accounting move associated with this line"),
799 }
800
801 _defaults = {
802@@ -1710,8 +1845,9 @@
803 res = wizard_obj.create_act_window(cr, uid, res_id, context=context)
804 return res
805
806-
807- def _convert_currency(self, cr, uid, from_curr_id, to_curr_id, from_amount, round=False, date=None, context=None):
808+ def _convert_currency(
809+ self, cr, uid, from_curr_id, to_curr_id, from_amount,
810+ round=False, date=None, context=None):
811 """Convert currency amount using the company rate on a specific date"""
812 curr_obj = self.pool.get('res.currency')
813 if context:
814@@ -1721,10 +1857,11 @@
815 if date:
816 ctxt["date"] = date
817
818- amount = curr_obj.compute(cr, uid, from_curr_id, to_curr_id, from_amount, round=round, context=ctxt)
819+ amount = curr_obj.compute(
820+ cr, uid, from_curr_id, to_curr_id, from_amount,
821+ round=round, context=ctxt)
822 return amount
823
824-
825 def confirm(self, cr, uid, ids, context=None):
826 """
827 Create (or update) a voucher for each statement line, and then generate
828@@ -1732,10 +1869,10 @@
829 If a line does not have a move line against it, but has an account, then
830 generate a journal entry that moves the line amount to the specified account.
831 """
832- voucher_pool = self.pool.get('account.voucher')
833 statement_pool = self.pool.get('account.bank.statement')
834 obj_seq = self.pool.get('ir.sequence')
835 move_pool = self.pool.get('account.move')
836+ import_transaction_obj = self.pool.get('banking.import.transaction')
837
838 for st_line in self.browse(cr, uid, ids, context):
839 if st_line.state != 'draft':
840@@ -1754,157 +1891,47 @@
841 "journal!") % (st_line.statement_id.journal_id.name,))
842 if not st_line.amount:
843 continue
844-
845+ if not st_line.period_id:
846+ self.write(
847+ cr, uid, [st_line.id], {
848+ 'period_id': self._get_period(
849+ cr, uid, {'date': st_line.date})
850+ })
851+ st_line.refresh()
852 # Generate the statement number, if it is not already done
853 st = st_line.statement_id
854 if not st.name == '/':
855 st_number = st.name
856 else:
857 if st.journal_id.sequence_id:
858- c = {'fiscalyear_id': st.period_id.fiscalyear_id.id}
859+ period = st.period_id or st_line.period_id
860+ c = {'fiscalyear_id': period.fiscalyear_id.id}
861 st_number = obj_seq.next_by_id(cr, uid, st.journal_id.sequence_id.id, context=c)
862 else:
863 st_number = obj_seq.next_by_code(cr, uid, 'account.bank.statement')
864- statement_obj.write(cr, uid, [st.id], {'name': st_number}, context=context)
865-
866- # Check if this line has been matched against a journal item
867- if st_line.import_transaction_id.move_line_id:
868- # Line has been matched, so post it via a voucher
869- self._post_with_voucher(cr, uid, ids, st_line, voucher_pool, move_pool, context=context)
870-
871- else:
872- # Check to see if the line has an account that can be used to generate a journal entry
873- if st_line.account_id:
874- # Generate a journal for the entry using the standard bank statement code
875- self._create_move(cr, uid, st_line, statement_pool, st, st_number, context=context)
876- else:
877- raise osv.except_osv(
878- _('Statement line has no account'),
879- _("You cannot confirm a bank statement line that has no account against it (%s.%s)") %
880- (st_line.statement_id.name, st_line.name,))
881-
882+ statement_pool.write(cr, uid, [st.id], {'name': st_number}, context=context)
883+
884+ if st_line.import_transaction_id:
885+ import_transaction_obj.confirm(
886+ cr, uid, st_line.import_transaction_id.id, context)
887+ st_line.refresh()
888+ st_line_number = statement_pool.get_next_st_line_number(
889+ cr, uid, st_number, st_line, context)
890+ company_currency_id = st.journal_id.company_id.currency_id.id
891+ statement_pool.create_move_from_st_line(
892+ cr, uid, st_line.id, company_currency_id, st_line_number, context)
893+ self.write(
894+ cr, uid, st_line.id, {'state': 'confirmed'}, context)
895 return True
896
897- def _create_move(self, cr, uid, st_line, statement_pool, st, st_number, context):
898- """
899- The line is not matched against a move, but the account has been defined for the line.
900- Generate a journal entry for the statement line that transfers the full amount against the account.
901- """
902- st_line_number = statement_pool.get_next_st_line_number(cr, uid, st_number, st_line, context)
903- company_currency_id = st.journal_id.company_id.currency_id.id
904- move_id = statement_pool.create_move_from_st_line(cr, uid, st_line.id, company_currency_id, st_line_number, context)
905- self.write(cr, uid, st_line.id, {'state': 'confirmed', 'move_id': move_id}, context)
906-
907-
908- def _post_with_voucher(self, cr, uid, ids, st_line, voucher_pool, move_pool, context):
909- # Check if a voucher has already been created
910- if st_line.voucher_id:
911- # There's an existing voucher on the statement line which was probably created
912- # manually. This may have been done because it was a single payment for multiple
913- # invoices. Just get the voucher ID.
914- voucher_id = st_line.voucher_id.id
915- else:
916- # Create a voucher and store the ID on the statement line
917- voucher_id = self._create_voucher(cr, uid, ids, st_line, context=context)
918- self.pool.get('account.bank.statement.line').write(cr,uid,st_line.id,{'voucher_id':voucher_id} , context=context)
919-
920- # If the voucher is in draft mode, then post it
921- voucher = voucher_pool.browse(cr, uid, voucher_id, context=context)
922- if voucher.state in ('draft','proforma'):
923- voucher.action_move_line_create()
924-
925- # Update the statement line to indicate that it has been posted
926- # ... no longer need to set the move_id on the voucher?
927- self.write(cr, uid, st_line.id, {'state': 'confirmed'}, context)
928-
929- # The voucher journal isn't automatically posted, so post it (if needed)
930- if not voucher.journal_id.entry_posted:
931- voucher = voucher_pool.browse(cr, uid, voucher_id, context=context)
932- move_pool.post(cr, uid, [voucher.move_id.id], context={})
933-
934-
935- def _create_voucher(self, cr, uid, ids, st_line, context):
936- """
937- The line is matched against a move (invoice), so generate a payment voucher with the write-off settings that the
938- user requested. The move lines will be generated by the voucher, handling rounding and currency conversion.
939- """
940- journal = st_line.statement_id.journal_id
941- if st_line.amount < 0.0:
942- voucher_type = 'payment'
943- account_id = journal.default_debit_account_id and journal.default_debit_account_id.id or False
944- else:
945- voucher_type = 'receipt'
946- account_id = journal.default_credit_account_id and journal.default_credit_account_id.id or False
947-
948- # Use the statement line's date determine the period
949- ctxt = context.copy()
950- ctxt['company_id'] = st_line.company_id.id
951- if 'period_id' in ctxt:
952- del ctxt['period_id']
953- period_id = self.pool.get('account.period').find(cr, uid, st_line.date, context=ctxt)[0]
954-
955- # Convert the move line amount to the journal currency
956- move_line_amount = st_line.import_transaction_id.move_line_id.amount_residual_currency
957- 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
958- 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
959- if from_curr_id != to_curr_id:
960- amount_currency = self._convert_currency(cr, uid, from_curr_id, to_curr_id, move_line_amount, round=True,
961- date=time.strftime('%Y-%m-%d'), context=context)
962- else:
963- amount_currency = move_line_amount
964-
965- # Check whether this is a full or partial reconciliation
966- if st_line.import_transaction_id.payment_option=='with_writeoff':
967- writeoff = abs(st_line.amount)-abs(amount_currency)
968- line_amount = abs(amount_currency)
969- else:
970- writeoff = 0.0
971- line_amount = abs(st_line.amount)
972-
973- # Define the voucher
974- voucher = {
975- 'journal_id': st_line.statement_id.journal_id.id,
976- 'partner_id': st_line.partner_id and st_line.partner_id.id or False,
977- 'company_id': st_line.company_id.id,
978- 'type':voucher_type,
979- 'company_id': st_line.company_id.id,
980- 'account_id': account_id,
981- 'amount': abs(st_line.amount),
982- 'writeoff_amount': writeoff,
983- 'payment_option': st_line.import_transaction_id.payment_option,
984- 'writeoff_acc_id': st_line.import_transaction_id.writeoff_account_id.id,
985- 'analytic_id': st_line.import_transaction_id.writeoff_analytic_id.id,
986- 'date': st_line.date,
987- 'date_due': st_line.date,
988- 'period_id': period_id,
989- 'payment_rate_currency_id':to_curr_id,
990- }
991-
992- # Define the voucher line
993- vch_line = {
994- #'voucher_id': v_id,
995- 'move_line_id': st_line.import_transaction_id.move_line_id.id,
996- 'reconcile': True,
997- 'amount': line_amount,
998- 'account_id': st_line.import_transaction_id.move_line_id.account_id.id,
999- 'type': st_line.import_transaction_id.move_line_id.credit and 'dr' or 'cr',
1000- }
1001- voucher['line_ids'] = [(0,0,vch_line)]
1002- v_id = self.pool.get('account.voucher').create(cr, uid, voucher, context=context)
1003-
1004- return v_id
1005-
1006-
1007 def cancel(self, cr, uid, ids, context=None):
1008 if ids and isinstance(ids, (int, float)):
1009 ids = [ids]
1010- account_move_obj = self.pool.get('account.move')
1011 import_transaction_obj = self.pool.get('banking.import.transaction')
1012- voucher_pool = self.pool.get('account.voucher')
1013+ move_pool = self.pool.get('account.move')
1014 transaction_cancel_ids = []
1015- voucher_cancel_ids = []
1016+ set_draft_ids = []
1017 move_unlink_ids = []
1018- set_draft_ids = []
1019 # harvest ids for various actions
1020 for st_line in self.browse(cr, uid, ids, context):
1021 if st_line.state != 'confirmed':
1022@@ -1915,32 +1942,23 @@
1023 _("The bank statement that this transaction belongs to has "
1024 "already been confirmed"))
1025
1026- # Check if the transaction has a voucher
1027- if st_line.voucher_id:
1028- voucher_cancel_ids.append(st_line.voucher_id.id)
1029- else:
1030- if st_line.import_transaction_id:
1031- transaction_cancel_ids.append(st_line.import_transaction_id.id)
1032- if st_line.move_id:
1033- move_unlink_ids.append(st_line.move_id.id)
1034- else:
1035- raise osv.except_osv(
1036- _("Cannot cancel bank transaction"),
1037- _("Cannot cancel this bank transaction. The information "
1038- "needed to undo the accounting entries has not been "
1039- "recorded"))
1040+ if st_line.import_transaction_id:
1041+ # Cancel transaction immediately.
1042+ # If it has voucher, this will clean up
1043+ # the moves on the st_line.
1044+ import_transaction_obj.cancel(
1045+ cr, uid, [st_line.import_transaction_id.id], context=context)
1046+ st_line.refresh()
1047+ for line in st_line.move_ids:
1048+ # We allow for people canceling and removing
1049+ # the associated payments, which can lead to confirmed
1050+ # statement lines without an associated move
1051+ move_unlink_ids.append(line.id)
1052 set_draft_ids.append(st_line.id)
1053
1054- # Cancel and delete the vouchers
1055- voucher_pool.cancel_voucher(cr, uid, voucher_cancel_ids, context=context)
1056- voucher_pool.action_cancel_draft(cr, uid, voucher_cancel_ids, context=context)
1057- voucher_pool.unlink(cr, uid, voucher_cancel_ids, context=context)
1058-
1059- # Perform actions
1060- import_transaction_obj.cancel(
1061- cr, uid, transaction_cancel_ids, context=context)
1062- account_move_obj.button_cancel(cr, uid, move_unlink_ids, context)
1063- account_move_obj.unlink(cr, uid, move_unlink_ids, context)
1064+ move_pool.button_cancel(
1065+ cr, uid, move_unlink_ids, context=context)
1066+ move_pool.unlink(cr, uid, move_unlink_ids, context=context)
1067 self.write(
1068 cr, uid, set_draft_ids, {'state': 'draft'}, context=context)
1069 return True
1070@@ -1954,8 +1972,12 @@
1071 ids = [ids]
1072 for line in self.browse(cr, uid, ids, context=context):
1073 if line.state == 'confirmed':
1074- raise osv.except_osv(_('Confirmed Statement Line'), _("You cannot delete a confirmed Statement Line: '%s'" % line.name))
1075- return super(account_bank_statement_line,self).unlink(cr, uid, ids, context=context)
1076+ raise osv.except_osv(
1077+ _('Confirmed Statement Line'),
1078+ _("You cannot delete a confirmed Statement Line"
1079+ ": '%s'" % line.name))
1080+ return super(account_bank_statement_line, self).unlink(
1081+ cr, uid, ids, context=context)
1082
1083 account_bank_statement_line()
1084
1085@@ -2000,7 +2022,7 @@
1086
1087 # protect against misguided manual changes
1088 for line in st.move_line_ids:
1089- if line.state <> 'valid':
1090+ if line.state != 'valid':
1091 raise osv.except_osv(_('Error !'),
1092 _('The account entries lines are not in valid state.'))
1093
1094
1095=== modified file 'account_banking/wizard/banking_transaction_wizard.py'
1096--- account_banking/wizard/banking_transaction_wizard.py 2012-03-07 23:02:52 +0000
1097+++ account_banking/wizard/banking_transaction_wizard.py 2012-05-03 11:53:24 +0000
1098@@ -21,6 +21,7 @@
1099 #
1100 ##############################################################################
1101 from osv import osv, fields
1102+from openerp.tools.translate import _
1103
1104 """
1105
1106@@ -38,7 +39,7 @@
1107 """
1108 Return a popup window for this model
1109 """
1110- if isinstance(ids, (int,long)):
1111+ if isinstance(ids, (int, long)):
1112 ids = [ids]
1113 return {
1114 'name': self._description,
1115@@ -160,9 +161,7 @@
1116 statement_line_obj = self.pool.get('account.bank.statement.line')
1117 for wiz in self.browse(
1118 cr, uid, ids, context=context):
1119- invoice_ids = False
1120 move_line_id = False
1121- move_line_ids = False
1122 invoice_id = manual_invoice_id
1123 if invoice_id:
1124 invoice = invoice_obj.browse(
1125
1126=== modified file 'account_direct_debit/model/account_payment.py'
1127--- account_direct_debit/model/account_payment.py 2012-01-12 10:58:49 +0000
1128+++ account_direct_debit/model/account_payment.py 2012-05-03 11:53:24 +0000
1129@@ -104,7 +104,6 @@
1130 _("Cannot unreconcile"),
1131 _("Cannot unreconcile debit order: "+
1132 "Workflow will not allow it."))
1133-
1134 return True
1135
1136 def test_undo_done(self, cr, uid, ids, context=None):
1137
1138=== modified file 'account_direct_debit/view/account_invoice.xml'
1139--- account_direct_debit/view/account_invoice.xml 2011-12-21 13:06:26 +0000
1140+++ account_direct_debit/view/account_invoice.xml 2012-05-03 11:53:24 +0000
1141@@ -13,9 +13,9 @@
1142 Maybe apply trick in fields_view_get instead, for
1143 better compatibility with other modules?
1144 -->
1145- <button name="invoice_open" position="attributes">
1146+ <!-- button name="invoice_open" position="attributes">
1147 <attribute name="states">draft,proforma2,debit_denied</attribute>
1148- </button>
1149+ </button -->
1150 <button string='Re-Open' position="attributes">
1151 <attribute name="states">paid,debit_denied</attribute>
1152 <!--
1153
1154=== modified file 'account_direct_debit/workflow/account_invoice.xml'
1155--- account_direct_debit/workflow/account_invoice.xml 2011-12-21 13:06:26 +0000
1156+++ account_direct_debit/workflow/account_invoice.xml 2012-05-03 11:53:24 +0000
1157@@ -8,20 +8,44 @@
1158 <field name="kind">function</field>
1159 </record>
1160 <record id="paid_to_debit_denied" model="workflow.transition">
1161+ <!--
1162+ Set an invoice to state debit denied, either manually
1163+ or by confirming a bank statement line that constitutes
1164+ a fatal storno
1165+ -->
1166 <field name="act_from" ref="account.act_paid"/>
1167 <field name="act_to" ref="act_debit_denied"/>
1168 <field name="signal">invoice_debit_denied</field>
1169 </record>
1170+ <record id="open_test_to_debit_denied" model="workflow.transition">
1171+ <!--
1172+ A storno leads to unreconciling the move line, which
1173+ reopens the invoice. We need to allow a transition from
1174+ this state to the debit denied state if the storno is fatal.
1175+ -->
1176+ <field name="act_from" ref="account.act_open_test"/>
1177+ <field name="act_to" ref="act_debit_denied"/>
1178+ <field name="signal">invoice_debit_denied</field>
1179+ </record>
1180 <record id="debit_denied_to_paid" model="workflow.transition">
1181+ <!--
1182+ Cancel a bank statement line that constitutes a fatal
1183+ storno
1184+ -->
1185 <field name="act_from" ref="act_debit_denied"/>
1186 <field name="act_to" ref="account.act_paid"/>
1187 <field name="condition">test_undo_debit_denied()</field>
1188 <field name="signal">undo_debit_denied</field>
1189 </record>
1190 <record id="debit_denied_to_open" model="workflow.transition">
1191+ <!--
1192+ Allow the user to manually reset a debit denied status
1193+ on a paid invoice (but only after manually unreconciling
1194+ the invoice)
1195+ -->
1196 <field name="act_from" ref="act_debit_denied"/>
1197- <field name="act_to" ref="account.act_open"/>
1198- <field name="signal">invoice_open</field>
1199+ <field name="act_to" ref="account.act_open_test"/>
1200+ <field name="signal">open_test</field>
1201 </record>
1202 </data>
1203 </openerp>

Subscribers

People subscribed via source and target branches