Merge lp:~therp-nl/banking-addons/ba7.0-MIG-payment into lp:banking-addons

Proposed by Stefan Rijnhart (Opener)
Status: Merged
Merged at revision: 174
Proposed branch: lp:~therp-nl/banking-addons/ba7.0-MIG-payment
Merge into: lp:banking-addons
Prerequisite: lp:~therp-nl/banking-addons/ba7.0-MIG-import
Diff against target: 4032 lines (+1174/-1209)
48 files modified
account_banking/account_banking.py (+26/-26)
account_banking/account_banking_view.xml (+0/-9)
account_banking/banking_import_transaction.py (+84/-85)
account_banking/wizard/bank_import.py (+16/-16)
account_banking/wizard/bank_import_view.xml (+0/-1)
account_banking/wizard/banking_transaction_wizard.py (+40/-66)
account_banking/wizard/banking_transaction_wizard.xml (+0/-1)
account_banking/wizard/banktools.py (+18/-18)
account_banking_nl_clieop/__openerp__.py (+1/-1)
account_banking_nl_clieop/account_banking_nl_clieop.py (+7/-8)
account_banking_nl_clieop/account_banking_nl_clieop.xml (+0/-2)
account_banking_nl_clieop/wizard/export_clieop.py (+42/-48)
account_banking_nl_clieop/wizard/export_clieop_view.xml (+0/-1)
account_banking_nl_girotel/girotel.py (+14/-2)
account_banking_payment/__openerp__.py (+2/-1)
account_banking_payment/model/__init__.py (+0/-1)
account_banking_payment/model/account_bank_statement_line.py (+0/-40)
account_banking_payment/model/account_payment.py (+166/-46)
account_banking_payment/model/banking_import_transaction.py (+56/-46)
account_banking_payment/model/banking_transaction_wizard.py (+58/-0)
account_banking_payment/model/payment_line.py (+91/-129)
account_banking_payment/model/payment_mode.py (+21/-0)
account_banking_payment/model/payment_order_create.py (+74/-5)
account_banking_payment/view/account_payment.xml (+16/-21)
account_banking_payment/view/bank_payment_manual.xml (+1/-2)
account_banking_payment/view/banking_transaction_wizard.xml (+11/-9)
account_banking_payment/view/payment_mode.xml (+43/-0)
account_banking_payment/view/payment_mode_type.xml (+1/-16)
account_banking_payment/workflow/account_payment.xml (+40/-9)
account_banking_uk_hsbc/__openerp__.py (+1/-1)
account_direct_debit/__openerp__.py (+4/-5)
account_direct_debit/i18n/nl.po (+2/-2)
account_direct_debit/migrations/7.0.2/pre-migration.py (+57/-0)
account_direct_debit/model/__init__.py (+2/-0)
account_direct_debit/model/account_invoice.py (+26/-5)
account_direct_debit/model/account_move_line.py (+7/-67)
account_direct_debit/model/account_payment.py (+5/-434)
account_direct_debit/model/payment_line.py (+152/-0)
account_direct_debit/model/payment_order_create.py (+41/-0)
account_direct_debit/view/account_invoice.xml (+5/-10)
account_direct_debit/view/account_payment.xml (+2/-28)
account_direct_debit/workflow/account_payment.xml (+0/-25)
account_payment_shortcut/__init__.py (+1/-0)
account_payment_shortcut/payment_order.py (+26/-6)
bank_statement_instant_voucher/model/account_bank_statement_line.py (+5/-5)
bank_statement_instant_voucher/model/account_voucher_instant.py (+10/-10)
bank_statement_instant_voucher/view/account_bank_statement_line.xml (+0/-1)
bank_statement_instant_voucher/view/account_voucher_instant.xml (+0/-1)
To merge this branch: bzr merge lp:~therp-nl/banking-addons/ba7.0-MIG-payment
Reviewer Review Type Date Requested Status
Holger Brunn (Therp) Approve
Review via email: mp+166451@code.launchpad.net

Commit message

[MRG] Migration of payment modules

Description of the change

- Apply invoice integration of debit orders also to payment orders
- Allow for manual reconciliation of payment and debit orders
- Update payment order workflow so as to allow for unreconciliation
- Consistency in usage of cr and uid variable names
- Adapt to API changes
- Removed obsolete workaround for old bugs in OpenERP server
- Remove unused code paths

For migrations from 6.1, I am still considering how to provide an upgrade path that migrates payment order workflow instances in activity 'sent' to the new 'sent_wait' state.

To post a comment you must log in.
Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

just a few small things, approving anyways:

#102 maybe use the opportunity to make this function contaxt-aware?
#140ff same with passing context in the onchange_functions
#550 570, 880 considering how often you use this selection, you should move it to its own field. This also simplifies adjusting the selection
#1591 check if there are line_ids
#1682 should be translated
#1692 too
#1784 why can we drop this check? shouldn't it be changed to check for transit_move_line_id?

Could you elaborate on the sent_wait state? If it's meant how I think it is, such a migration seems not necessary to me.

review: Approve
Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

should I wait with merging this one until you had a look at my points?

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

Yes, will do so this week! Thanks for the review.

227. By Stefan Rijnhart (Opener)

[MGR] HSBC module depends on split off payment part

228. By Stefan Rijnhart (Opener)

[FIX] Add context to browse calls in account_banking.py

229. By Stefan Rijnhart (Opener)

[FIX] Improve translatable labels for move and move lines

230. By Stefan Rijnhart (Opener)

[MRG] Merged with target branch

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

Holger, thanks for your comments. I tried to address them in my latest commits or the comments below.

> #550 570, 880 considering how often you use this selection, you should move it to its own field. This also
> simplifies adjusting the selection

I'll leave it for now as I want to refactor it later on to make the selection options more dynamic.

> #1591 check if there are line_ids

As this code path occurs after a match with a bank transaction on amount, an absence of line_ids should not occur as the payment order amount would be zero as would the transaction amount.

> #1784 why can we drop this check? shouldn't it be changed to check for transit_move_line_id?

As per l.1591, implement old style behaviour for legacy payment orders depending on the presence of transit_move_line_id.

The sent state implements the transit move. Making it go into sent_wait allows the reconciliation process to cancel and reconfirm confirmed matches. Before I introduced the latter state and modified the workflow to go from 'done' to 'sent', I could not reconfirm a match on a payment order, because then the workflow would attempt to create the transit move again. I'd think that a manual migration for wkf instances currently in the 'sent' state should be necessary, or they might get stuck at that state.

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 2013-04-29 09:17:46 +0000
3+++ account_banking/account_banking.py 2013-06-26 21:16:23 +0000
4@@ -267,7 +267,7 @@
5 }
6 _defaults = {
7 'date': fields.date.context_today,
8- 'user_id': lambda self, cursor, uid, context: uid,
9+ 'user_id': lambda self, cr, uid, context: uid,
10 }
11 account_banking_imported_file()
12
13@@ -320,12 +320,12 @@
14 ['journal_id','period_id']),
15 ]
16
17- def _get_period(self, cursor, uid, date, context=None):
18+ def _get_period(self, cr, uid, date, context=None):
19 '''
20 Find matching period for date, not meant for _defaults.
21 '''
22 period_obj = self.pool.get('account.period')
23- periods = period_obj.find(cursor, uid, dt=date, context=context)
24+ periods = period_obj.find(cr, uid, dt=date, context=context)
25 return periods and periods[0] or False
26
27 def _prepare_move(
28@@ -398,7 +398,7 @@
29 # Write stored reconcile_id and pay invoices through workflow
30 if st_line.reconcile_id:
31 move_ids = [move.id for move in st_line.move_ids]
32- torec = account_move_obj.search(
33+ torec = account_move_line_obj.search(
34 cr, uid, [
35 ('move_id', 'in', move_ids),
36 ('account_id', '=', st_line.account_id.id)],
37@@ -450,7 +450,7 @@
38 context = {}
39 if not context.get('period_id') and context.get('move_line_ids'):
40 return self.pool.get('account.move.line').browse(
41- cr, uid , context.get('move_line_ids'))[0].period_id.id
42+ cr, uid , context.get('move_line_ids'), context=context)[0].period_id.id
43 return super(account_voucher, self)._get_period(cr, uid, context)
44
45 account_voucher()
46@@ -467,12 +467,12 @@
47 _inherit = 'account.bank.statement.line'
48 _description = 'Bank Transaction'
49
50- def _get_period(self, cursor, user, context=None):
51+ def _get_period(self, cr, uid, context=None):
52 date = context.get('date', None)
53- periods = self.pool.get('account.period').find(cursor, user, dt=date)
54+ periods = self.pool.get('account.period').find(cr, uid, dt=date)
55 return periods and periods[0] or False
56
57- def _get_currency(self, cursor, user, context=None):
58+ def _get_currency(self, cr, uid, context=None):
59 '''
60 Get the default currency (required to allow other modules to function,
61 which assume currency to be a calculated field and thus optional)
62@@ -480,7 +480,7 @@
63 which is inaccessible from within this method.
64 '''
65 res_users_obj = self.pool.get('res.users')
66- return res_users_obj.browse(cursor, user, user,
67+ return res_users_obj.browse(cr, uid, uid,
68 context=context).company_id.currency_id.id
69
70 def _get_invoice_id(self, cr, uid, ids, name, args, context=None):
71@@ -605,7 +605,7 @@
72 iban = sepa.IBAN(acc_number)
73 return (str(iban), iban.localized_BBAN)
74
75- def create(self, cursor, uid, vals, context=None):
76+ def create(self, cr, uid, vals, context=None):
77 '''
78 Create dual function IBAN account for SEPA countries
79 '''
80@@ -614,7 +614,7 @@
81 or vals.get('acc_number_domestic', False))
82 vals['acc_number'], vals['acc_number_domestic'] = (
83 self._correct_IBAN(iban))
84- return self._founder.create(self, cursor, uid, vals, context)
85+ return self._founder.create(self, cr, uid, vals, context)
86
87 def write(self, cr, uid, ids, vals, context=None):
88 '''
89@@ -637,7 +637,7 @@
90 self._founder.write(self, cr, uid, account['id'], vals, context)
91 return True
92
93- def search(self, cursor, uid, args, *rest, **kwargs):
94+ def search(self, cr, uid, args, *rest, **kwargs):
95 '''
96 Overwrite search, as both acc_number and iban now can be filled, so
97 the original base_iban 'search and search again fuzzy' tactic now can
98@@ -698,7 +698,7 @@
99
100 # Original search
101 results = super(res_partner_bank, self).search(
102- cursor, uid, newargs, *rest, **kwargs)
103+ cr, uid, newargs, *rest, **kwargs)
104 return results
105
106 def read(
107@@ -721,23 +721,23 @@
108 return records
109 return records[0]
110
111- def check_iban(self, cursor, uid, ids):
112+ def check_iban(self, cr, uid, ids, context=None):
113 '''
114 Check IBAN number
115 '''
116- for bank_acc in self.browse(cursor, uid, ids):
117+ for bank_acc in self.browse(cr, uid, ids, context=context):
118 if bank_acc.state == 'iban' and bank_acc.acc_number:
119 iban = sepa.IBAN(bank_acc.acc_number)
120 if not iban.valid:
121 return False
122 return True
123
124- def get_bban_from_iban(self, cursor, uid, ids, context=None):
125+ def get_bban_from_iban(self, cr, uid, ids, context=None):
126 '''
127 Return the local bank account number aka BBAN from the IBAN.
128 '''
129 res = {}
130- for record in self.browse(cursor, uid, ids, context):
131+ for record in self.browse(cr, uid, ids, context):
132 if not record.state == 'iban':
133 res[record.id] = False
134 else:
135@@ -763,7 +763,7 @@
136 )
137
138 def onchange_domestic(
139- self, cursor, uid, ids, acc_number,
140+ self, cr, uid, ids, acc_number,
141 partner_id, country_id, context=None):
142 '''
143 Trigger to find IBAN. When found:
144@@ -785,7 +785,7 @@
145 # which can be overridden by the user.
146 # 1. Use provided country_id (manually filled)
147 if country_id:
148- country = country_obj.browse(cursor, uid, country_id)
149+ country = country_obj.browse(cr, uid, country_id, context=context)
150 country_ids = [country_id]
151 # 2. Use country_id of found bank accounts
152 # This can be usefull when there is no country set in the partners
153@@ -793,7 +793,7 @@
154 # account itself before this method was triggered.
155 elif ids and len(ids) == 1:
156 partner_bank_obj = self.pool.get('res.partner.bank')
157- partner_bank_id = partner_bank_obj.browse(cursor, uid, ids[0])
158+ partner_bank_id = partner_bank_obj.browse(cr, uid, ids[0], context=context)
159 if partner_bank_id.country_id:
160 country = partner_bank_id.country_id
161 country_ids = [country.id]
162@@ -804,12 +804,12 @@
163 # bank account, hence the additional check.
164 elif partner_id:
165 partner_obj = self.pool.get('res.partner')
166- country = partner_obj.browse(cursor, uid, partner_id).country
167+ country = partner_obj.browse(cr, uid, partner_id, context=context).country
168 country_ids = country and [country.id] or []
169 # 4. Without any of the above, take the country from the company of
170 # the handling user
171 if not country_ids:
172- user = self.pool.get('res.users').browse(cursor, uid, uid)
173+ user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
174 # Try user companies partner (user no longer has address in 6.1)
175 if (user.company_id and
176 user.company_id.partner_id and
177@@ -830,7 +830,7 @@
178 # Complete data with online database when available
179 if country_ids:
180 country = country_obj.browse(
181- cursor, uid, country_ids[0], context=context)
182+ cr, uid, country_ids[0], context=context)
183 values['country_id'] = country_ids[0]
184 if country and country.code in sepa.IBAN.countries:
185 try:
186@@ -842,7 +842,7 @@
187 values['acc_number'] = unicode(iban_acc)
188 values['state'] = 'iban'
189 bank_id, country_id = get_or_create_bank(
190- self.pool, cursor, uid,
191+ self.pool, cr, uid,
192 info.bic or iban_acc.BIC_searchkey,
193 name = info.bank
194 )
195@@ -911,7 +911,7 @@
196 '''
197 _inherit = 'res.bank'
198
199- def onchange_bic(self, cursor, uid, ids, bic, name, context=None):
200+ def onchange_bic(self, cr, uid, ids, bic, name, context=None):
201 '''
202 Trigger to auto complete other fields.
203 '''
204@@ -924,7 +924,7 @@
205
206 if address and address.country_id:
207 country_id = self.pool.get('res.country').search(
208- cursor, uid, [('code','=',address.country_id)]
209+ cr, uid, [('code','=',address.country_id)]
210 )
211 country_id = country_id and country_id[0] or False
212 else:
213
214=== modified file 'account_banking/account_banking_view.xml'
215--- account_banking/account_banking_view.xml 2013-05-01 14:25:04 +0000
216+++ account_banking/account_banking_view.xml 2013-06-26 21:16:23 +0000
217@@ -36,7 +36,6 @@
218 <record model="ir.ui.view" id="view_banking_account_settings_form">
219 <field name="name">account.banking.account.settings.form</field>
220 <field name="model">account.banking.account.settings</field>
221- <field name="type">form</field>
222 <field name="arch" type="xml">
223 <form string="Default Import Settings for Bank Account">
224 <field name="company_id"
225@@ -70,7 +69,6 @@
226 <record model="ir.ui.view" id="view_banking_account_settings_tree">
227 <field name="name">account.banking.account.settings.tree</field>
228 <field name="model">account.banking.account.settings</field>
229- <field name="type">tree</field>
230 <field name="arch" type="xml">
231 <tree string="Default Import Settings for Bank Account">
232 <field name="company_id" />
233@@ -99,7 +97,6 @@
234 <record model="ir.ui.view" id="view_account_banking_imported_file_form">
235 <field name="name">account.banking.imported.file.form</field>
236 <field name="model">account.banking.imported.file</field>
237- <field name="type">form</field>
238 <field name="arch" type="xml">
239 <form string="Imported Bank Statements">
240 <notebook colspan="4">
241@@ -124,7 +121,6 @@
242 <record model="ir.ui.view" id="view_account_banking_imported_file_tree">
243 <field name="name">account.banking.imported.file.tree</field>
244 <field name="model">account.banking.imported.file</field>
245- <field name="type">tree</field>
246 <field name="arch" type="xml">
247 <tree string="Imported Bank Statements Files" colors="red:state=='error';blue:state=='unfinished'">
248 <field name="company_id" />
249@@ -182,7 +178,6 @@
250 <field name="name">account.bank.statement.tree.banking</field>
251 <field name="inherit_id" ref="account.view_bank_statement_tree" />
252 <field name="model">account.bank.statement</field>
253- <field name="type">tree</field>
254 <field name="arch" type="xml">
255 <!-- Remove period from bank statement -->
256 <field name="period_id" position="replace">
257@@ -197,7 +192,6 @@
258 <field name="inherit_id" ref="account.view_bank_statement_form" />
259 <field name="model">account.bank.statement</field>
260 <field name="sequence" eval="60"/>
261- <field name="type">form</field>
262 <field name="arch" type="xml">
263 <data>
264 <page string="Transactions" position="after">
265@@ -292,7 +286,6 @@
266 <field name="name">res.partner.bank.form.banking-2</field>
267 <field name="model">res.partner.bank</field>
268 <field name="inherit_id" ref="base.view_partner_bank_form"/>
269- <field name="type">form</field>
270 <field name="priority" eval="24"/>
271 <field name="arch" type="xml">
272 <data>
273@@ -311,7 +304,6 @@
274 <field name="name">res.bank.form.banking-1</field>
275 <field name="model">res.bank</field>
276 <field name="inherit_id" ref="base.view_res_bank_form"/>
277- <field name="type">form</field>
278 <field name="arch" type="xml">
279 <field name="bic" position="replace">
280 <field name="bic" on_change="onchange_bic(bic, name)"/>
281@@ -322,7 +314,6 @@
282 <record model="ir.ui.view" id="view_bank_statement_line_tree">
283 <field name="name">Bank statement line tree view</field>
284 <field name="model">account.bank.statement.line</field>
285- <field name="type">tree</field>
286 <field name="arch" type="xml">
287 <tree string="Statement lines" colors="black:state == 'confirmed';darkmagenta:match_multi == True;crimson:duplicate == True;grey:state=='draft';">
288 <field name="sequence" readonly="1" invisible="1"/>
289
290=== modified file 'account_banking/banking_import_transaction.py'
291--- account_banking/banking_import_transaction.py 2013-06-10 10:14:31 +0000
292+++ account_banking/banking_import_transaction.py 2013-06-26 21:16:23 +0000
293@@ -62,7 +62,7 @@
294 return []
295
296 digits = dp.get_precision('Account')(cr)[1]
297- amount = round(abs(trans.transferred_amount), digits)
298+ amount = round(abs(trans.statement_line_id.amount), digits)
299 # Make sure to be able to pinpoint our costs invoice for later
300 # matching
301 reference = '%s.%s: %s' % (trans.statement, trans.transaction, trans.reference)
302@@ -233,10 +233,6 @@
303 digits = dp.get_precision('Account')(cr)[1]
304 partial = False
305
306- # Disabled splitting transactions for now
307- # TODO allow splitting in the interactive wizard
308- allow_splitting = False
309-
310 # Search invoice on partner
311 if partner_ids:
312 candidates = [
313@@ -276,7 +272,7 @@
314 candidates = [
315 x for x in move_lines
316 if (is_zero(x.move_id, ((x.debit or 0.0) - (x.credit or 0.0)) -
317- trans.transferred_amount)
318+ trans.statement_line_id.amount)
319 and convert.str2date(x.date, '%Y-%m-%d') <=
320 (convert.str2date(trans.execution_date, '%Y-%m-%d') +
321 self.payment_window)
322@@ -292,7 +288,7 @@
323 # TODO: currency coercing
324 best = [x for x in candidates
325 if (is_zero(x.move_id, ((x.debit or 0.0) - (x.credit or 0.0)) -
326- trans.transferred_amount)
327+ trans.statement_line_id.amount)
328 and convert.str2date(x.date, '%Y-%m-%d') <=
329 (convert.str2date(trans.execution_date, '%Y-%m-%d') +
330 self.payment_window))
331@@ -343,7 +339,7 @@
332
333 trans2 = None
334 if move_line and partial:
335- found = round(trans.transferred_amount, digits)
336+ found = round(trans.statement_line_id.amount, digits)
337 if abs(expected) == abs(found):
338 partial = False
339 # Last partial payment will not flag invoice paid without
340@@ -358,24 +354,6 @@
341 elif abs(expected) > abs(found):
342 # Partial payment, reuse invoice
343 _cache(move_line, expected - found)
344- elif abs(expected) < abs(found) and allow_splitting:
345- # Possible combined payments, need to split transaction to
346- # verify
347- _cache(move_line)
348- trans2 = self.copy(
349- cr, uid, trans.id,
350- dict(
351- transferred_amount = trans.transferred_amount - expected,
352- transaction = trans.transaction + 'b',
353- parent_id = trans.id,
354- ), context=context)
355- # update the current record
356- self.write(cr, uid, trans.id, dict(
357- transferred_amount = expected,
358- transaction = trans.transaction + 'a',
359- ), context)
360- # rebrowse the current record after writing
361- trans = self.browse(cr, uid, trans.id, context=context)
362 if move_line:
363 account_ids = [
364 x.id for x in bank_account_ids
365@@ -712,7 +690,7 @@
366 # due to float representation and rounding difficulties
367 for trans in self.browse(cr, uid, ids, context=context):
368 if self.pool.get('res.currency').is_zero(
369- cr, uid,
370+ cr, uid,
371 trans.statement_id.currency,
372 me['transferred_amount'] - trans.transferred_amount):
373 dupes.append(trans.id)
374@@ -810,6 +788,12 @@
375 move_info['invoice_ids'][0]
376 )
377 return vals
378+
379+ def hook_match_payment(self, cr, uid, transaction, log, context=None):
380+ """
381+ To override in module 'account_banking_payment'
382+ """
383+ return False
384
385 def match(self, cr, uid, ids, results=None, context=None):
386 if not ids:
387@@ -872,12 +856,6 @@
388 else:
389 transaction = transactions[i]
390
391- if (transaction.statement_line_id and
392- transaction.statement_line_id.state == 'confirmed'):
393- raise orm.except_orm(
394- _("Cannot perform match"),
395- _("Cannot perform match on a confirmed transction"))
396-
397 if transaction.local_account in error_accounts:
398 results['trans_skipped_cnt'] += 1
399 if not injected:
400@@ -962,6 +940,44 @@
401 else:
402 info[transaction.local_account][currency_code] = account_info
403
404+ # Link accounting period
405+ period_id = banktools.get_period(
406+ self.pool, cr, uid, transaction.effective_date,
407+ company, results['log'])
408+ if not period_id:
409+ results['trans_skipped_cnt'] += 1
410+ if not injected:
411+ i += 1
412+ continue
413+
414+ if transaction.statement_line_id:
415+ if transaction.statement_line_id.state == 'confirmed':
416+ raise orm.except_orm(
417+ _("Cannot perform match"),
418+ _("Cannot perform match on a confirmed transction"))
419+ else:
420+ values = {
421+ 'name': '%s.%s' % (transaction.statement, transaction.transaction),
422+ 'date': transaction.effective_date,
423+ 'amount': transaction.transferred_amount,
424+ 'statement_id': transaction.statement_id.id,
425+ 'note': transaction.message,
426+ 'ref': transaction.reference,
427+ 'period_id': period_id,
428+ 'currency': account_info.currency_id.id,
429+ 'import_transaction_id': transaction.id,
430+ 'account_id': (
431+ transaction.transferred_amount < 0 and
432+ account_info.default_credit_account_id.id or
433+ account_info.default_debit_account_id.id),
434+ }
435+ statement_line_id = statement_line_obj.create(cr, uid, values, context)
436+ results['trans_loaded_cnt'] += 1
437+ transaction.write({'statement_line_id': statement_line_id})
438+ transaction.refresh()
439+ if transaction.statement_id.id not in imported_statement_ids:
440+ imported_statement_ids.append(transaction.statement_id.id)
441+
442 # Final check: no coercion of currencies!
443 if transaction.local_currency \
444 and account_info.currency_id.name != transaction.local_currency:
445@@ -971,8 +987,8 @@
446 ' uses different currency than the defined bank journal.'
447 ) % {
448 'bank_account': transactions.local_account,
449- 'transaction_id': transaction.statement,
450- 'statement_id': transaction.transaction,
451+ 'statement_id': transaction.statement,
452+ 'transaction_id': transaction.transaction,
453 }
454 )
455 error_accounts[transaction.local_account] = True
456@@ -981,16 +997,6 @@
457 i += 1
458 continue
459
460- # Link accounting period
461- period_id = banktools.get_period(
462- self.pool, cr, uid, transaction.effective_date,
463- company, results['log'])
464- if not period_id:
465- results['trans_skipped_cnt'] += 1
466- if not injected:
467- i += 1
468- continue
469-
470 # When bank costs are part of transaction itself, split it.
471 if transaction.type != bt.BANK_COSTS and transaction.provision_costs:
472 # Create new transaction for bank costs
473@@ -1022,13 +1028,12 @@
474 ), context=context)
475 # rebrowse the current record after writing
476 transaction = self.browse(cr, uid, transaction.id, context=context)
477- # Match full direct debit orders
478- if transaction.type == bt.DIRECT_DEBIT and has_payment:
479- move_info = self._match_debit_order(
480- cr, uid, transaction, results['log'], context)
481- if transaction.type == bt.STORNO and has_payment:
482- move_info = self._match_storno(
483- cr, uid, transaction, results['log'], context)
484+
485+ # Match payment and direct debit orders
486+ move_info_payment = self.hook_match_payment(
487+ cr, uid, transaction, results['log'], context=context)
488+ if move_info_payment:
489+ move_info = move_info_payment
490
491 # Allow inclusion of generated bank invoices
492 if transaction.type == bt.BANK_COSTS:
493@@ -1075,7 +1080,7 @@
494
495 # Credit means payment... isn't it?
496 if (not move_info
497- and transaction.transferred_amount < 0 and payment_lines):
498+ and transaction.statement_line_id.amount < 0 and payment_lines):
499 # Link open payment - if any
500 # Note that _match_payment is defined in the
501 # account_banking_payment module which should be installed
502@@ -1106,7 +1111,7 @@
503 # settings to overrule this. Note that you need to change
504 # the internal type of these accounts to either 'payable'
505 # or 'receivable' to enable usage like this.
506- if transaction.transferred_amount < 0:
507+ if transaction.statement_line_id.amount < 0:
508 if len(partner_banks) == 1:
509 account_id = (
510 partner_banks[0].partner_id.property_account_payable and
511@@ -1122,7 +1127,7 @@
512 if len(partner_banks) != 1 or not account_id or account_id == def_rec_account_id:
513 account_id = (account_info.default_debit_account_id and
514 account_info.default_debit_account_id.id)
515- values = {}
516+ values = {'account_id': account_id}
517 self_values = {}
518 if move_info:
519 results['trans_matched_cnt'] += 1
520@@ -1140,28 +1145,8 @@
521 len(partner_banks) == 1):
522 values['partner_bank_id'] = partner_banks[0].id
523
524- if not transaction.statement_line_id:
525- values.update(dict(
526- name = '%s.%s' % (transaction.statement, transaction.transaction),
527- date = transaction.effective_date,
528- amount = transaction.transferred_amount,
529- statement_id = transaction.statement_id.id,
530- note = transaction.message,
531- ref = transaction.reference,
532- period_id = period_id,
533- currency = account_info.currency_id.id,
534- account_id = account_id,
535- import_transaction_id = transaction.id,
536- ))
537-
538- statement_line_id = statement_line_obj.create(cr, uid, values, context)
539- results['trans_loaded_cnt'] += 1
540- self_values['statement_line_id'] = statement_line_id
541- if transaction.statement_id.id not in imported_statement_ids:
542- imported_statement_ids.append(transaction.statement_id.id)
543- else:
544- statement_line_obj.write(
545- cr, uid, transaction.statement_line_id.id, values, context)
546+ statement_line_obj.write(
547+ cr, uid, transaction.statement_line_id.id, values, context)
548 self.write(cr, uid, transaction.id, self_values, context)
549 if not injected:
550 i += 1
551@@ -1350,9 +1335,16 @@
552 'parent_id': fields.many2one(
553 'banking.import.transaction', 'Split off from this transaction'),
554 # match fields
555- 'match_type': fields.selection(
556- [('manual', 'Manual'), ('move','Move'), ('invoice', 'Invoice'),
557- ], 'Match type'),
558+ 'match_type': fields.selection([
559+ ('move','Move'),
560+ ('invoice', 'Invoice'),
561+ ('payment', 'Payment line'),
562+ ('payment_order', 'Payment order'),
563+ ('storno', 'Storno'),
564+ ('manual', 'Manual'),
565+ ('payment_manual', 'Payment line (manual)'),
566+ ('payment_order_manual', 'Payment order (manual)'),
567+ ], 'Match type'),
568 'match_multi': fields.function(
569 _get_match_multi, method=True, string='Multi match',
570 type='boolean'),
571@@ -1439,9 +1431,16 @@
572 string='Possible duplicate import', readonly=True),
573 'match_type': fields.related(
574 'import_transaction_id', 'match_type', type='selection',
575- selection=[('manual', 'Manual'), ('move','Move'),
576- ('invoice', 'Invoice'),
577- ],
578+ selection=[
579+ ('move','Move'),
580+ ('invoice', 'Invoice'),
581+ ('payment', 'Payment line'),
582+ ('payment_order', 'Payment order'),
583+ ('storno', 'Storno'),
584+ ('manual', 'Manual'),
585+ ('payment_manual', 'Payment line (manual)'),
586+ ('payment_order_manual', 'Payment order (manual)'),
587+ ],
588 string='Match type', readonly=True,),
589 'state': fields.selection(
590 [('draft', 'Draft'), ('confirmed', 'Confirmed')], 'State',
591@@ -1755,14 +1754,14 @@
592 class account_bank_statement(orm.Model):
593 _inherit = 'account.bank.statement'
594
595- def _end_balance(self, cursor, user, ids, name, attr, context=None):
596+ def _end_balance(self, cr, uid, ids, name, attr, context=None):
597 """
598 This method taken from account/account_bank_statement.py and
599 altered to take the statement line subflow into account
600 """
601 res = {}
602
603- statements = self.browse(cursor, user, ids, context=context)
604+ statements = self.browse(cr, uid, ids, context=context)
605 for statement in statements:
606 res[statement.id] = statement.balance_start
607
608
609=== modified file 'account_banking/wizard/bank_import.py'
610--- account_banking/wizard/bank_import.py 2013-04-27 10:14:24 +0000
611+++ account_banking/wizard/bank_import.py 2013-06-26 21:16:23 +0000
612@@ -28,9 +28,9 @@
613 The parsing is done in the parser modules. Every parser module is required to
614 use parser.models as a mean of communication with the business logic.
615 '''
616-from osv import orm, fields
617 import base64
618 import datetime
619+from openerp.osv import orm, fields
620 from openerp.tools.translate import _
621 from openerp.addons.account_banking.parsers import models
622 from openerp.addons.account_banking.parsers import convert
623@@ -97,13 +97,13 @@
624 class banking_import(orm.TransientModel):
625 _name = 'account.banking.bank.import'
626
627- def import_statements_file(self, cursor, uid, ids, context):
628+ def import_statements_file(self, cr, uid, ids, context):
629 '''
630 Import bank statements / bank transactions file.
631 This method is a wrapper for the business logic on the transaction.
632 The parser modules represent the decoding logic.
633 '''
634- banking_import = self.browse(cursor, uid, ids, context)[0]
635+ banking_import = self.browse(cr, uid, ids, context)[0]
636 statements_file = banking_import.file
637 data = base64.decodestring(statements_file)
638
639@@ -125,10 +125,10 @@
640
641 # Get the company
642 company = (banking_import.company or
643- user_obj.browse(cursor, uid, uid, context).company_id)
644+ user_obj.browse(cr, uid, uid, context).company_id)
645
646 # Parse the file
647- statements = parser.parse(cursor, data)
648+ statements = parser.parse(cr, data)
649
650 if any([x for x in statements if not x.is_valid()]):
651 raise orm.except_orm(
652@@ -137,7 +137,7 @@
653 )
654
655 # Create the file now, as the statements need to be linked to it
656- import_id = statement_file_obj.create(cursor, uid, dict(
657+ import_id = statement_file_obj.create(cr, uid, dict(
658 company_id = company.id,
659 file = statements_file,
660 state = 'unfinished',
661@@ -184,7 +184,7 @@
662 else:
663 # Pull account info/currency
664 account_info = banktools.get_company_bank_account(
665- self.pool, cursor, uid, statement.local_account,
666+ self.pool, cr, uid, statement.local_account,
667 statement.local_currency, company, results.log
668 )
669 if not account_info:
670@@ -238,7 +238,7 @@
671 # (e.g. a datetime string of the moment of import)
672 # and have potential duplicates flagged by the
673 # matching procedure
674- statement_ids = statement_obj.search(cursor, uid, [
675+ statement_ids = statement_obj.search(cr, uid, [
676 ('name', '=', statement.id),
677 ('date', '=', convert.date2str(statement.date)),
678 ])
679@@ -252,7 +252,7 @@
680
681 # Get the period for the statement (as bank statement object checks this)
682 period_ids = period_obj.search(
683- cursor, uid, [
684+ cr, uid, [
685 ('company_id', '=', company.id),
686 ('date_start', '<=', statement.date),
687 ('date_stop', '>=', statement.date),
688@@ -270,7 +270,7 @@
689 continue
690
691 # Create the bank statement record
692- statement_id = statement_obj.create(cursor, uid, dict(
693+ statement_id = statement_obj.create(cr, uid, dict(
694 name = statement.id,
695 journal_id = account_info.journal_id.id,
696 date = convert.date2str(statement.date),
697@@ -302,21 +302,21 @@
698 values['local_currency'] = statement.local_currency
699
700 transaction_id = import_transaction_obj.create(
701- cursor, uid, values, context=context)
702+ cr, uid, values, context=context)
703 transaction_ids.append(transaction_id)
704
705 results.stat_loaded_cnt += 1
706
707- import_transaction_obj.match(cursor, uid, transaction_ids, results=results, context=context)
708+ import_transaction_obj.match(cr, uid, transaction_ids, results=results, context=context)
709
710 #recompute statement end_balance for validation
711 statement_obj.button_dummy(
712- cursor, uid, imported_statement_ids, context=context)
713+ cr, uid, imported_statement_ids, context=context)
714
715
716 # Original code. Didn't take workflow logistics into account...
717 #
718- #cursor.execute(
719+ #cr.execute(
720 # "UPDATE payment_order o "
721 # "SET state = 'done', "
722 # "date_done = '%s' "
723@@ -358,13 +358,13 @@
724 ]
725 text_log = '\n'.join(report + results.log)
726 state = results.error_cnt and 'error' or 'ready'
727- statement_file_obj.write(cursor, uid, import_id, dict(
728+ statement_file_obj.write(cr, uid, import_id, dict(
729 state = state, log = text_log,
730 ), context)
731 if not imported_statement_ids or not results.trans_loaded_cnt:
732 # file state can be 'ready' while import state is 'error'
733 state = 'error'
734- self.write(cursor, uid, [ids[0]], dict(
735+ self.write(cr, uid, [ids[0]], dict(
736 import_id = import_id, log = text_log, state = state,
737 statement_ids = [(6, 0, imported_statement_ids)],
738 ), context)
739
740=== modified file 'account_banking/wizard/bank_import_view.xml'
741--- account_banking/wizard/bank_import_view.xml 2012-05-02 15:09:49 +0000
742+++ account_banking/wizard/bank_import_view.xml 2013-06-26 21:16:23 +0000
743@@ -4,7 +4,6 @@
744 <record id="view_banking_import" model="ir.ui.view">
745 <field name="name">account.banking.bank.import</field>
746 <field name="model">account.banking.bank.import</field>
747- <field name="type">form</field>
748 <field name="arch" type="xml">
749 <form string="Import Bank Transactions File">
750 <group colspan="4" states="init,ready,error">
751
752=== modified file 'account_banking/wizard/banking_transaction_wizard.py'
753--- account_banking/wizard/banking_transaction_wizard.py 2013-06-10 10:14:31 +0000
754+++ account_banking/wizard/banking_transaction_wizard.py 2013-06-26 21:16:23 +0000
755@@ -90,56 +90,33 @@
756 statement_line_obj = self.pool.get('account.bank.statement.line')
757 transaction_obj = self.pool.get('banking.import.transaction')
758
759- if not vals:
760+ if not vals or not ids:
761 return True
762
763+ wiz = self.browse(cr, uid, ids[0], context=context)
764+
765 # The following fields get never written
766 # they are just triggers for manual matching
767 # which populates regular fields on the transaction
768 manual_invoice_ids = vals.pop('manual_invoice_ids', [])
769 manual_move_line_ids = vals.pop('manual_move_line_ids', [])
770
771- # Support for writing fields.related is still flakey:
772- # https://bugs.launchpad.net/openobject-server/+bug/915975
773- # Will do so myself.
774-
775- # Separate the related fields
776- transaction_vals = {}
777- wizard_vals = vals.copy()
778- for key in vals.keys():
779- field = self._columns[key]
780- if (isinstance(field, fields.related) and
781- field._arg[0] == 'import_transaction_id'):
782- transaction_vals[field._arg[1]] = vals[key]
783- del wizard_vals[key]
784-
785- # write the related fields on the transaction model
786- if isinstance(ids, int):
787- ids = [ids]
788- for wizard in self.browse(cr, uid, ids, context=context):
789- if wizard.import_transaction_id:
790- transaction_obj.write(
791- cr, uid, wizard.import_transaction_id.id,
792- transaction_vals, context=context)
793-
794- # write other fields to the wizard model
795 res = super(banking_transaction_wizard, self).write(
796- cr, uid, ids, wizard_vals, context=context)
797+ cr, uid, ids, vals, context=context)
798+ wiz.refresh()
799
800- # End of workaround for lp:915975
801-
802- """ Process the logic of the written values """
803+ # Process the logic of the written values
804
805 # An invoice is selected from multiple candidates
806 if vals and 'invoice_id' in vals:
807- for wiz in self.browse(cr, uid, ids, context=context):
808- if (wiz.import_transaction_id.match_type == 'invoice' and
809+ if (wiz.import_transaction_id.match_type == 'invoice' and
810 wiz.import_transaction_id.invoice_id):
811- # the current value might apply
812- if (wiz.move_line_id and wiz.move_line_id.invoice and
813- wiz.move_line_id.invoice.id == wiz.invoice_id.id):
814- found = True
815- continue
816+ found = False
817+ # the current value might apply
818+ if (wiz.move_line_id and wiz.move_line_id.invoice and
819+ wiz.move_line_id.invoice == wiz.invoice_id):
820+ found = True
821+ else:
822 # Otherwise, retrieve the move line for this invoice
823 # Given the arity of the relation, there is are always
824 # multiple possibilities but the move lines here are
825@@ -147,8 +124,8 @@
826 # and the regular invoice workflow should only come up with
827 # one of those only.
828 for move_line in wiz.import_transaction_id.move_line_ids:
829- if (move_line.invoice.id ==
830- wiz.import_transaction_id.invoice_id.id):
831+ if (move_line.invoice ==
832+ wiz.import_transaction_id.invoice_id):
833 transaction_obj.write(
834 cr, uid, wiz.import_transaction_id.id,
835 { 'move_line_id': move_line.id, }, context=context)
836@@ -159,15 +136,12 @@
837 }, context=context)
838 found = True
839 break
840- # Cannot match the invoice
841- if not found:
842- # transaction_obj.write(
843- # cr, uid, wiz.import_transaction_id.id,
844- # { 'invoice_id': False, }, context=context)
845- orm.except_orm(
846- _("No entry found for the selected invoice"),
847- _("No entry found for the selected invoice. " +
848- "Try manual reconciliation."))
849+ # Cannot match the invoice
850+ if not found:
851+ orm.except_orm(
852+ _("No entry found for the selected invoice"),
853+ _("No entry found for the selected invoice. " +
854+ "Try manual reconciliation."))
855
856 if manual_move_line_ids or manual_invoice_ids:
857 move_line_obj = self.pool.get('account.move.line')
858@@ -323,24 +297,9 @@
859 {'duplicate': not wiz['duplicate']}, context=context)
860 return self.create_act_window(cr, uid, ids, context=None)
861
862- def _get_default_match_type(self, cr, uid, context=None):
863- """
864- Take initial value for the match type from the statement line
865- """
866- res = False
867- if context and 'statement_line_id' in context:
868- res = self.pool.get('account.bank.statement.line').read(
869- cr, uid, context['statement_line_id'],
870- ['match_type'], context=context)['match_type']
871- return res
872-
873 def button_done(self, cr, uid, ids, context=None):
874 return {'type': 'ir.actions.act_window_close'}
875
876- _defaults = {
877-# 'match_type': _get_default_match_type,
878- }
879-
880 _columns = {
881 'name': fields.char('Name', size=64),
882 'statement_line_id': fields.many2one(
883@@ -392,8 +351,25 @@
884 'import_transaction_id', 'match_multi',
885 type="boolean", string='Multiple matches'),
886 'match_type': fields.related(
887- 'import_transaction_id', 'match_type',
888- type="char", size=16, string='Match type', readonly=True),
889+ 'import_transaction_id', 'match_type', type='selection',
890+ selection=[
891+ ('move','Move'),
892+ ('invoice', 'Invoice'),
893+ ('payment', 'Payment line'),
894+ ('payment_order', 'Payment order'),
895+ ('storno', 'Storno'),
896+ ('manual', 'Manual'),
897+ ('payment_manual', 'Payment line (manual)'),
898+ ('payment_order_manual', 'Payment order (manual)'),
899+ ],
900+ string='Match type', readonly=True),
901+ 'manual_invoice_id': fields.many2one(
902+ 'account.invoice', 'Match this invoice',
903+ domain=[('reconciled', '=', False)]),
904+ 'manual_move_line_id': fields.many2one(
905+ 'account.move.line', 'Or match this entry',
906+ domain=[('account_id.reconcile', '=', True),
907+ ('reconcile_id', '=', False)]),
908 'manual_invoice_ids': fields.many2many(
909 'account.invoice',
910 'banking_transaction_wizard_account_invoice_rel',
911@@ -417,8 +393,6 @@
912 string="Analytic Account"),
913 'move_currency_amount': fields.related('import_transaction_id','move_currency_amount',
914 type='float', string='Match Currency Amount', readonly=True),
915- #'manual_payment_order_id': fields.many2one(
916- # 'payment.order', "Payment order to reconcile"),
917 }
918
919 banking_transaction_wizard()
920
921=== modified file 'account_banking/wizard/banking_transaction_wizard.xml'
922--- account_banking/wizard/banking_transaction_wizard.xml 2013-05-01 15:12:53 +0000
923+++ account_banking/wizard/banking_transaction_wizard.xml 2013-06-26 21:16:23 +0000
924@@ -3,7 +3,6 @@
925 <data>
926 <record model="ir.ui.view" id="transaction_wizard_first">
927 <field name="name">transaction.wizard.first</field>
928- <field name="type">form</field>
929 <field name="model">banking.transaction.wizard</field>
930 <field name="arch" type="xml">
931 <form string="Match transaction">
932
933=== modified file 'account_banking/wizard/banktools.py'
934--- account_banking/wizard/banktools.py 2013-05-01 14:25:04 +0000
935+++ account_banking/wizard/banktools.py 2013-06-26 21:16:23 +0000
936@@ -51,7 +51,7 @@
937 return False
938 return period_ids[0]
939
940-def get_bank_accounts(pool, cursor, uid, account_number, log, fail=False):
941+def get_bank_accounts(pool, cr, uid, account_number, log, fail=False):
942 '''
943 Get the bank account with account number account_number
944 '''
945@@ -60,13 +60,13 @@
946 return []
947
948 partner_bank_obj = pool.get('res.partner.bank')
949- bank_account_ids = partner_bank_obj.search(cursor, uid, [
950+ bank_account_ids = partner_bank_obj.search(cr, uid, [
951 ('acc_number', '=', account_number)
952 ])
953 if not bank_account_ids:
954 # SR 2012-02-19 does the search() override in res_partner_bank
955 # provides this result on the previous query?
956- bank_account_ids = partner_bank_obj.search(cursor, uid, [
957+ bank_account_ids = partner_bank_obj.search(cr, uid, [
958 ('acc_number_domestic', '=', account_number)
959 ])
960 if not bank_account_ids:
961@@ -76,7 +76,7 @@
962 % dict(account_no=account_number)
963 )
964 return []
965- return partner_bank_obj.browse(cursor, uid, bank_account_ids)
966+ return partner_bank_obj.browse(cr, uid, bank_account_ids)
967
968 def _has_attr(obj, attr):
969 # Needed for dangling addresses and a weird exception scheme in
970@@ -129,14 +129,14 @@
971 'name %(name)s') % {'name': name})
972 return partner_ids and partner_ids[0] or False
973
974-def get_company_bank_account(pool, cursor, uid, account_number, currency,
975+def get_company_bank_account(pool, cr, uid, account_number, currency,
976 company, log):
977 '''
978 Get the matching bank account for this company. Currency is the ISO code
979 for the requested currency.
980 '''
981 results = struct()
982- bank_accounts = get_bank_accounts(pool, cursor, uid, account_number, log,
983+ bank_accounts = get_bank_accounts(pool, cr, uid, account_number, log,
984 fail=True)
985 if not bank_accounts:
986 return False
987@@ -159,12 +159,12 @@
988
989 # Find matching journal for currency
990 journal_obj = pool.get('account.journal')
991- journal_ids = journal_obj.search(cursor, uid, [
992+ journal_ids = journal_obj.search(cr, uid, [
993 ('type', '=', 'bank'),
994 ('currency.name', '=', currency or company.currency_id.name)
995 ])
996 if currency == company.currency_id.name:
997- journal_ids_no_curr = journal_obj.search(cursor, uid, [
998+ journal_ids_no_curr = journal_obj.search(cr, uid, [
999 ('type', '=', 'bank'), ('currency', '=', False)
1000 ])
1001 journal_ids.extend(journal_ids_no_curr)
1002@@ -172,9 +172,9 @@
1003 criteria.append(('journal_id', 'in', journal_ids))
1004
1005 # Find bank account settings
1006- bank_settings_ids = bank_settings_obj.search(cursor, uid, criteria)
1007+ bank_settings_ids = bank_settings_obj.search(cr, uid, criteria)
1008 if bank_settings_ids:
1009- settings = bank_settings_obj.browse(cursor, uid, bank_settings_ids)[0]
1010+ settings = bank_settings_obj.browse(cr, uid, bank_settings_ids)[0]
1011 results.company_id = company
1012 results.journal_id = settings.journal_id
1013
1014@@ -192,7 +192,7 @@
1015
1016 return results
1017
1018-def get_or_create_bank(pool, cursor, uid, bic, online=False, code=None,
1019+def get_or_create_bank(pool, cr, uid, bic, online=False, code=None,
1020 name=None):
1021 '''
1022 Find or create the bank with the provided BIC code.
1023@@ -208,27 +208,27 @@
1024 if len(bic) < 8:
1025 # search key
1026 bank_ids = bank_obj.search(
1027- cursor, uid, [
1028+ cr, uid, [
1029 ('bic', '=', bic[:6])
1030 ])
1031 if not bank_ids:
1032 bank_ids = bank_obj.search(
1033- cursor, uid, [
1034+ cr, uid, [
1035 ('bic', 'ilike', bic + '%')
1036 ])
1037 else:
1038 bank_ids = bank_obj.search(
1039- cursor, uid, [
1040+ cr, uid, [
1041 ('bic', '=', bic)
1042 ])
1043
1044 if bank_ids and len(bank_ids) == 1:
1045- banks = bank_obj.browse(cursor, uid, bank_ids)
1046+ banks = bank_obj.browse(cr, uid, bank_ids)
1047 return banks[0].id, banks[0].country.id
1048
1049 country_obj = pool.get('res.country')
1050 country_ids = country_obj.search(
1051- cursor, uid, [('code', '=', bic[4:6])]
1052+ cr, uid, [('code', '=', bic[4:6])]
1053 )
1054 country_id = country_ids and country_ids[0] or False
1055 bank_id = False
1056@@ -236,7 +236,7 @@
1057 if online:
1058 info, address = sepa.online.bank_info(bic)
1059 if info:
1060- bank_id = bank_obj.create(cursor, uid, dict(
1061+ bank_id = bank_obj.create(cr, uid, dict(
1062 code = info.code,
1063 name = info.name,
1064 street = address.street,
1065@@ -250,7 +250,7 @@
1066 info = struct(name=name, code=code)
1067
1068 if not online or not bank_id:
1069- bank_id = bank_obj.create(cursor, uid, dict(
1070+ bank_id = bank_obj.create(cr, uid, dict(
1071 code = info.code or 'UNKNOW',
1072 name = info.name or _('Unknown Bank'),
1073 country = country_id,
1074
1075=== modified file 'account_banking_nl_clieop/__openerp__.py'
1076--- account_banking_nl_clieop/__openerp__.py 2013-04-15 14:14:27 +0000
1077+++ account_banking_nl_clieop/__openerp__.py 2013-06-26 21:16:23 +0000
1078@@ -37,5 +37,5 @@
1079 ClieOp format is used by Dutch banks to batch national bank transfers.
1080 This module uses the account_banking logic.
1081 ''',
1082- 'installable': False,
1083+ 'installable': True,
1084 }
1085
1086=== modified file 'account_banking_nl_clieop/account_banking_nl_clieop.py'
1087--- account_banking_nl_clieop/account_banking_nl_clieop.py 2013-04-15 13:59:50 +0000
1088+++ account_banking_nl_clieop/account_banking_nl_clieop.py 2013-06-26 21:16:23 +0000
1089@@ -1,6 +1,7 @@
1090 ##############################################################################
1091 #
1092 # Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
1093+# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
1094 # All Rights Reserved
1095 #
1096 # This program is free software: you can redistribute it and/or modify
1097@@ -18,11 +19,12 @@
1098 #
1099 ##############################################################################
1100
1101-from osv import osv, fields
1102 from datetime import date
1103-from tools.translate import _
1104-
1105-class clieop_export(osv.osv):
1106+from openerp.osv import orm, fields
1107+from openerp.tools.translate import _
1108+
1109+
1110+class clieop_export(orm.Model):
1111 '''ClieOp3 Export'''
1112 _name = 'banking.export.clieop'
1113 _description = __doc__
1114@@ -80,7 +82,7 @@
1115 last = 1
1116 last_ids = self.search(cr, uid, [
1117 ('date_generated', '=',
1118- fields.date.context_today(cr,uid,context))
1119+ fields.date.context_today(self, cr,uid,context))
1120 ], context=context)
1121 if last_ids:
1122 last = 1 + max([x['daynumber'] for x in self.read(
1123@@ -94,6 +96,3 @@
1124 'state': 'draft',
1125 'daynumber': get_daynr,
1126 }
1127-clieop_export()
1128-
1129-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1130
1131=== modified file 'account_banking_nl_clieop/account_banking_nl_clieop.xml'
1132--- account_banking_nl_clieop/account_banking_nl_clieop.xml 2013-01-02 15:14:53 +0000
1133+++ account_banking_nl_clieop/account_banking_nl_clieop.xml 2013-06-26 21:16:23 +0000
1134@@ -11,7 +11,6 @@
1135 <record id="view_banking_export_clieop_form" model="ir.ui.view">
1136 <field name="name">account.banking.export.clieop.form</field>
1137 <field name="model">banking.export.clieop</field>
1138- <field name="type">form</field>
1139 <field name="arch" type="xml">
1140 <form string="Client Opdrachten Export">
1141 <notebook>
1142@@ -48,7 +47,6 @@
1143 <record id="view_banking_export_clieop_tree" model="ir.ui.view">
1144 <field name="name">account.banking.export.clieop.tree</field>
1145 <field name="model">banking.export.clieop</field>
1146- <field name="type">tree</field>
1147 <field name="arch" type="xml">
1148 <tree string="Client Opdrachten Export">
1149 <field name="filetype" />
1150
1151=== modified file 'account_banking_nl_clieop/wizard/export_clieop.py'
1152--- account_banking_nl_clieop/wizard/export_clieop.py 2013-04-15 13:59:50 +0000
1153+++ account_banking_nl_clieop/wizard/export_clieop.py 2013-06-26 21:16:23 +0000
1154@@ -2,6 +2,7 @@
1155 ##############################################################################
1156 #
1157 # Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
1158+# 2011 - 2013 Therp BV (<http://therp.nl>).
1159 # All Rights Reserved
1160 #
1161 # This program is free software: you can redistribute it and/or modify
1162@@ -21,21 +22,22 @@
1163
1164 import base64
1165 from datetime import datetime, date, timedelta
1166-from osv import osv, fields
1167-from tools.translate import _
1168-import netsvc
1169-from account_banking import sepa
1170-import clieop
1171-
1172-def strpdate(arg, format='%Y-%m-%d'):
1173- '''shortcut'''
1174- return datetime.strptime(arg, format).date()
1175-
1176-def strfdate(arg, format='%Y-%m-%d'):
1177- '''shortcut'''
1178- return arg.strftime(format)
1179-
1180-class banking_export_clieop_wizard(osv.osv_memory):
1181+from openerp.osv import orm, fields
1182+from openerp.tools.translate import _
1183+from openerp import netsvc
1184+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
1185+from openerp.addons.account_banking import sepa
1186+from openerp.addons.account_banking_nl_clieop.wizard import clieop
1187+
1188+def strpdate(arg):
1189+ '''shortcut'''
1190+ return datetime.strptime(arg, DEFAULT_SERVER_DATE_FORMAT).date()
1191+
1192+def strfdate(arg):
1193+ '''shortcut'''
1194+ return arg.strftime(DEFAULT_SERVER_DATE_FORMAT)
1195+
1196+class banking_export_clieop_wizard(orm.TransientModel):
1197 _name = 'banking.export.clieop.wizard'
1198 _description = 'Client Opdrachten Export'
1199 _columns = {
1200@@ -151,21 +153,17 @@
1201 ),
1202 }
1203
1204- _defaults = {
1205- 'test': True,
1206- }
1207-
1208- def create(self, cursor, uid, vals, context=None):
1209+ def create(self, cr, uid, vals, context=None):
1210 '''
1211 Retrieve a sane set of default values based on the payment orders
1212 from the context.
1213 '''
1214 if 'batchtype' not in vals:
1215- self.check_orders(cursor, uid, vals, context)
1216+ self.check_orders(cr, uid, vals, context)
1217 return super(banking_export_clieop_wizard, self).create(
1218- cursor, uid, vals, context)
1219+ cr, uid, vals, context)
1220
1221- def check_orders(self, cursor, uid, vals, context):
1222+ def check_orders(self, cr, uid, vals, context):
1223 '''
1224 Check payment type for all orders.
1225
1226@@ -177,14 +175,14 @@
1227 Also mind that rates for batches are way higher than those for
1228 transactions. It pays to limit the number of batches.
1229 '''
1230- today = date.today()
1231+ today = strpdate(fields.date.context_today(self, cr, uid, context=context))
1232 payment_order_obj = self.pool.get('payment.order')
1233
1234 # Payment order ids are provided in the context
1235 payment_order_ids = context.get('active_ids', [])
1236 runs = {}
1237 # Only orders of same type can be combined
1238- payment_orders = payment_order_obj.browse(cursor, uid, payment_order_ids)
1239+ payment_orders = payment_order_obj.browse(cr, uid, payment_order_ids)
1240 for payment_order in payment_orders:
1241
1242 payment_type = payment_order.mode.type.code
1243@@ -194,8 +192,8 @@
1244 runs[payment_type] = [payment_order]
1245
1246 if payment_order.date_prefered == 'fixed':
1247- if payment_order.date_planned:
1248- execution_date = strpdate(payment_order.date_planned)
1249+ if payment_order.date_scheduled:
1250+ execution_date = strpdate(payment_order.date_scheduled)
1251 else:
1252 execution_date = today
1253 elif payment_order.date_prefered == 'now':
1254@@ -212,12 +210,12 @@
1255 else:
1256 execution_date = today
1257 if execution_date and execution_date >= max_date:
1258- raise osv.except_osv(
1259+ raise orm.except_orm(
1260 _('Error'),
1261 _('You can\'t create ClieOp orders more than 30 days in advance.')
1262 )
1263 if len(runs) != 1:
1264- raise osv.except_osv(
1265+ raise orm.except_orm(
1266 _('Error'),
1267 _('You can only combine payment orders of the same type')
1268 )
1269@@ -231,12 +229,12 @@
1270 'state': 'create',
1271 })
1272
1273- def create_clieop(self, cursor, uid, ids, context):
1274+ def create_clieop(self, cr, uid, ids, context):
1275 '''
1276 Wizard to actually create the ClieOp3 file
1277 '''
1278 payment_order_obj = self.pool.get('payment.order')
1279- clieop_export = self.browse(cursor, uid, ids, context)[0]
1280+ clieop_export = self.browse(cr, uid, ids, context)[0]
1281 clieopfile = None
1282 for payment_order in clieop_export.payment_order_ids:
1283 if not clieopfile:
1284@@ -253,7 +251,7 @@
1285 else:
1286 our_account_nr = payment_order.mode.bank_id.acc_number
1287 if not our_account_nr:
1288- raise osv.except_osv(
1289+ raise orm.except_orm(
1290 _('Error'),
1291 _('Your bank account has to have a valid account number')
1292 )
1293@@ -267,7 +265,7 @@
1294 accountno_sender = our_account_nr,
1295 seqno = self.pool.get(
1296 'banking.export.clieop').get_daynr(
1297- cursor, uid, context=context),
1298+ cr, uid, context=context),
1299 test = clieop_export['test']
1300 )
1301
1302@@ -291,7 +289,7 @@
1303 for line in payment_order.line_ids:
1304 # Check on missing partner of bank account (this can happen!)
1305 if not line.bank_id or not line.bank_id.partner_id:
1306- raise osv.except_osv(
1307+ raise orm.except_orm(
1308 _('Error'),
1309 _('There is insufficient information.\r\n'
1310 'Both destination address and account '
1311@@ -314,7 +312,7 @@
1312 # Is this an IBAN account?
1313 if iban.valid:
1314 if iban.countrycode != 'NL':
1315- raise osv.except_osv(
1316+ raise orm.except_orm(
1317 _('Error'),
1318 _('You cannot send international bank transfers '
1319 'through ClieOp3!')
1320@@ -331,7 +329,7 @@
1321 # Generate the specifics of this clieopfile
1322 order = clieopfile.order
1323 file_id = self.pool.get('banking.export.clieop').create(
1324- cursor, uid, dict(
1325+ cr, uid, dict(
1326 filetype = order.name_transactioncode,
1327 identification = order.identification,
1328 prefered_date = strfdate(order.preferred_execution_date),
1329@@ -346,7 +344,7 @@
1330 [6, 0, [x.id for x in clieop_export['payment_order_ids']]]
1331 ],
1332 ), context)
1333- self.write(cursor, uid, [ids[0]], dict(
1334+ self.write(cr, uid, [ids[0]], dict(
1335 filetype = order.name_transactioncode,
1336 testcode = order.testcode,
1337 file_id = file_id,
1338@@ -364,31 +362,27 @@
1339 'res_id': ids[0] or False,
1340 }
1341
1342- def cancel_clieop(self, cursor, uid, ids, context):
1343+ def cancel_clieop(self, cr, uid, ids, context):
1344 '''
1345 Cancel the ClieOp: just drop the file
1346 '''
1347- clieop_export = self.read(cursor, uid, ids, ['file_id'], context)[0]
1348- self.pool.get('banking.export.clieop').unlink(cursor, uid, clieop_export['file_id'][0])
1349+ clieop_export = self.read(cr, uid, ids, ['file_id'], context)[0]
1350+ self.pool.get('banking.export.clieop').unlink(cr, uid, clieop_export['file_id'][0])
1351 return {'type': 'ir.actions.act_window_close'}
1352
1353- def save_clieop(self, cursor, uid, ids, context):
1354+ def save_clieop(self, cr, uid, ids, context):
1355 '''
1356 Save the ClieOp: mark all payments in the file as 'sent', if not a test
1357 '''
1358 clieop_export = self.browse(
1359- cursor, uid, ids, context)[0]
1360+ cr, uid, ids, context)[0]
1361 if not clieop_export['test']:
1362 clieop_obj = self.pool.get('banking.export.clieop')
1363 payment_order_obj = self.pool.get('payment.order')
1364 clieop_file = clieop_obj.write(
1365- cursor, uid, clieop_export['file_id'].id, {'state': 'sent'}
1366+ cr, uid, clieop_export['file_id'].id, {'state': 'sent'}
1367 )
1368 wf_service = netsvc.LocalService('workflow')
1369 for order in clieop_export['payment_order_ids']:
1370- wf_service.trg_validate(uid, 'payment.order', order.id, 'sent', cursor)
1371+ wf_service.trg_validate(uid, 'payment.order', order.id, 'sent', cr)
1372 return {'type': 'ir.actions.act_window_close'}
1373-
1374-banking_export_clieop_wizard()
1375-
1376-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1377
1378=== modified file 'account_banking_nl_clieop/wizard/export_clieop_view.xml'
1379--- account_banking_nl_clieop/wizard/export_clieop_view.xml 2013-01-02 15:14:53 +0000
1380+++ account_banking_nl_clieop/wizard/export_clieop_view.xml 2013-06-26 21:16:23 +0000
1381@@ -4,7 +4,6 @@
1382 <record id="banking_export_clieop_wizard_view" model="ir.ui.view">
1383 <field name="name">banking.export.clieop.wizard.view</field>
1384 <field name="model">banking.export.clieop.wizard</field>
1385- <field name="type">form</field>
1386 <field name="arch" type="xml">
1387 <form string="Client Opdrachten Export">
1388 <field name="state" invisible="True"/>
1389
1390=== modified file 'account_banking_nl_girotel/girotel.py'
1391--- account_banking_nl_girotel/girotel.py 2013-04-15 13:59:50 +0000
1392+++ account_banking_nl_girotel/girotel.py 2013-06-26 21:16:23 +0000
1393@@ -45,6 +45,7 @@
1394 from account_banking.parsers import models
1395 from account_banking.parsers.convert import str2date, to_swift
1396 from tools.translate import _
1397+import re
1398 import csv
1399
1400 bt = models.mem_bank_transaction
1401@@ -105,6 +106,13 @@
1402 self.date = str2date(self.date, '%Y%m%d')
1403 if self.direction == 'A':
1404 self.transferred_amount = -float(self.transferred_amount)
1405+ if (self.transfer_type == 'VZ'
1406+ and (not self.remote_account or self.remote_account == '0')
1407+ and (not self.message or re.match('^\s*$', self.message))
1408+ and self.remote_owner.startswith('TOTAAL ')):
1409+ self.transfer_type = 'PB'
1410+ self.message = self.remote_owner
1411+ self.remove_owner = False
1412 else:
1413 self.transferred_amount = float(self.transferred_amount)
1414 self.local_account = self.local_account.zfill(10)
1415@@ -140,7 +148,8 @@
1416 'GT': bt.ORDER,
1417 'IC': bt.DIRECT_DEBIT,
1418 'OV': bt.ORDER,
1419- 'VZ': bt.PAYMENT_BATCH,
1420+ 'VZ': bt.ORDER,
1421+ 'PB': bt.PAYMENT_BATCH,
1422 }
1423
1424 def __init__(self, line, *args, **kwargs):
1425@@ -171,11 +180,14 @@
1426 4. Cash withdrawals from banks are too not seen as a transfer between
1427 two accounts - the cash exits the banking system. These withdrawals
1428 have their transfer_type set to 'GM'.
1429+ 5. Aggregated payment batches. These transactions have transfer type
1430+ 'VZ' natively but are changed to 'PB' while parsing. These transactions
1431+ have no remote account.
1432 '''
1433 return bool(self.transferred_amount and self.execution_date and (
1434 self.remote_account or
1435 self.transfer_type in [
1436- 'DV', 'BT', 'BA', 'GM',
1437+ 'DV', 'PB', 'BT', 'BA', 'GM',
1438 ]))
1439
1440 def refold_message(self, message):
1441
1442=== modified file 'account_banking_payment/__openerp__.py'
1443--- account_banking_payment/__openerp__.py 2013-03-17 12:53:37 +0000
1444+++ account_banking_payment/__openerp__.py 2013-06-26 21:16:23 +0000
1445@@ -37,6 +37,7 @@
1446 'data': [
1447 'view/account_payment.xml',
1448 'view/banking_transaction_wizard.xml',
1449+ 'view/payment_mode.xml',
1450 'view/payment_mode_type.xml',
1451 'view/bank_payment_manual.xml',
1452 'data/payment_mode_type.xml',
1453@@ -53,5 +54,5 @@
1454 account_banking_nl_clieop
1455 ''',
1456 'auto_install': True,
1457- 'installable': False,
1458+ 'installable': True,
1459 }
1460
1461=== modified file 'account_banking_payment/model/__init__.py'
1462--- account_banking_payment/model/__init__.py 2013-03-16 19:00:59 +0000
1463+++ account_banking_payment/model/__init__.py 2013-06-26 21:16:23 +0000
1464@@ -4,7 +4,6 @@
1465 import payment_mode_type
1466 import payment_order_create
1467 import banking_import_transaction
1468-import account_bank_statement_line
1469 import banking_transaction_wizard
1470 import bank_payment_manual
1471 import banking_import_line
1472
1473=== removed file 'account_banking_payment/model/account_bank_statement_line.py'
1474--- account_banking_payment/model/account_bank_statement_line.py 2013-03-17 09:10:15 +0000
1475+++ account_banking_payment/model/account_bank_statement_line.py 1970-01-01 00:00:00 +0000
1476@@ -1,40 +0,0 @@
1477-# -*- coding: utf-8 -*-
1478-##############################################################################
1479-#
1480-# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
1481-# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
1482-#
1483-# All other contributions are (C) by their respective contributors
1484-#
1485-# All Rights Reserved
1486-#
1487-# This program is free software: you can redistribute it and/or modify
1488-# it under the terms of the GNU Affero General Public License as
1489-# published by the Free Software Foundation, either version 3 of the
1490-# License, or (at your option) any later version.
1491-#
1492-# This program is distributed in the hope that it will be useful,
1493-# but WITHOUT ANY WARRANTY; without even the implied warranty of
1494-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1495-# GNU Affero General Public License for more details.
1496-#
1497-# You should have received a copy of the GNU Affero General Public License
1498-# along with this program. If not, see <http://www.gnu.org/licenses/>.
1499-#
1500-##############################################################################
1501-
1502-from openerp.osv import orm, fields
1503-
1504-
1505-class account_bank_statement_line(orm.Model):
1506- _inherit = 'account.bank.statement.line'
1507- _columns = {
1508- 'match_type': fields.related(
1509- # Add payment and storno types
1510- 'import_transaction_id', 'match_type', type='selection',
1511- selection=[('manual', 'Manual'), ('move','Move'),
1512- ('invoice', 'Invoice'), ('payment', 'Payment'),
1513- ('payment_order', 'Payment order'),
1514- ('storno', 'Storno')],
1515- string='Match type', readonly=True,),
1516- }
1517
1518=== modified file 'account_banking_payment/model/account_payment.py'
1519--- account_banking_payment/model/account_payment.py 2013-04-06 05:02:18 +0000
1520+++ account_banking_payment/model/account_payment.py 2013-06-26 21:16:23 +0000
1521@@ -73,6 +73,8 @@
1522 'line_ids': fields.one2many(
1523 'payment.line', 'order_id', 'Payment lines',
1524 states={
1525+ 'open': [('readonly', True)],
1526+ 'cancel': [('readonly', True)],
1527 'sent': [('readonly', True)],
1528 'rejected': [('readonly', True)],
1529 'done': [('readonly', True)]
1530@@ -172,32 +174,10 @@
1531 ])
1532 payment_line_obj.write(cr, uid, line_ids, kwargs)
1533
1534- def set_to_draft(self, cr, uid, ids, *args):
1535- '''
1536- Set both self and payment lines to state 'draft'.
1537- '''
1538- self._write_payment_lines(cr, uid, ids, export_state='draft')
1539- return super(payment_order, self).set_to_draft(
1540- cr, uid, ids, *args
1541- )
1542-
1543- def action_sent(self, cr, uid, ids, context=None):
1544- '''
1545- Set both self and payment lines to state 'sent'.
1546- '''
1547- self._write_payment_lines(cr, uid, ids, export_state='sent')
1548- self.write(cr, uid, ids, {
1549- 'state': 'sent',
1550- 'date_sent': fields.date.context_today(
1551- self, cr, uid, context=context),
1552- }, context=context)
1553- return True
1554-
1555 def action_rejected(self, cr, uid, ids, *args):
1556 '''
1557 Set both self and payment lines to state 'rejected'.
1558 '''
1559- self._write_payment_lines(cr, uid, ids, export_state='rejected')
1560 wf_service = netsvc.LocalService('workflow')
1561 for id in ids:
1562 wf_service.trg_validate(uid, 'payment.order', id, 'rejected', cr)
1563@@ -209,32 +189,172 @@
1564 '''
1565 self._write_payment_lines(
1566 cr, uid, ids,
1567- export_state='done',
1568 date_done=fields.date.context_today(self, cr, uid))
1569 return super(payment_order, self).set_done(
1570 cr, uid, ids, *args
1571 )
1572
1573- """
1574- Hooks for processing direct debit orders, such as implemented in
1575- account_direct_debit module.
1576- """
1577- def debit_reconcile_transfer(
1578- self, cr, uid, payment_order_id, amount, currency, context=None):
1579- """
1580- Reconcile the payment order if the amount is correct. Return the
1581- id of the reconciliation.
1582- """
1583- raise orm.except_orm(
1584- _("Cannot reconcile"),
1585- _("Cannot reconcile debit order: "+
1586- "Not implemented."))
1587-
1588- def debit_unreconcile_transfer(
1589- self, cr, uid, payment_order_id, reconcile_id, amount, currency,
1590- context=None):
1591- """ Unreconcile the payment_order if at all possible """
1592- raise orm.except_orm(
1593- _("Cannot unreconcile"),
1594- _("Cannot unreconcile debit order: "+
1595- "Not implemented."))
1596+ def debit_reconcile_transfer(self, cr, uid, payment_order_id,
1597+ amount, currency, context=None):
1598+ """
1599+ During import of bank statements, create the reconcile on the transfer
1600+ account containing all the open move lines on the transfer account.
1601+ """
1602+ move_line_obj = self.pool.get('account.move.line')
1603+ order = self.browse(cr, uid, payment_order_id, context)
1604+ line_ids = []
1605+ reconcile_id = False
1606+ if not order.line_ids[0].transit_move_line_id:
1607+ wf_service = netsvc.LocalService('workflow')
1608+ wf_service.trg_validate(
1609+ uid, 'payment.order', payment_order_id, 'done', cr)
1610+ return False
1611+ for order_line in order.line_ids:
1612+ for line in order_line.transit_move_line_id.move_id.line_id:
1613+ if line.account_id.type == 'other' and not line.reconcile_id:
1614+ line_ids.append(line.id)
1615+ if self.pool.get('res.currency').is_zero(
1616+ cr, uid, currency,
1617+ move_line_obj.get_balance(cr, uid, line_ids) - amount):
1618+ reconcile_id = self.pool.get('account.move.reconcile').create(
1619+ cr, uid,
1620+ {'type': 'auto', 'line_id': [(6, 0, line_ids)]},
1621+ context)
1622+ # set direct debit order to finished state
1623+ wf_service = netsvc.LocalService('workflow')
1624+ wf_service.trg_validate(
1625+ uid, 'payment.order', payment_order_id, 'done', cr)
1626+ return reconcile_id
1627+
1628+ def debit_unreconcile_transfer(self, cr, uid, payment_order_id, reconcile_id,
1629+ amount, currency, context=None):
1630+ """
1631+ Due to a cancelled bank statements import, unreconcile the move on
1632+ the transfer account. Delegate the conditions to the workflow.
1633+ Raise on failure for rollback.
1634+
1635+ Workflow appears to return False even on success so we just check
1636+ the order's state that we know to be set to 'sent' in that case.
1637+ """
1638+ self.pool.get('account.move.reconcile').unlink(
1639+ cr, uid, [reconcile_id], context=context)
1640+ netsvc.LocalService('workflow').trg_validate(
1641+ uid, 'payment.order', payment_order_id, 'undo_done', cr)
1642+ state = self.pool.get('payment.order').read(
1643+ cr, uid, payment_order_id, ['state'], context=context)['state']
1644+ if state != 'sent':
1645+ raise orm.except_orm(
1646+ _("Cannot unreconcile"),
1647+ _("Cannot unreconcile payment order: "+
1648+ "Workflow will not allow it."))
1649+ return True
1650+
1651+ def test_undo_done(self, cr, uid, ids, context=None):
1652+ """
1653+ Called from the workflow. Used to unset done state on
1654+ payment orders that were reconciled with bank transfers
1655+ which are being cancelled.
1656+
1657+ Test if the payment order has not been reconciled. Depends
1658+ on the restriction that transit move lines should use an
1659+ account of type 'other', and on the restriction of payment
1660+ and debit orders that they only take moves on accounts
1661+ payable/receivable.
1662+ """
1663+ for order in self.browse(cr, uid, ids, context=context):
1664+ for order_line in order.line_ids:
1665+ if order_line.transit_move_line_id.move_id:
1666+ for line in order_line.transit_move_line_id.move_id.line_id:
1667+ if (line.account_id.type == 'other' and
1668+ line.reconcile_id):
1669+ return False
1670+ return True
1671+
1672+ def action_sent(self, cr, uid, ids, context=None):
1673+ """
1674+ Create the moves that pay off the move lines from
1675+ the debit order. This happens when the debit order file is
1676+ generated.
1677+ """
1678+ account_move_obj = self.pool.get('account.move')
1679+ account_move_line_obj = self.pool.get('account.move.line')
1680+ payment_line_obj = self.pool.get('payment.line')
1681+ labels = {
1682+ 'payment': _('Payment order'),
1683+ 'debit': _('Direct debit order'),
1684+ }
1685+ for order in self.browse(cr, uid, ids, context=context):
1686+ for line in order.line_ids:
1687+ # basic checks
1688+ if not line.move_line_id:
1689+ raise orm.except_orm(
1690+ _('Error'),
1691+ _('No move line provided for line %s') % line.name)
1692+ if line.move_line_id.reconcile_id:
1693+ raise orm.except_orm(
1694+ _('Error'),
1695+ _('Move line %s has already been paid/reconciled') %
1696+ line.move_line_id.name
1697+ )
1698+
1699+ move_id = account_move_obj.create(cr, uid, {
1700+ 'journal_id': order.mode.transfer_journal_id.id,
1701+ 'name': '%s %s' % (labels[order.payment_order_type],
1702+ line.move_line_id.move_id.name),
1703+ 'reference': '%s%s' % (order.payment_order_type[:3].upper(),
1704+ line.move_line_id.move_id.name),
1705+ }, context=context)
1706+
1707+ # TODO: take multicurrency into account
1708+
1709+ # create the debit move line on the transfer account
1710+ vals = {
1711+ 'name': _('%s for %s') % (
1712+ labels[order.payment_order_type],
1713+ line.move_line_id.invoice and
1714+ line.move_line_id.invoice.number or
1715+ line.move_line_id.name),
1716+ 'move_id': move_id,
1717+ 'partner_id': line.partner_id.id,
1718+ 'account_id': order.mode.transfer_account_id.id,
1719+ 'credit': (order.payment_order_type == 'payment'
1720+ and line.amount or 0.0),
1721+ 'debit': (order.payment_order_type == 'debit'
1722+ and line.amount or 0.0),
1723+ 'date': fields.date.context_today(
1724+ self, cr, uid, context=context),
1725+ }
1726+ transfer_move_line_id = account_move_line_obj.create(
1727+ cr, uid, vals, context=context)
1728+
1729+ # create the debit move line on the receivable account
1730+ vals.update({
1731+ 'account_id': line.move_line_id.account_id.id,
1732+ 'credit': (order.payment_order_type == 'debit'
1733+ and line.amount or 0.0),
1734+ 'debit': (order.payment_order_type == 'payment'
1735+ and line.amount or 0.0),
1736+ })
1737+ reconcile_move_line_id = account_move_line_obj.create(
1738+ cr, uid, vals, context=context)
1739+
1740+ # register the debit move line on the payment line
1741+ # and call reconciliation on it
1742+ payment_line_obj.write(
1743+ cr, uid, line.id,
1744+ {'transit_move_line_id': reconcile_move_line_id},
1745+ context=context)
1746+
1747+ payment_line_obj.debit_reconcile(
1748+ cr, uid, line.id, context=context)
1749+ account_move_obj.post(cr, uid, [move_id], context=context)
1750+
1751+ # State field is written by act_sent_wait
1752+ self.write(cr, uid, ids, {
1753+ 'date_sent': fields.date.context_today(
1754+ self, cr, uid, context=context),
1755+ }, context=context)
1756+
1757+ return True
1758+
1759+
1760
1761=== modified file 'account_banking_payment/model/banking_import_transaction.py'
1762--- account_banking_payment/model/banking_import_transaction.py 2013-06-03 09:47:11 +0000
1763+++ account_banking_payment/model/banking_import_transaction.py 2013-06-26 21:16:23 +0000
1764@@ -27,37 +27,44 @@
1765 from openerp import netsvc
1766 from openerp.tools.translate import _
1767 from openerp.addons.decimal_precision import decimal_precision as dp
1768+from openerp.addons.account_banking.parsers.models import mem_bank_transaction as bt
1769
1770
1771 class banking_import_transaction(orm.Model):
1772 _inherit = 'banking.import.transaction'
1773
1774- def _match_debit_order(
1775- self, cr, uid, trans, log, context=None):
1776+ def _match_payment_order(
1777+ self, cr, uid, trans, log, order_type='payment', context=None):
1778
1779- def is_zero(total):
1780+ def equals_order_amount(payment_order, transferred_amount):
1781+ if (not hasattr(payment_order, 'payment_order_type')
1782+ or payment_order.payment_order_type == 'payment'):
1783+ sign = 1
1784+ else:
1785+ sign = -1
1786+ total = payment_order.total + sign * transferred_amount
1787 return self.pool.get('res.currency').is_zero(
1788- cr, uid, trans.statement_id.currency, total)
1789+ cr, uid, trans.statement_line_id.statement_id.currency, total)
1790
1791 payment_order_obj = self.pool.get('payment.order')
1792
1793 order_ids = payment_order_obj.search(
1794- cr, uid, [('payment_order_type', '=', 'debit'),
1795+ cr, uid, [('payment_order_type', '=', order_type),
1796 ('state', '=', 'sent'),
1797 ('date_sent', '<=', trans.execution_date),
1798 ],
1799 limit=0, context=context)
1800 orders = payment_order_obj.browse(cr, uid, order_ids, context)
1801 candidates = [x for x in orders if
1802- is_zero(x.total - trans.transferred_amount) and
1803- x.line_ids and x.line_ids[0].debit_move_line_id]
1804+ equals_order_amount(x, trans.statement_line_id.amount)]
1805 if len(candidates) > 0:
1806 # retrieve the common account_id, if any
1807 account_id = False
1808- for line in candidates[0].line_ids[0].debit_move_line_id.move_id.line_id:
1809- if line.account_id.type == 'other':
1810- account_id = line.account_id.id
1811- break
1812+ if (candidates[0].line_ids[0].transit_move_line_id):
1813+ for line in candidates[0].line_ids[0].transit_move_line_id.move_id.line_id:
1814+ if line.account_id.type == 'other':
1815+ account_id = line.account_id.id
1816+ break
1817 return dict(
1818 move_line_ids = False,
1819 match_type = 'payment_order',
1820@@ -82,7 +89,7 @@
1821 # stornos MUST have an exact match
1822 if len(line_ids) == 1:
1823 account_id = payment_line_obj.get_storno_account_id(
1824- cr, uid, line_ids[0], trans.transferred_amount,
1825+ cr, uid, line_ids[0], trans.statement_line_id.amount,
1826 trans.statement_id.currency, context=None)
1827 if account_id:
1828 return dict(
1829@@ -114,7 +121,7 @@
1830 x for x in payment_lines
1831 if x.communication == trans.reference
1832 and round(x.amount, digits) == -round(
1833- trans.transferred_amount, digits)
1834+ trans.statement_line_id.amount, digits)
1835 and trans.remote_account in (x.bank_id.acc_number,
1836 x.bank_id.acc_number_domestic)
1837 ]
1838@@ -171,10 +178,6 @@
1839 raise orm.except_orm(
1840 _("Cannot reconcile"),
1841 _("Cannot reconcile: no direct debit order"))
1842- if transaction.payment_order_id.payment_order_type != 'debit':
1843- raise orm.except_orm(
1844- _("Cannot reconcile"),
1845- _("Reconcile payment order not implemented"))
1846 reconcile_id = payment_order_obj.debit_reconcile_transfer(
1847 cr, uid,
1848 transaction.payment_order_id.id,
1849@@ -195,7 +198,6 @@
1850 payment_line_obj = self.pool.get('payment.line')
1851 payment_line_obj.write(
1852 cr, uid, transaction.payment_line_id.id, {
1853- 'export_state': 'done',
1854 'date_done': transaction.statement_line_id.date,
1855 }
1856 )
1857@@ -232,11 +234,12 @@
1858 if not transaction.payment_order_id:
1859 raise orm.except_orm(
1860 _("Cannot unreconcile"),
1861- _("Cannot unreconcile: no direct debit order"))
1862- if transaction.payment_order_id.payment_order_type != 'debit':
1863+ _("Cannot unreconcile: no payment or direct debit order"))
1864+ if not transaction.statement_line_id.reconcile_id:
1865 raise orm.except_orm(
1866 _("Cannot unreconcile"),
1867- _("Unreconcile payment order not implemented"))
1868+ _("Payment orders without transfer move lines cannot be "
1869+ "unreconciled this way"))
1870 return payment_order_obj.debit_unreconcile_transfer(
1871 cr, uid, transaction.payment_order_id.id,
1872 transaction.statement_line_id.reconcile_id.id,
1873@@ -302,17 +305,6 @@
1874 cr, uid, transaction.payment_line_id.id, context)
1875
1876 _columns = {
1877- 'match_type': fields.selection(
1878- # Add payment and storno types
1879- [
1880- ('manual', 'Manual'),
1881- ('move','Move'),
1882- ('invoice', 'Invoice'),
1883- ('payment', 'Payment'),
1884- ('payment_order', 'Payment order'),
1885- ('storno', 'Storno'),
1886- ],
1887- 'Match type'),
1888 'payment_order_ids': fields.many2many(
1889 'payment.order', 'banking_transaction_payment_order_rel',
1890 'order_id', 'transaction_id', 'Payment orders'),
1891@@ -334,14 +326,14 @@
1892 return res
1893
1894 def clear_and_write(self, cr, uid, ids, vals=None, context=None):
1895- super(banking_import_transaction, self).clear_and_write(
1896+ write_vals = {
1897+ 'payment_line_id': False,
1898+ 'payment_order_id': False,
1899+ 'payment_order_ids': [(6, 0, [])],
1900+ }
1901+ write_vals.update(vals or {})
1902+ return super(banking_import_transaction, self).clear_and_write(
1903 cr, uid, ids, vals=vals, context=context)
1904- return self.write(
1905- cr, uid, ids, {
1906- 'payment_line_id': False,
1907- 'payment_order_ids': [(6, 0, [])],
1908- },
1909- context=context)
1910
1911 def move_info2values(self, move_info):
1912 vals = super(banking_import_transaction, self).move_info2values(
1913@@ -356,11 +348,25 @@
1914 )
1915 return vals
1916
1917- def match(self, cr, uid, ids, results=None, context=None):
1918- res = super(banking_import_transaction, self).match(
1919- cr, uid, ids, results=results, context=context)
1920-
1921- return res
1922+ def hook_match_payment(self, cr, uid, transaction, log, context=None):
1923+ """
1924+ Called from match() in the core module.
1925+ Match payment batches, direct debit orders and stornos
1926+ """
1927+ move_info = False
1928+ if transaction.type == bt.PAYMENT_BATCH:
1929+ move_info = self._match_payment_order(
1930+ cr, uid, transaction, log,
1931+ order_type='payment', context=context)
1932+ elif transaction.type == bt.DIRECT_DEBIT:
1933+ move_info = self._match_payment_order(
1934+ cr, uid, transaction, log,
1935+ order_type='debit', context=context)
1936+ elif transaction.type == bt.STORNO:
1937+ move_info = self._match_storno(
1938+ cr, uid, transaction, log,
1939+ context=context)
1940+ return move_info
1941
1942 def __init__(self, pool, cr):
1943 """
1944@@ -369,14 +375,18 @@
1945 """
1946 super(banking_import_transaction, self).__init__(pool, cr)
1947
1948- banking_import_transaction.confirm_map.update({
1949+ self.confirm_map.update({
1950 'storno': banking_import_transaction._confirm_storno,
1951 'payment_order': banking_import_transaction._confirm_payment_order,
1952 'payment': banking_import_transaction._confirm_payment,
1953+ 'payment_order_manual': banking_import_transaction._confirm_payment_order,
1954+ 'payment_manual': banking_import_transaction._confirm_payment,
1955 })
1956
1957- banking_import_transaction.cancel_map.update({
1958+ self.cancel_map.update({
1959 'storno': banking_import_transaction._cancel_storno,
1960 'payment_order': banking_import_transaction._cancel_payment_order,
1961 'payment': banking_import_transaction._cancel_payment,
1962+ 'payment_order_manual': banking_import_transaction._cancel_payment_order,
1963+ 'payment_manual': banking_import_transaction._cancel_payment,
1964 })
1965
1966=== modified file 'account_banking_payment/model/banking_transaction_wizard.py'
1967--- account_banking_payment/model/banking_transaction_wizard.py 2013-03-17 09:10:15 +0000
1968+++ account_banking_payment/model/banking_transaction_wizard.py 2013-06-26 21:16:23 +0000
1969@@ -24,10 +24,59 @@
1970 ##############################################################################
1971
1972 from openerp.osv import orm, fields
1973+from openerp.tools.translate import _
1974
1975
1976 class banking_transaction_wizard(orm.TransientModel):
1977 _inherit = 'banking.transaction.wizard'
1978+
1979+ def write(self, cr, uid, ids, vals, context=None):
1980+ """
1981+ Check for manual payment orders or lines
1982+ """
1983+ if not vals or not ids:
1984+ return True
1985+ manual_payment_order_id = vals.pop('manual_payment_order_id', False)
1986+ manual_payment_line_id = vals.pop('manual_payment_line_id', False)
1987+ res = super(banking_transaction_wizard, self).write(
1988+ cr, uid, ids, vals, context=context)
1989+ if manual_payment_order_id or manual_payment_line_id:
1990+ transaction_id = self.browse(
1991+ cr, uid, ids[0],
1992+ context=context).import_transaction_id
1993+ write_vals = {}
1994+ if manual_payment_order_id:
1995+ payment_order = self.pool.get('payment.order').browse(
1996+ cr, uid, manual_payment_order_id,
1997+ context=context)
1998+ if payment_order.payment_order_type == 'payment':
1999+ sign = 1
2000+ else:
2001+ sign = -1
2002+ total = (payment_order.total + sign *
2003+ transaction_id.statement_line_id.amount)
2004+ if not self.pool.get('res.currency').is_zero(
2005+ cr, uid, transaction_id.statement_line_id.statement_id.currency, total):
2006+ raise orm.except_orm(
2007+ _('Error'),
2008+ _('When matching a payment order, the amounts have to '
2009+ 'match exactly'))
2010+
2011+ if payment_order.mode and payment_order.mode.transfer_account_id:
2012+ transaction_id.statement_line_id.write({
2013+ 'account_id': payment_order.mode.transfer_account_id.id,
2014+ })
2015+ write_vals.update(
2016+ {'payment_order_id': manual_payment_order_id,
2017+ 'match_type': 'payment_order_manual'})
2018+ else:
2019+ write_vals.update(
2020+ {'payment_line_id': manual_payment_line_id,
2021+ 'match_type': 'payment_manual'})
2022+ self.pool.get('banking.import.transaction').clear_and_write(
2023+ cr, uid, transaction_id.id, write_vals, context=context)
2024+ return res
2025+
2026 _columns = {
2027 'payment_line_id': fields.related(
2028 'import_transaction_id', 'payment_line_id',
2029@@ -42,4 +91,13 @@
2030 'import_transaction_id', 'payment_order_id',
2031 string="Payment order to reconcile",
2032 type='many2one', relation='payment.order'),
2033+ 'manual_payment_order_id': fields.many2one(
2034+ 'payment.order', 'Match this payment order',
2035+ domain=[('state', '=', 'sent')]),
2036+ 'manual_payment_line_id': fields.many2one(
2037+ 'payment.line', 'Match this payment line',
2038+ domain=[
2039+ ('order_id.state', '=', 'sent'),
2040+ ('date_done', '=', False),
2041+ ]),
2042 }
2043
2044=== modified file 'account_banking_payment/model/payment_line.py'
2045--- account_banking_payment/model/payment_line.py 2013-03-17 09:10:15 +0000
2046+++ account_banking_payment/model/payment_line.py 2013-06-26 21:16:23 +0000
2047@@ -24,11 +24,12 @@
2048 ##############################################################################
2049
2050 from openerp.osv import orm, fields
2051-
2052+from openerp import netsvc
2053+from openerp.tools.translate import _
2054
2055 class payment_line(orm.Model):
2056 '''
2057- Add extra export_state and date_done fields; make destination bank account
2058+ Add some fields; make destination bank account
2059 mandatory, as it makes no sense to send payments into thin air.
2060 Edit: Payments can be by cash too, which is prohibited by mandatory bank
2061 accounts.
2062@@ -36,146 +37,34 @@
2063 _inherit = 'payment.line'
2064 _columns = {
2065 # New fields
2066- 'export_state': fields.selection([
2067- ('draft', 'Draft'),
2068- ('open','Confirmed'),
2069- ('cancel','Cancelled'),
2070- ('sent', 'Sent'),
2071- ('rejected', 'Rejected'),
2072- ('done','Done'),
2073- ], 'State', select=True
2074- ),
2075 'msg': fields.char('Message', size=255, required=False, readonly=True),
2076-
2077- # Redefined fields: added states
2078- 'date_done': fields.datetime('Date Confirmed', select=True,
2079- readonly=True),
2080- 'name': fields.char(
2081- 'Your Reference', size=64, required=True,
2082- states={
2083- 'sent': [('readonly', True)],
2084- 'rejected': [('readonly', True)],
2085- 'done': [('readonly', True)]
2086- },
2087- ),
2088+ 'date_done': fields.date(
2089+ 'Date Confirmed', select=True, readonly=True),
2090+ # Communication: required is dependend on the mode
2091 'communication': fields.char(
2092 'Communication', size=64, required=False,
2093 help=("Used as the message between ordering customer and current "
2094 "company. Depicts 'What do you want to say to the recipient"
2095 " about this order ?'"
2096 ),
2097- states={
2098- 'sent': [('readonly', True)],
2099- 'rejected': [('readonly', True)],
2100- 'done': [('readonly', True)]
2101- },
2102 ),
2103+ # Communication2: enlarge to 128
2104 'communication2': fields.char(
2105 'Communication 2', size=128,
2106 help='The successor message of Communication.',
2107- states={
2108- 'sent': [('readonly', True)],
2109- 'rejected': [('readonly', True)],
2110- 'done': [('readonly', True)]
2111- },
2112- ),
2113- 'move_line_id': fields.many2one(
2114- 'account.move.line', 'Entry line',
2115- domain=[('reconcile_id','=', False),
2116- ('account_id.type', '=','payable')
2117- ],
2118- help=('This Entry Line will be referred for the information of '
2119- 'the ordering customer.'
2120- ),
2121- states={
2122- 'sent': [('readonly', True)],
2123- 'rejected': [('readonly', True)],
2124- 'done': [('readonly', True)]
2125- },
2126- ),
2127- 'amount_currency': fields.float(
2128- 'Amount in Partner Currency', digits=(16,2),
2129- required=True,
2130- help='Payment amount in the partner currency',
2131- states={
2132- 'sent': [('readonly', True)],
2133- 'rejected': [('readonly', True)],
2134- 'done': [('readonly', True)]
2135- },
2136- ),
2137- 'currency': fields.many2one(
2138- 'res.currency', 'Partner Currency', required=True,
2139- states={
2140- 'sent': [('readonly', True)],
2141- 'rejected': [('readonly', True)],
2142- 'done': [('readonly', True)]
2143- },
2144- ),
2145- 'bank_id': fields.many2one(
2146- 'res.partner.bank', 'Destination Bank account',
2147- states={
2148- 'sent': [('readonly', True)],
2149- 'rejected': [('readonly', True)],
2150- 'done': [('readonly', True)]
2151- },
2152- ),
2153- 'order_id': fields.many2one(
2154- 'payment.order', 'Order', required=True,
2155- ondelete='cascade', select=True,
2156- states={
2157- 'sent': [('readonly', True)],
2158- 'rejected': [('readonly', True)],
2159- 'done': [('readonly', True)]
2160- },
2161- ),
2162- 'partner_id': fields.many2one(
2163- 'res.partner', string="Partner", required=True,
2164- help='The Ordering Customer',
2165- states={
2166- 'sent': [('readonly', True)],
2167- 'rejected': [('readonly', True)],
2168- 'done': [('readonly', True)]
2169- },
2170- ),
2171- 'date': fields.date(
2172- 'Payment Date',
2173- help=("If no payment date is specified, the bank will treat this "
2174- "payment line directly"
2175- ),
2176- states={
2177- 'sent': [('readonly', True)],
2178- 'rejected': [('readonly', True)],
2179- 'done': [('readonly', True)]
2180- },
2181- ),
2182- 'state': fields.selection([
2183- ('normal','Free'),
2184- ('structured','Structured')
2185- ], 'Communication Type', required=True,
2186- states={
2187- 'sent': [('readonly', True)],
2188- 'rejected': [('readonly', True)],
2189- 'done': [('readonly', True)]
2190- },
2191- ),
2192- }
2193+ ),
2194+ 'transit_move_line_id': fields.many2one(
2195+ # this line is part of the credit side of move 2a
2196+ # from the documentation
2197+ 'account.move.line', 'Debit move line',
2198+ readonly=True,
2199+ help="Move line through which the debit order pays the invoice",
2200+ ),
2201+ }
2202+
2203 _defaults = {
2204- 'export_state': 'draft',
2205- 'date_done': False,
2206 'msg': '',
2207- }
2208-
2209- def fields_get(self, cr, uid, fields=None, context=None):
2210- res = super(payment_line, self).fields_get(cr, uid, fields, context)
2211- if 'communication' in res:
2212- res['communication'].setdefault('states', {})
2213- res['communication']['states']['structured'] = [('required', True)]
2214- if 'communication2' in res:
2215- res['communication2'].setdefault('states', {})
2216- res['communication2']['states']['structured'] = [('readonly', True)]
2217- res['communication2']['states']['normal'] = [('readonly', False)]
2218-
2219- return res
2220+ }
2221
2222 """
2223 Hooks for processing direct debit orders, such as implemented in
2224@@ -216,3 +105,76 @@
2225 """
2226
2227 return False
2228+
2229+ def debit_reconcile(self, cr, uid, payment_line_id, context=None):
2230+ """
2231+ Reconcile a debit order's payment line with the the move line
2232+ that it is based on. Called from payment_order.action_sent().
2233+ As the amount is derived directly from the counterpart move line,
2234+ we do not expect a write off. Take partially reconcilions into
2235+ account though.
2236+
2237+ :param payment_line_id: the single id of the canceled payment line
2238+ """
2239+
2240+ if isinstance(payment_line_id, (list, tuple)):
2241+ payment_line_id = payment_line_id[0]
2242+ reconcile_obj = self.pool.get('account.move.reconcile')
2243+ move_line_obj = self.pool.get('account.move.line')
2244+ payment_line = self.browse(cr, uid, payment_line_id, context=context)
2245+
2246+ transit_move_line = payment_line.transit_move_line_id
2247+ torec_move_line = payment_line.move_line_id
2248+
2249+ if (not transit_move_line or not torec_move_line):
2250+ raise orm.except_orm(
2251+ _('Can not reconcile'),
2252+ _('No move line for line %s') % payment_line.name)
2253+ if torec_move_line.reconcile_id: # torec_move_line.reconcile_partial_id:
2254+ raise orm.except_orm(
2255+ _('Error'),
2256+ _('Move line %s has already been reconciled') %
2257+ torec_move_line.name
2258+ )
2259+ if transit_move_line.reconcile_id or transit_move_line.reconcile_partial_id:
2260+ raise orm.except_orm(
2261+ _('Error'),
2262+ _('Move line %s has already been reconciled') %
2263+ transit_move_line.name
2264+ )
2265+
2266+ def is_zero(total):
2267+ return self.pool.get('res.currency').is_zero(
2268+ cr, uid, transit_move_line.company_id.currency_id, total)
2269+
2270+ line_ids = [transit_move_line.id, torec_move_line.id]
2271+ if torec_move_line.reconcile_partial_id:
2272+ line_ids = [
2273+ x.id for x in
2274+ transit_move_line.reconcile_partial_id.line_partial_ids
2275+ ] + [torec_move_line.id]
2276+
2277+ total = move_line_obj.get_balance(cr, uid, line_ids)
2278+ vals = {
2279+ 'type': 'auto',
2280+ 'line_id': is_zero(total) and [(6, 0, line_ids)] or [(6, 0, [])],
2281+ 'line_partial_ids': is_zero(total) and [(6, 0, [])] or [(6, 0, line_ids)],
2282+ }
2283+
2284+ if torec_move_line.reconcile_partial_id:
2285+ reconcile_obj.write(
2286+ cr, uid, transit_move_line.reconcile_partial_id.id,
2287+ vals, context=context)
2288+ else:
2289+ reconcile_obj.create(
2290+ cr, uid, vals, context=context)
2291+ for line_id in line_ids:
2292+ netsvc.LocalService("workflow").trg_trigger(
2293+ uid, 'account.move.line', line_id, cr)
2294+
2295+ # If a bank transaction of a storno was first confirmed
2296+ # and now canceled (the invoice is now in state 'debit_denied'
2297+ if torec_move_line.invoice:
2298+ netsvc.LocalService("workflow").trg_validate(
2299+ uid, 'account.invoice', torec_move_line.invoice.id,
2300+ 'undo_debit_denied', cr)
2301
2302=== modified file 'account_banking_payment/model/payment_mode.py'
2303--- account_banking_payment/model/payment_mode.py 2013-03-17 09:10:15 +0000
2304+++ account_banking_payment/model/payment_mode.py 2013-06-26 21:16:23 +0000
2305@@ -46,6 +46,27 @@
2306 _columns = {
2307 'type': fields.many2one(
2308 'payment.mode.type', 'Payment type',
2309+ required=True,
2310 help='Select the Payment Type for the Payment Mode.'
2311 ),
2312+ 'transfer_account_id': fields.many2one(
2313+ 'account.account', 'Transfer account',
2314+ domain=[('type', '=', 'other'),
2315+ ('reconcile', '=', True)],
2316+ help=('Pay off lines in sent orders with a '
2317+ 'move on this account. For debit type modes only. '
2318+ 'You can only select accounts of type regular that '
2319+ 'are marked for reconciliation'),
2320+ ),
2321+ 'transfer_journal_id': fields.many2one(
2322+ 'account.journal', 'Transfer journal',
2323+ help=('Journal to write payment entries when confirming '
2324+ 'a debit order of this mode'),
2325+ ),
2326+ 'payment_term_ids': fields.many2many(
2327+ 'account.payment.term', 'account_payment_order_terms_rel',
2328+ 'mode_id', 'term_id', 'Payment terms',
2329+ help=('Limit selected invoices to invoices with these payment '
2330+ 'terms')
2331+ ),
2332 }
2333
2334=== modified file 'account_banking_payment/model/payment_order_create.py'
2335--- account_banking_payment/model/payment_order_create.py 2013-06-04 21:19:18 +0000
2336+++ account_banking_payment/model/payment_order_create.py 2013-06-26 21:16:23 +0000
2337@@ -26,11 +26,77 @@
2338 from datetime import datetime
2339 from openerp.osv import orm, fields
2340 from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
2341+from openerp.tools.translate import _
2342
2343
2344 class payment_order_create(orm.TransientModel):
2345 _inherit = 'payment.order.create'
2346
2347+ def extend_payment_order_domain(
2348+ self, cr, uid, payment_order, domain, context=None):
2349+ if payment_order.payment_order_type == 'payment':
2350+ domain += [
2351+ ('account_id.type', '=', 'payable'),
2352+ ('amount_to_pay', '>', 0)
2353+ ]
2354+ return True
2355+
2356+ def search_entries(self, cr, uid, ids, context=None):
2357+ """
2358+ This method taken from account_payment module.
2359+ We adapt the domain based on the payment_order_type
2360+ """
2361+ line_obj = self.pool.get('account.move.line')
2362+ mod_obj = self.pool.get('ir.model.data')
2363+ if context is None:
2364+ context = {}
2365+ data = self.read(cr, uid, ids, ['duedate'], context=context)[0]
2366+ search_due_date = data['duedate']
2367+
2368+ ### start account_banking_payment ###
2369+ payment = self.pool.get('payment.order').browse(
2370+ cr, uid, context['active_id'], context=context)
2371+ # Search for move line to pay:
2372+ domain = [
2373+ ('move_id.state', '=', 'posted'),
2374+ ('reconcile_id', '=', False),
2375+ ('company_id', '=', payment.mode.company_id.id),
2376+ ]
2377+ # apply payment term filter
2378+ if payment.mode.payment_term_ids:
2379+ domain += [
2380+ ('invoice.payment_term', 'in',
2381+ [term.id for term in payment.mode.payment_term_ids]
2382+ )
2383+ ]
2384+ self.extend_payment_order_domain(
2385+ cr, uid, payment, domain, context=context)
2386+ ### end account_direct_debit ###
2387+
2388+ domain = domain + [
2389+ '|', ('date_maturity', '<=', search_due_date),
2390+ ('date_maturity', '=', False)
2391+ ]
2392+ line_ids = line_obj.search(cr, uid, domain, context=context)
2393+ context.update({'line_ids': line_ids})
2394+ model_data_ids = mod_obj.search(
2395+ cr, uid,[
2396+ ('model', '=', 'ir.ui.view'),
2397+ ('name', '=', 'view_create_payment_order_lines')],
2398+ context=context)
2399+ resource_id = mod_obj.read(
2400+ cr, uid, model_data_ids, fields=['res_id'],
2401+ context=context)[0]['res_id']
2402+ return {'name': _('Entry Lines'),
2403+ 'context': context,
2404+ 'view_type': 'form',
2405+ 'view_mode': 'form',
2406+ 'res_model': 'payment.order.create',
2407+ 'views': [(resource_id, 'form')],
2408+ 'type': 'ir.actions.act_window',
2409+ 'target': 'new',
2410+ }
2411+
2412 def create_payment(self, cr, uid, ids, context=None):
2413 '''
2414 This method is a slightly modified version of the existing method on this
2415@@ -51,7 +117,8 @@
2416 if not line_ids:
2417 return {'type': 'ir.actions.act_window_close'}
2418
2419- payment = order_obj.browse(cr, uid, context['active_id'], context=context)
2420+ payment = order_obj.browse(
2421+ cr, uid, context['active_id'], context=context)
2422 ### account banking
2423 # t = None
2424 # line2bank = line_obj.line2bank(cr, uid, line_ids, t, context)
2425@@ -75,10 +142,10 @@
2426 ### end account banking
2427 elif payment.date_prefered == 'fixed':
2428 ### account_banking
2429- # date_to_pay = payment.date_planned
2430+ # date_to_pay = payment.date_scheduled
2431 date_to_pay = (
2432- payment.date_planned
2433- if payment.date_planned and payment.date_planned > _today
2434+ payment.date_scheduled
2435+ if payment.date_scheduled and payment.date_scheduled > _today
2436 else False)
2437 ### end account banking
2438
2439@@ -125,6 +192,8 @@
2440 'state': state,
2441 ### end account banking
2442 'date': date_to_pay,
2443- 'currency': line.invoice and line.invoice.currency_id.id or line.journal_id.currency.id or line.journal_id.company_id.currency_id.id,
2444+ 'currency': (line.invoice and line.invoice.currency_id.id
2445+ or line.journal_id.currency.id
2446+ or line.journal_id.company_id.currency_id.id),
2447 }, context=context)
2448 return {'type': 'ir.actions.act_window_close'}
2449
2450=== modified file 'account_banking_payment/view/account_payment.xml'
2451--- account_banking_payment/view/account_payment.xml 2013-05-01 14:40:54 +0000
2452+++ account_banking_payment/view/account_payment.xml 2013-06-26 21:16:23 +0000
2453@@ -9,33 +9,28 @@
2454 <field name="name">account.payment.order.form.banking-1</field>
2455 <field name="inherit_id" ref="account_payment.view_payment_order_form" />
2456 <field name="model">payment.order</field>
2457- <field name="type">form</field>
2458 <field name="arch" type="xml">
2459 <data>
2460- <xpath expr="/form/group/button[@string='Select Invoices to Pay']"
2461- position="attributes">
2462- <attribute name="attrs">{'invisible':[('state','!=','draft')]}</attribute>
2463- </xpath>
2464- <xpath expr="/form/group/button[@string='Make Payments']"
2465- position="replace">
2466- <button name="launch_wizard" states="open" string="Make Payments" type="object" icon="gtk-execute"/>
2467- <newline/>
2468+ <xpath expr="//button[@string='Select Invoices to Pay']"
2469+ position="attributes">
2470+ <attribute name="attrs">{
2471+ 'invisible':[('state','!=','draft')]
2472+ }</attribute>
2473+ </xpath>
2474+ <xpath expr="//button[@string='Make Payments']"
2475+ position="attributes">
2476+ <attribute name="name">launch_wizard</attribute>
2477+ </xpath>
2478+ <!-- Communication only used for 'structured' communication -->
2479+ <xpath expr="//field[@name='line_ids']/form//field[@name='communication']"
2480+ position="attributes">
2481+ <attribute name="attrs">{
2482+ 'readonly': [('state', '=', 'normal')]
2483+ }</attribute>
2484 </xpath>
2485 </data>
2486 </field>
2487 </record>
2488
2489- <record id="view_banking_payment_order_tree_1" model="ir.ui.view">
2490- <field name="name">account.payment.order.tree.banking-1</field>
2491- <field name="inherit_id" ref="account_payment.view_payment_order_tree" />
2492- <field name="model">payment.order</field>
2493- <field name="type">tree</field>
2494- <field name="arch" type="xml">
2495- <button string="Make Payments" position="replace">
2496- <button name="launch_wizard" states="open" string="Make Payments" type="object" icon="gtk-execute"/>
2497- </button>
2498- </field>
2499- </record>
2500-
2501 </data>
2502 </openerp>
2503
2504=== modified file 'account_banking_payment/view/bank_payment_manual.xml'
2505--- account_banking_payment/view/bank_payment_manual.xml 2013-03-17 20:04:17 +0000
2506+++ account_banking_payment/view/bank_payment_manual.xml 2013-06-26 21:16:23 +0000
2507@@ -4,9 +4,8 @@
2508 <record id="view_payment_manual_form" model="ir.ui.view">
2509 <field name="name">Form for manual payment wizard</field>
2510 <field name="model">payment.manual</field>
2511- <field name="type">form</field>
2512 <field name="arch" type="xml">
2513- <form>
2514+ <form string="Manual payment">
2515 <label string="Payment order(s) have been set to 'sent'"/>
2516 <button special="cancel" icon="gtk-ok" string="OK"/>
2517 </form>
2518
2519=== modified file 'account_banking_payment/view/banking_transaction_wizard.xml'
2520--- account_banking_payment/view/banking_transaction_wizard.xml 2013-03-17 20:04:17 +0000
2521+++ account_banking_payment/view/banking_transaction_wizard.xml 2013-06-26 21:16:23 +0000
2522@@ -6,7 +6,6 @@
2523 <field name="model">banking.transaction.wizard</field>
2524 <field name="inherit_id"
2525 ref="account_banking.transaction_wizard_first" />
2526- <field name="type">form</field>
2527 <field name="arch" type="xml">
2528 <field name="invoice_ids" position="before">
2529 <field name="payment_order_ids" invisible="True"/>
2530@@ -14,20 +13,23 @@
2531 <xpath expr="//group/separator[@string='Multiple matches']/.."
2532 position="after">
2533 <field name='payment_line_id'
2534- attrs="{'invisible': [
2535- ('match_type', '!=', 'storno'),
2536- ('match_type', '!=', 'payment')]
2537- }" />
2538+ attrs="{'invisible': [('match_type', 'not in',
2539+ ('storno', 'payment', 'payment_manual'))]}"
2540+ />
2541 </xpath>
2542 <field name="move_line_id" position="after">
2543 <field name='payment_order_id'
2544- attrs="{'readonly': [
2545- ('match_multi', '=', False)],
2546- 'invisible': [
2547- ('match_type', '!=', 'payment_order')]}"
2548+ attrs="{'readonly': [('match_multi', '=', False)],
2549+ 'invisible': [('match_type', 'not in',
2550+ ('payment_order', 'payment_order_manual'))]
2551+ }"
2552 domain="[('id', 'in', payment_order_ids[0][2])]"
2553 />
2554 </field>
2555+ <field name="manual_move_line_id" position="after">
2556+ <field name="manual_payment_line_id"/>
2557+ <field name="manual_payment_order_id"/>
2558+ </field>
2559 </field>
2560 </record>
2561 </data>
2562
2563=== added file 'account_banking_payment/view/payment_mode.xml'
2564--- account_banking_payment/view/payment_mode.xml 1970-01-01 00:00:00 +0000
2565+++ account_banking_payment/view/payment_mode.xml 2013-06-26 21:16:23 +0000
2566@@ -0,0 +1,43 @@
2567+<?xml version="1.0" encoding="utf-8"?>
2568+<openerp>
2569+ <data>
2570+
2571+ <!--
2572+ Add the payment mode type and transfer settings
2573+ -->
2574+ <record id="view_payment_mode_form_inherit" model="ir.ui.view">
2575+ <field name="name">payment.mode.form.inherit</field>
2576+ <field name="model">payment.mode</field>
2577+ <field name="inherit_id" ref="account_payment.view_payment_mode_form"/>
2578+ <field name="arch" type="xml">
2579+ <field name="company_id" position="after">
2580+ <field name="type"/>
2581+ <group colspan="4" col="4">
2582+ <group colspan="2">
2583+ <separator colspan="2"
2584+ string="Transfer move settings" />
2585+ <field name="transfer_account_id"
2586+ domain="[('type', '=', 'other'),
2587+ ('reconcile', '=', True),
2588+ ('company_id', '=', company_id)]"
2589+ context="{
2590+ 'default_type': 'other',
2591+ 'default_reconcile': True,
2592+ 'default_company_id': company_id}"
2593+ />
2594+ <field name="transfer_journal_id"
2595+ domain="[('company_id', '=', company_id)]"
2596+ />
2597+ </group>
2598+ <group colspan="2">
2599+ <separator colspan="2"
2600+ string="Optional filter by payment term" />
2601+ <field name="payment_term_ids" nolabel="1" colspan="2"/>
2602+ </group>
2603+ </group>
2604+ </field>
2605+ </field>
2606+ </record>
2607+
2608+ </data>
2609+</openerp>
2610
2611=== modified file 'account_banking_payment/view/payment_mode_type.xml'
2612--- account_banking_payment/view/payment_mode_type.xml 2013-03-16 16:44:19 +0000
2613+++ account_banking_payment/view/payment_mode_type.xml 2013-06-26 21:16:23 +0000
2614@@ -2,24 +2,10 @@
2615 <openerp>
2616 <data>
2617
2618- <!-- Add the payment mode type to the payment mode views -->
2619- <record id="view_payment_mode_form_inherit" model="ir.ui.view">
2620- <field name="name">payment.mode.form.inherit</field>
2621- <field name="model">payment.mode</field>
2622- <field name="inherit_id" ref="account_payment.view_payment_mode_form"/>
2623- <field name="type">form</field>
2624- <field name="arch" type="xml">
2625- <field name="company_id" position="after">
2626- <field name="type"/>
2627- </field>
2628- </field>
2629- </record>
2630-
2631 <record id="view_payment_mode_tree_inherit" model="ir.ui.view">
2632 <field name="name">payment.mode.tree.inherit</field>
2633 <field name="model">payment.mode</field>
2634 <field name="inherit_id" ref="account_payment.view_payment_mode_tree"/>
2635- <field name="type">tree</field>
2636 <field name="arch" type="xml">
2637 <field name="company_id" position="after">
2638 <field name="type"/>
2639@@ -31,9 +17,8 @@
2640 <record model="ir.ui.view" id="view_payment_mode_type_form">
2641 <field name="name">view.payment.mode.type.form</field>
2642 <field name="model">payment.mode.type</field>
2643- <field name="type">form</field>
2644 <field name="arch" type="xml">
2645- <form>
2646+ <form string="Payment mode">
2647 <field name="name" />
2648 <field name="code" />
2649 <field name="suitable_bank_types"/>
2650
2651=== modified file 'account_banking_payment/workflow/account_payment.xml'
2652--- account_banking_payment/workflow/account_payment.xml 2013-03-16 16:44:19 +0000
2653+++ account_banking_payment/workflow/account_payment.xml 2013-06-26 21:16:23 +0000
2654@@ -8,6 +8,13 @@
2655 <field name="action">action_sent()</field>
2656 <field name="kind">function</field>
2657 </record>
2658+ <!-- New activity for workflow payment order: sent -->
2659+ <record id="account_banking.act_sent_wait" model="workflow.activity">
2660+ <field name="name">sent_wait</field>
2661+ <field name="wkf_id" ref="account_payment.wkf_payment_order"/>
2662+ <field name="action">write({'state': 'sent'})</field>
2663+ <field name="kind">function</field>
2664+ </record>
2665 <!-- New activity for workflow payment order: rejected -->
2666 <record id="account_banking.act_rejected" model="workflow.activity">
2667 <field name="name">rejected</field>
2668@@ -16,23 +23,47 @@
2669 write({'state':'rejected'})</field>
2670 <field name="kind">function</field>
2671 </record>
2672- <!-- Add new transition sent -> done -->
2673+ <!-- Rewrite existing open -> done transition to include 'sent' stage -->
2674+ <record id="account_payment.trans_open_done" model="workflow.transition">
2675+ <field name="act_from" ref="account_payment.act_open"/>
2676+ <field name="act_to" ref="account_banking.act_sent"/>
2677+ <field name="signal">sent</field>
2678+ </record>
2679+ <!-- From sent straight to sent_wait -->
2680+ <record id="account_banking.trans_sent_sent_wait" model="workflow.transition">
2681+ <field name="act_from" ref="account_banking.act_sent"/>
2682+ <field name="act_to" ref="account_banking.act_sent_wait"/>
2683+ </record>
2684+ <!-- Reconciliation from the banking statement leads to done state -->
2685 <record id="account_banking.trans_sent_done" model="workflow.transition">
2686- <field name="act_from" ref="account_banking.act_sent"/>
2687+ <field name="act_from" ref="account_banking.act_sent_wait"/>
2688 <field name="act_to" ref="account_payment.act_done"/>
2689 <field name="signal">done</field>
2690 </record>
2691- <!-- Add new transition sent -> rejected -->
2692+ <!-- Rejected by the bank -->
2693 <record id="account_banking.trans_sent_rejected" model="workflow.transition">
2694 <field name="act_from" ref="account_banking.act_sent"/>
2695 <field name="act_to" ref="account_banking.act_rejected"/>
2696 <field name="signal">rejected</field>
2697 </record>
2698- <!-- Rewrite existing open -> done transition to include 'sent' -->
2699- <record id="account_payment.trans_open_done" model="workflow.transition">
2700- <field name="act_from" ref="account_payment.act_open"/>
2701- <field name="act_to" ref="account_banking.act_sent"/>
2702- <field name="signal">sent</field>
2703- </record>
2704+ <!--
2705+ Transition to undo the payment order and reset to
2706+ sent, triggered by cancelling a bank transaction
2707+ with which the order was reconciled.
2708+ For this, we need to cancel the flow stop on the done state,
2709+ unfortunately.
2710+ -->
2711+ <record id="account_payment.act_done" model="workflow.activity">
2712+ <field name="flow_stop" eval="False"/>
2713+ </record>
2714+
2715+ <!-- Cancel the reconciled payment order -->
2716+ <record id="trans_done_sent" model="workflow.transition">
2717+ <field name="act_from" ref="account_payment.act_done"/>
2718+ <field name="act_to" ref="account_banking.act_sent_wait"/>
2719+ <field name="condition">test_undo_done()</field>
2720+ <field name="signal">undo_done</field>
2721+ </record>
2722+
2723 </data>
2724 </openerp>
2725
2726=== modified file 'account_banking_uk_hsbc/__openerp__.py'
2727--- account_banking_uk_hsbc/__openerp__.py 2013-04-15 14:14:27 +0000
2728+++ account_banking_uk_hsbc/__openerp__.py 2013-06-26 21:16:23 +0000
2729@@ -25,7 +25,7 @@
2730 'author': 'credativ Ltd',
2731 'website': 'http://www.credativ.co.uk',
2732 'category': 'Account Banking',
2733- 'depends': ['account_banking'],
2734+ 'depends': ['account_banking_payment'],
2735 'data': [
2736 'account_banking_uk_hsbc.xml',
2737 'hsbc_clientid_view.xml',
2738
2739=== modified file 'account_direct_debit/__openerp__.py'
2740--- account_direct_debit/__openerp__.py 2013-04-15 14:14:27 +0000
2741+++ account_direct_debit/__openerp__.py 2013-06-26 21:16:23 +0000
2742@@ -1,6 +1,6 @@
2743 ##############################################################################
2744 #
2745-# Copyright (C) 2011 Therp BV (<http://therp.nl>).
2746+# Copyright (C) 2011 - 2013 Therp BV (<http://therp.nl>).
2747 # Copyright (C) 2011 Smile (<http://smile.fr>).
2748 # All Rights Reserved
2749 #
2750@@ -20,9 +20,9 @@
2751 ##############################################################################
2752 {
2753 'name': 'Direct Debit',
2754- 'version': '6.1.1.134',
2755+ 'version': '7.0.2.134',
2756 'license': 'AGPL-3',
2757- 'author': 'Therp BV / Smile',
2758+ 'author': ['Therp BV', 'Smile'],
2759 'website': 'https://launchpad.net/banking-addons',
2760 'category': 'Banking addons',
2761 'depends': ['account_banking'],
2762@@ -30,7 +30,6 @@
2763 'view/account_payment.xml',
2764 'view/account_invoice.xml',
2765 'workflow/account_invoice.xml',
2766- 'workflow/account_payment.xml',
2767 'data/account_payment_term.xml',
2768 ],
2769 'description': '''
2770@@ -49,5 +48,5 @@
2771 banking institutions. The banking addons are a continuation of Account Banking
2772 Framework by Edusense BV. See https://launchpad.net/banking-addons.
2773 ''',
2774- 'installable': False,
2775+ 'installable': True,
2776 }
2777
2778=== modified file 'account_direct_debit/i18n/nl.po'
2779--- account_direct_debit/i18n/nl.po 2012-08-08 10:32:52 +0000
2780+++ account_direct_debit/i18n/nl.po 2013-06-26 21:16:23 +0000
2781@@ -175,7 +175,7 @@
2782 msgstr "De betaalregelnaam moet uniek zijn!"
2783
2784 #. module: account_direct_debit
2785-#: field:payment.line,debit_move_line_id:0
2786+#: field:payment.line,transit_move_line_id:0
2787 msgid "Debit move line"
2788 msgstr "Debetboeking"
2789
2790@@ -200,7 +200,7 @@
2791 msgstr "Factuur"
2792
2793 #. module: account_direct_debit
2794-#: help:payment.line,debit_move_line_id:0
2795+#: help:payment.line,transit_move_line_id:0
2796 msgid "Move line through which the debit order pays the invoice"
2797 msgstr "Dagboekregel waarmee de incasso-opdracht de factuur voldoet"
2798
2799
2800=== added directory 'account_direct_debit/migrations'
2801=== added directory 'account_direct_debit/migrations/7.0.2'
2802=== added file 'account_direct_debit/migrations/7.0.2/pre-migration.py'
2803--- account_direct_debit/migrations/7.0.2/pre-migration.py 1970-01-01 00:00:00 +0000
2804+++ account_direct_debit/migrations/7.0.2/pre-migration.py 2013-06-26 21:16:23 +0000
2805@@ -0,0 +1,57 @@
2806+# -*- coding: utf-8 -*-
2807+##############################################################################
2808+#
2809+# Copyright (C) 2013 Therp BV (<http://therp.nl>).
2810+#
2811+# All other contributions are (C) by their respective contributors
2812+#
2813+# All Rights Reserved
2814+#
2815+# This program is free software: you can redistribute it and/or modify
2816+# it under the terms of the GNU Affero General Public License as
2817+# published by the Free Software Foundation, either version 3 of the
2818+# License, or (at your option) any later version.
2819+#
2820+# This program is distributed in the hope that it will be useful,
2821+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2822+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2823+# GNU Affero General Public License for more details.
2824+#
2825+# You should have received a copy of the GNU Affero General Public License
2826+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2827+#
2828+##############################################################################
2829+
2830+def rename_columns(cr, column_spec):
2831+ """
2832+ Rename table columns. Taken from OpenUpgrade.
2833+
2834+ :param column_spec: a hash with table keys, with lists of tuples as values. \
2835+ Tuples consist of (old_name, new_name).
2836+
2837+ """
2838+ for table in column_spec.keys():
2839+ for (old, new) in column_spec[table]:
2840+ logger.info("table %s, column %s: renaming to %s",
2841+ table, old, new)
2842+ cr.execute('ALTER TABLE "%s" RENAME "%s" TO "%s"' % (table, old, new,))
2843+ cr.execute('DROP INDEX IF EXISTS "%s_%s_index"' % (table, old))
2844+
2845+def migrate(cr, version):
2846+ if not version:
2847+ return
2848+
2849+ # workflow state moved to another module
2850+ cr.execute(
2851+ """
2852+ UPDATE ir_model_data
2853+ SET module = 'account_banking_payment'
2854+ WHERE name = 'trans_done_sent'
2855+ AND module = 'account_direct_debit'
2856+ """)
2857+
2858+ # rename field debit_move_line_id
2859+ rename_columns(cr, {
2860+ 'payment_line': [
2861+ ('debit_move_line_id', 'transit_move_line_id'),
2862+ ]})
2863
2864=== modified file 'account_direct_debit/model/__init__.py'
2865--- account_direct_debit/model/__init__.py 2011-12-11 15:00:41 +0000
2866+++ account_direct_debit/model/__init__.py 2013-06-26 21:16:23 +0000
2867@@ -1,3 +1,5 @@
2868 import account_payment
2869+import payment_line
2870 import account_move_line
2871 import account_invoice
2872+import payment_order_create
2873
2874=== modified file 'account_direct_debit/model/account_invoice.py'
2875--- account_direct_debit/model/account_invoice.py 2012-01-12 10:58:49 +0000
2876+++ account_direct_debit/model/account_invoice.py 2013-06-26 21:16:23 +0000
2877@@ -1,6 +1,29 @@
2878 # -*- coding: utf-8 -*-
2879-from osv import osv, fields
2880-from tools.translate import _
2881+##############################################################################
2882+#
2883+# Copyright (C) 2011 - 2013 Therp BV (<http://therp.nl>).
2884+#
2885+# All other contributions are (C) by their respective contributors
2886+#
2887+# All Rights Reserved
2888+#
2889+# This program is free software: you can redistribute it and/or modify
2890+# it under the terms of the GNU Affero General Public License as
2891+# published by the Free Software Foundation, either version 3 of the
2892+# License, or (at your option) any later version.
2893+#
2894+# This program is distributed in the hope that it will be useful,
2895+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2896+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2897+# GNU Affero General Public License for more details.
2898+#
2899+# You should have received a copy of the GNU Affero General Public License
2900+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2901+#
2902+##############################################################################
2903+
2904+from openerp.osv import orm, fields
2905+from openerp.tools.translate import _
2906
2907 """
2908 This module adds support for Direct debit orders as applicable
2909@@ -98,7 +121,7 @@
2910 open invoices with a matured invoice- or due date.
2911 """
2912
2913-class account_invoice(osv.osv):
2914+class account_invoice(orm.Model):
2915 _inherit = "account.invoice"
2916
2917 def __init__(self, pool, cr):
2918@@ -139,5 +162,3 @@
2919 if not invoice['reconciled']:
2920 return False
2921 return True
2922-
2923-account_invoice()
2924
2925=== modified file 'account_direct_debit/model/account_move_line.py'
2926--- account_direct_debit/model/account_move_line.py 2013-04-24 14:36:15 +0000
2927+++ account_direct_debit/model/account_move_line.py 2013-06-26 21:16:23 +0000
2928@@ -2,9 +2,8 @@
2929 ##############################################################################
2930 #
2931 # OpenERP, Open Source Management Solution
2932-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2933-# This module additional (C) 2011 Therp BV (<http://therp.nl>).
2934-# (C) 2011 Smile Benelux (<http://smile.fr>).
2935+# This module (C) 2011 - 2013 Therp BV (<http://therp.nl>).
2936+# (C) 2011 Smile Benelux (<http://smile.fr>).
2937 #
2938 # This program is free software: you can redistribute it and/or modify
2939 # it under the terms of the GNU Affero General Public License as
2940@@ -22,10 +21,9 @@
2941 ##############################################################################
2942
2943 from operator import itemgetter
2944-from osv import fields, osv
2945-from tools.translate import _
2946+from openerp.osv import fields, orm
2947
2948-class account_move_line(osv.osv):
2949+class account_move_line(orm.Model):
2950 _inherit = "account.move.line"
2951
2952 def amount_to_receive(self, cr, uid, ids, name, arg={}, context=None):
2953@@ -55,6 +53,9 @@
2954 return r
2955
2956 def _to_receive_search(self, cr, uid, obj, name, args, context=None):
2957+ """
2958+ Reverse of account_payment/account_move_line.py:_to_pay_search()
2959+ """
2960 if not args:
2961 return []
2962 line_obj = self.pool.get('account.move.line')
2963@@ -86,70 +87,9 @@
2964 return [('id', '=', '0')]
2965 return [('id', 'in', map(lambda x:x[0], res))]
2966
2967- def _dummy(self, cr, user, ids, name, arg, context=None):
2968- res = {}
2969- if ids:
2970- res = dict([(x, False) for x in ids])
2971- return res
2972-
2973- def _invoice_payment_term_id_search(
2974- self, cr, uid, obj, name, args, context=None):
2975- """
2976- Allow to search move lines associated with an invoice with
2977- a particular payment term
2978- """
2979- if not args:
2980- return []
2981- invoice_obj = self.pool.get('account.invoice')
2982- invoice_ids = invoice_obj.search(
2983- cr, uid, [('payment_term', args[0][1], args[0][2])],
2984- context=context)
2985- operator = 'in' # (args[0][1] not in ['in', '=', '==', 'like', 'ilike']
2986- # and 'not in' or 'in')
2987- if not invoice_ids:
2988- return [('id', operator, [])]
2989- cr.execute('SELECT l.id ' \
2990- 'FROM account_move_line l, account_invoice i ' \
2991- 'WHERE l.move_id = i.move_id AND i.id in %s', (tuple(invoice_ids),))
2992- res = cr.fetchall()
2993- if not res:
2994- return [('id', '=', False)]
2995- return [('id', operator, [x[0] for x in res])]
2996-
2997- def _invoice_state_search(self, cr, uid, obj, name, args, context=None):
2998- if not args:
2999- return []
3000- invoice_obj = self.pool.get('account.invoice')
3001- invoice_ids = invoice_obj.search(
3002- cr, uid, [('state', args[0][1], args[0][2])],
3003- context=context)
3004- operator = 'in' # (args[0][1] not in ['in', '=', '==', 'like', 'ilike']
3005- # and 'not in' or 'in')
3006- if not invoice_ids:
3007- return [('id', operator, [])]
3008- cr.execute('SELECT l.id ' \
3009- 'FROM account_move_line l, account_invoice i ' \
3010- 'WHERE l.move_id = i.move_id AND i.id in %s', (tuple(invoice_ids),))
3011- res = cr.fetchall()
3012- if not res:
3013- return [('id', '=', False)]
3014- return [('id', operator, [x[0] for x in res])]
3015-
3016 _columns = {
3017 'amount_to_receive': fields.function(
3018 amount_to_receive, method=True,
3019 type='float', string='Amount to receive',
3020 fnct_search=_to_receive_search),
3021- 'payment_term_id': fields.function(
3022- _dummy, method=True,
3023- string='Select by invoice payment term',
3024- type='many2one', relation='account.payment.term',
3025- fnct_search=_invoice_payment_term_id_search),
3026- 'invoice_state': fields.function(
3027- _dummy, method=True,
3028- string='Select by invoice state',
3029- type='char', size=24,
3030- fnct_search=_invoice_state_search),
3031 }
3032-
3033-account_move_line()
3034
3035=== modified file 'account_direct_debit/model/account_payment.py'
3036--- account_direct_debit/model/account_payment.py 2013-01-21 11:30:46 +0000
3037+++ account_direct_debit/model/account_payment.py 2013-06-26 21:16:23 +0000
3038@@ -1,36 +1,9 @@
3039 # -*- coding: utf-8 -*-
3040-import time
3041-from osv import osv, fields
3042+from openerp.osv import orm, fields
3043 import netsvc
3044 from tools.translate import _
3045
3046-class payment_mode(osv.osv):
3047- _inherit = 'payment.mode'
3048- _columns = {
3049- 'transfer_account_id': fields.many2one(
3050- 'account.account', 'Transfer account',
3051- domain=[('type', '=', 'other'),
3052- ('reconcile', '=', True)],
3053- help=('Pay off lines in sent orders with a ' +
3054- 'move on this account. For debit type modes only. ' +
3055- 'You can only select accounts of type regular that ' +
3056- 'are marked for reconciliation'),
3057- ),
3058- 'transfer_journal_id': fields.many2one(
3059- 'account.journal', 'Transfer journal',
3060- help=('Journal to write payment entries when confirming ' +
3061- 'a debit order of this mode'),
3062- ),
3063- 'payment_term_ids': fields.many2many(
3064- 'account.payment.term', 'account_payment_order_terms_rel',
3065- 'mode_id', 'term_id', 'Payment terms',
3066- help=('Limit selected invoices to invoices with these payment ' +
3067- 'terms')
3068- ),
3069- }
3070-payment_mode()
3071-
3072-class payment_order(osv.osv):
3073+class payment_order(orm.Model):
3074 _inherit = 'payment.order'
3075
3076 def fields_view_get(self, cr, user, view_id=None, view_type='form',
3077@@ -56,56 +29,11 @@
3078 context['search_payment_order_type'])]
3079 # the magic is in the value of the selection
3080 res['fields']['mode']['selection'] = mode_obj._name_search(
3081- cr, user, args=domain)
3082+ cr, user, args=domain, context=context)
3083 # also update the domain
3084 res['fields']['mode']['domain'] = domain
3085 return res
3086
3087- def debit_reconcile_transfer(self, cr, uid, payment_order_id,
3088- amount, currency, context=None):
3089- """
3090- During import of bank statements, create the reconcile on the transfer
3091- account containing all the open move lines on the transfer account.
3092- """
3093- move_line_obj = self.pool.get('account.move.line')
3094- order = self.browse(cr, uid, payment_order_id, context)
3095- line_ids = []
3096- reconcile_id = False
3097- for order_line in order.line_ids:
3098- for line in order_line.debit_move_line_id.move_id.line_id:
3099- if line.account_id.type == 'other' and not line.reconcile_id:
3100- line_ids.append(line.id)
3101- if self.pool.get('res.currency').is_zero(
3102- cr, uid, currency,
3103- move_line_obj.get_balance(cr, uid, line_ids) - amount):
3104- reconcile_id = self.pool.get('account.move.reconcile').create(
3105- cr, uid,
3106- {'type': 'auto', 'line_id': [(6, 0, line_ids)]},
3107- context)
3108- # set direct debit order to finished state
3109- wf_service = netsvc.LocalService('workflow')
3110- wf_service.trg_validate(
3111- uid, 'payment.order', payment_order_id, 'done', cr)
3112- return reconcile_id
3113-
3114- def debit_unreconcile_transfer(self, cr, uid, payment_order_id, reconcile_id,
3115- amount, currency, context=None):
3116- """
3117- Due to a cancelled bank statements import, unreconcile the move on
3118- the transfer account. Delegate the conditions to the workflow.
3119- Raise on failure for rollback.
3120- """
3121- self.pool.get('account.move.reconcile').unlink(
3122- cr, uid, reconcile_id, context=context)
3123- wkf_ok = netsvc.LocalService('workflow').trg_validate(
3124- uid, 'payment.order', payment_order_id, 'undo_done', cr)
3125- if not wkf_ok:
3126- raise osv.except_osv(
3127- _("Cannot unreconcile"),
3128- _("Cannot unreconcile debit order: "+
3129- "Workflow will not allow it."))
3130- return True
3131-
3132 def test_undo_done(self, cr, uid, ids, context=None):
3133 """
3134 Called from the workflow. Used to unset done state on
3135@@ -117,362 +45,5 @@
3136 for line in order.line_ids:
3137 if line.storno:
3138 return False
3139- else:
3140- # TODO: define conditions for 'payment' orders
3141- return False
3142- return True
3143-
3144- def action_sent(self, cr, uid, ids, context=None):
3145- """
3146- Create the moves that pay off the move lines from
3147- the debit order. This happens when the debit order file is
3148- generated.
3149- """
3150- res = super(payment_order, self).action_sent(
3151- cr, uid, ids, context)
3152-
3153- account_move_obj = self.pool.get('account.move')
3154- account_move_line_obj = self.pool.get('account.move.line')
3155- payment_line_obj = self.pool.get('payment.line')
3156- for order in self.browse(cr, uid, ids, context=context):
3157- if order.payment_order_type != 'debit':
3158- continue
3159- for line in order.line_ids:
3160- # basic checks
3161- if not line.move_line_id:
3162- raise osv.except_osv(
3163- _('Error'),
3164- _('No move line provided for line %s') % line.name)
3165- if line.move_line_id.reconcile_id:
3166- raise osv.except_osv(
3167- _('Error'),
3168- _('Move line %s has already been paid/reconciled') %
3169- line.move_line_id.name
3170- )
3171-
3172- move_id = account_move_obj.create(cr, uid, {
3173- 'journal_id': order.mode.transfer_journal_id.id,
3174- 'name': 'Debit order %s' % line.move_line_id.move_id.name,
3175- 'reference': 'DEB%s' % line.move_line_id.move_id.name,
3176- }, context=context)
3177-
3178- # TODO: take multicurrency into account
3179-
3180- # create the debit move line on the transfer account
3181- vals = {
3182- 'name': 'Debit order for %s' % (
3183- line.move_line_id.invoice and
3184- line.move_line_id.invoice.number or
3185- line.move_line_id.name),
3186- 'move_id': move_id,
3187- 'partner_id': line.partner_id.id,
3188- 'account_id': order.mode.transfer_account_id.id,
3189- 'credit': 0.0,
3190- 'debit': line.amount,
3191- 'date': time.strftime('%Y-%m-%d'),
3192- }
3193- transfer_move_line_id = account_move_line_obj.create(
3194- cr, uid, vals, context=context)
3195-
3196- # create the debit move line on the receivable account
3197- vals.update({
3198- 'account_id': line.move_line_id.account_id.id,
3199- 'credit': line.amount,
3200- 'debit': 0.0,
3201- })
3202- reconcile_move_line_id = account_move_line_obj.create(
3203- cr, uid, vals, context=context)
3204-
3205- # register the debit move line on the payment line
3206- # and call reconciliation on it
3207- payment_line_obj.write(
3208- cr, uid, line.id,
3209- {'debit_move_line_id': reconcile_move_line_id},
3210- context=context)
3211-
3212- payment_line_obj.debit_reconcile(
3213- cr, uid, line.id, context=context)
3214- account_move_obj.post(cr, uid, [move_id], context=context)
3215- return res
3216-
3217-payment_order()
3218-
3219-class payment_line(osv.osv):
3220- _inherit = 'payment.line'
3221-
3222- def debit_storno(self, cr, uid, payment_line_id, amount,
3223- currency, storno_retry=True, context=None):
3224- """
3225- The processing of a storno is triggered by a debit
3226- transfer on one of the company's bank accounts.
3227- This method offers to re-reconcile the original debit
3228- payment. For this purpose, we have registered that
3229- payment move on the payment line.
3230-
3231- Return the (now incomplete) reconcile id. The caller MUST
3232- re-reconcile this reconcile with the bank transfer and
3233- re-open the associated invoice.
3234-
3235- :param payment_line_id: the single payment line id
3236- :param amount: the (signed) amount debited from the bank account
3237- :param currency: the bank account's currency *browse object*
3238- :param boolean storno_retry: when True, attempt to reopen the invoice, \
3239- set the invoice to 'Debit denied' otherwise.
3240- :return: an incomplete reconcile for the caller to fill
3241- :rtype: database id of an account.move.reconcile resource.
3242- """
3243-
3244- move_line_obj = self.pool.get('account.move.line')
3245- reconcile_obj = self.pool.get('account.move.reconcile')
3246- line = self.browse(cr, uid, payment_line_id)
3247- reconcile_id = False
3248- if (line.debit_move_line_id and not line.storno and
3249- self.pool.get('res.currency').is_zero(
3250- cr, uid, currency, (
3251- (line.debit_move_line_id.credit or 0.0) -
3252- (line.debit_move_line_id.debit or 0.0) + amount))):
3253- # Two different cases, full and partial
3254- # Both cases differ subtly in the procedure to follow
3255- # Needs refractoring, but why is this not in the OpenERP API?
3256- # Actually, given the nature of a direct debit order and storno,
3257- # we should not need to take partial into account on the side of
3258- # the debit_move_line.
3259- if line.debit_move_line_id.reconcile_partial_id:
3260- reconcile_id = line.debit_move_line_id.reconcile_partial_id.id
3261- attribute = 'reconcile_partial_id'
3262- if len(line.debit_move_line_id.reconcile_id.line_partial_ids) == 2:
3263- # reuse the simple reconcile for the storno transfer
3264- reconcile_obj.write(
3265- cr, uid, reconcile_id, {
3266- 'line_id': [(6, 0, line.debit_move_line_id.id)],
3267- 'line_partial_ids': [(6, 0, [])],
3268- }, context=context)
3269- else:
3270- # split up the original reconcile in a partial one
3271- # and a new one for reconciling the storno transfer
3272- reconcile_obj.write(
3273- cr, uid, reconcile_id, {
3274- 'line_partial_ids': [(3, line.debit_move_line_id.id)],
3275- }, context=context)
3276- reconcile_id = reconcile_obj.create(
3277- cr, uid, {
3278- 'type': 'auto',
3279- 'line_id': [(6, 0, line.debit_move_line_id.id)],
3280- }, context=context)
3281- elif line.debit_move_line_id.reconcile_id:
3282- reconcile_id = line.debit_move_line_id.reconcile_id.id
3283- if len(line.debit_move_line_id.reconcile_id.line_id) == 2:
3284- # reuse the simple reconcile for the storno transfer
3285- reconcile_obj.write(
3286- cr, uid, reconcile_id, {
3287- 'line_id': [(6, 0, [line.debit_move_line_id.id])]
3288- }, context=context)
3289- else:
3290- # split up the original reconcile in a partial one
3291- # and a new one for reconciling the storno transfer
3292- partial_ids = [
3293- x.id for x in line.debit_move_line_id.reconcile_id.line_id
3294- if x.id != line.debit_move_line_id.id
3295- ]
3296- reconcile_obj.write(
3297- cr, uid, reconcile_id, {
3298- 'line_partial_ids': [(6, 0, partial_ids)],
3299- 'line_id': [(6, 0, [])],
3300- }, context=context)
3301- reconcile_id = reconcile_obj.create(
3302- cr, uid, {
3303- 'type': 'auto',
3304- 'line_id': [(6, 0, line.debit_move_line_id.id)],
3305- }, context=context)
3306- # mark the payment line for storno processed
3307- if reconcile_id:
3308- self.write(cr, uid, [payment_line_id],
3309- {'storno': True}, context=context)
3310- # put forth the invoice workflow
3311- if line.move_line_id.invoice:
3312- activity = (storno_retry and 'open_test'
3313- or 'invoice_debit_denied')
3314- netsvc.LocalService("workflow").trg_validate(
3315- uid, 'account.invoice', line.move_line_id.invoice.id,
3316- activity, cr)
3317- return reconcile_id
3318-
3319- def get_storno_account_id(self, cr, uid, payment_line_id, amount,
3320- currency, context=None):
3321- """
3322- Check the match of the arguments, and return the account associated
3323- with the storno.
3324- Used in account_banking interactive mode
3325-
3326- :param payment_line_id: the single payment line id
3327- :param amount: the (signed) amount debited from the bank account
3328- :param currency: the bank account's currency *browse object*
3329- :return: an account if there is a full match, False otherwise
3330- :rtype: database id of an account.account resource.
3331- """
3332-
3333- line = self.browse(cr, uid, payment_line_id)
3334- account_id = False
3335- if (line.debit_move_line_id and not line.storno and
3336- self.pool.get('res.currency').is_zero(
3337- cr, uid, currency, (
3338- (line.debit_move_line_id.credit or 0.0) -
3339- (line.debit_move_line_id.debit or 0.0) + amount))):
3340- account_id = line.debit_move_line_id.account_id.id
3341- return account_id
3342-
3343- def debit_reconcile(self, cr, uid, payment_line_id, context=None):
3344- """
3345- Reconcile a debit order's payment line with the the move line
3346- that it is based on. Called from payment_order.action_sent().
3347- As the amount is derived directly from the counterpart move line,
3348- we do not expect a write off. Take partially reconcilions into
3349- account though.
3350-
3351- :param payment_line_id: the single id of the canceled payment line
3352- """
3353-
3354- if isinstance(payment_line_id, (list, tuple)):
3355- payment_line_id = payment_line_id[0]
3356- reconcile_obj = self.pool.get('account.move.reconcile')
3357- move_line_obj = self.pool.get('account.move.line')
3358- payment_line = self.browse(cr, uid, payment_line_id, context=context)
3359-
3360- debit_move_line = payment_line.debit_move_line_id
3361- torec_move_line = payment_line.move_line_id
3362-
3363- if payment_line.storno:
3364- raise osv.except_osv(
3365- _('Can not reconcile'),
3366- _('Cancelation of payment line \'%s\' has already been ' +
3367- 'processed') % payment_line.name)
3368- if (not debit_move_line or not torec_move_line):
3369- raise osv.except_osv(
3370- _('Can not reconcile'),
3371- _('No move line for line %s') % payment_line.name)
3372- if torec_move_line.reconcile_id: # torec_move_line.reconcile_partial_id:
3373- raise osv.except_osv(
3374- _('Error'),
3375- _('Move line %s has already been reconciled') %
3376- torec_move_line.name
3377- )
3378- if debit_move_line.reconcile_id or debit_move_line.reconcile_partial_id:
3379- raise osv.except_osv(
3380- _('Error'),
3381- _('Move line %s has already been reconciled') %
3382- debit_move_line.name
3383- )
3384-
3385- def is_zero(total):
3386- return self.pool.get('res.currency').is_zero(
3387- cr, uid, debit_move_line.company_id.currency_id, total)
3388-
3389- line_ids = [debit_move_line.id, torec_move_line.id]
3390- if torec_move_line.reconcile_partial_id:
3391- line_ids = [
3392- x.id for x in debit_move_line.reconcile_partial_id.line_partial_ids] + [torec_move_line_id]
3393-
3394- total = move_line_obj.get_balance(cr, uid, line_ids)
3395- vals = {
3396- 'type': 'auto',
3397- 'line_id': is_zero(total) and [(6, 0, line_ids)] or [(6, 0, [])],
3398- 'line_partial_ids': is_zero(total) and [(6, 0, [])] or [(6, 0, line_ids)],
3399- }
3400-
3401- if torec_move_line.reconcile_partial_id:
3402- reconcile_obj.write(
3403- cr, uid, debit_move_line.reconcile_partial_id.id,
3404- vals, context=context)
3405- else:
3406- reconcile_obj.create(
3407- cr, uid, vals, context=context)
3408- for line_id in line_ids:
3409- netsvc.LocalService("workflow").trg_trigger(
3410- uid, 'account.move.line', line_id, cr)
3411-
3412- # If a bank transaction of a storno was first confirmed
3413- # and now canceled (the invoice is now in state 'debit_denied'
3414- if torec_move_line.invoice:
3415- netsvc.LocalService("workflow").trg_validate(
3416- uid, 'account.invoice', torec_move_line.invoice.id,
3417- 'undo_debit_denied', cr)
3418-
3419-
3420-
3421- _columns = {
3422- 'debit_move_line_id': fields.many2one(
3423- # this line is part of the credit side of move 2a
3424- # from the documentation
3425- 'account.move.line', 'Debit move line',
3426- readonly=True,
3427- help="Move line through which the debit order pays the invoice"),
3428- 'storno': fields.boolean(
3429- 'Storno',
3430- readonly=True,
3431- help=("If this is true, the debit order has been canceled " +
3432- "by the bank or by the customer")),
3433- }
3434-payment_line()
3435-
3436-
3437-class payment_order_create(osv.osv_memory):
3438- _inherit = 'payment.order.create'
3439-
3440- def search_entries(self, cr, uid, ids, context=None):
3441- """
3442- This method taken from account_payment module.
3443- We adapt the domain based on the payment_order_type
3444- """
3445- line_obj = self.pool.get('account.move.line')
3446- mod_obj = self.pool.get('ir.model.data')
3447- if context is None:
3448- context = {}
3449- data = self.read(cr, uid, ids, [], context=context)[0]
3450- search_due_date = data['duedate']
3451-
3452- ### start account_direct_debit ###
3453- payment = self.pool.get('payment.order').browse(
3454- cr, uid, context['active_id'], context=context)
3455- # Search for move line to pay:
3456- if payment.payment_order_type == 'debit':
3457- domain = [
3458- ('reconcile_id', '=', False),
3459- ('account_id.type', '=', 'receivable'),
3460- ('invoice_state', '!=', 'debit_denied'),
3461- ('amount_to_receive', '>', 0),
3462- ]
3463- else:
3464- domain = [
3465- ('reconcile_id', '=', False),
3466- ('account_id.type', '=', 'payable'),
3467- ('amount_to_pay', '>', 0)
3468- ]
3469- domain.append(('company_id', '=', payment.mode.company_id.id))
3470- # apply payment term filter
3471- if payment.mode.payment_term_ids:
3472- domain = domain + [
3473- ('payment_term_id', 'in',
3474- [term.id for term in payment.mode.payment_term_ids]
3475- )
3476- ]
3477- # domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_to_pay', '>', 0)]
3478- ### end account_direct_debit ###
3479-
3480- domain = domain + ['|', ('date_maturity', '<=', search_due_date), ('date_maturity', '=', False)]
3481- line_ids = line_obj.search(cr, uid, domain, context=context)
3482- context.update({'line_ids': line_ids})
3483- model_data_ids = mod_obj.search(cr, uid,[('model', '=', 'ir.ui.view'), ('name', '=', 'view_create_payment_order_lines')], context=context)
3484- resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id']
3485- return {'name': ('Entry Lines'),
3486- 'context': context,
3487- 'view_type': 'form',
3488- 'view_mode': 'form',
3489- 'res_model': 'payment.order.create',
3490- 'views': [(resource_id,'form')],
3491- 'type': 'ir.actions.act_window',
3492- 'target': 'new',
3493- }
3494-payment_order_create()
3495-
3496-
3497-
3498+ return super(payment_order, self).test_undo_done(
3499+ cr, uid, ids, context=context)
3500
3501=== added file 'account_direct_debit/model/payment_line.py'
3502--- account_direct_debit/model/payment_line.py 1970-01-01 00:00:00 +0000
3503+++ account_direct_debit/model/payment_line.py 2013-06-26 21:16:23 +0000
3504@@ -0,0 +1,152 @@
3505+# -*- coding: utf-8 -*-
3506+from openerp.osv import orm, fields
3507+import netsvc
3508+from tools.translate import _
3509+
3510+class payment_line(orm.Model):
3511+ _inherit = 'payment.line'
3512+
3513+ def debit_storno(self, cr, uid, payment_line_id, amount,
3514+ currency, storno_retry=True, context=None):
3515+ """
3516+ The processing of a storno is triggered by a debit
3517+ transfer on one of the company's bank accounts.
3518+ This method offers to re-reconcile the original debit
3519+ payment. For this purpose, we have registered that
3520+ payment move on the payment line.
3521+
3522+ Return the (now incomplete) reconcile id. The caller MUST
3523+ re-reconcile this reconcile with the bank transfer and
3524+ re-open the associated invoice.
3525+
3526+ :param payment_line_id: the single payment line id
3527+ :param amount: the (signed) amount debited from the bank account
3528+ :param currency: the bank account's currency *browse object*
3529+ :param boolean storno_retry: when True, attempt to reopen the invoice, \
3530+ set the invoice to 'Debit denied' otherwise.
3531+ :return: an incomplete reconcile for the caller to fill
3532+ :rtype: database id of an account.move.reconcile resource.
3533+ """
3534+
3535+ move_line_obj = self.pool.get('account.move.line')
3536+ reconcile_obj = self.pool.get('account.move.reconcile')
3537+ line = self.browse(cr, uid, payment_line_id)
3538+ reconcile_id = False
3539+ if (line.transit_move_line_id and not line.storno and
3540+ self.pool.get('res.currency').is_zero(
3541+ cr, uid, currency, (
3542+ (line.transit_move_line_id.credit or 0.0) -
3543+ (line.transit_move_line_id.debit or 0.0) + amount))):
3544+ # Two different cases, full and partial
3545+ # Both cases differ subtly in the procedure to follow
3546+ # Needs refractoring, but why is this not in the OpenERP API?
3547+ # Actually, given the nature of a direct debit order and storno,
3548+ # we should not need to take partial into account on the side of
3549+ # the transit_move_line.
3550+ if line.transit_move_line_id.reconcile_partial_id:
3551+ reconcile_id = line.transit_move_line_id.reconcile_partial_id.id
3552+ attribute = 'reconcile_partial_id'
3553+ if len(line.transit_move_line_id.reconcile_id.line_partial_ids) == 2:
3554+ # reuse the simple reconcile for the storno transfer
3555+ reconcile_obj.write(
3556+ cr, uid, reconcile_id, {
3557+ 'line_id': [(6, 0, line.transit_move_line_id.id)],
3558+ 'line_partial_ids': [(6, 0, [])],
3559+ }, context=context)
3560+ else:
3561+ # split up the original reconcile in a partial one
3562+ # and a new one for reconciling the storno transfer
3563+ reconcile_obj.write(
3564+ cr, uid, reconcile_id, {
3565+ 'line_partial_ids': [(3, line.transit_move_line_id.id)],
3566+ }, context=context)
3567+ reconcile_id = reconcile_obj.create(
3568+ cr, uid, {
3569+ 'type': 'auto',
3570+ 'line_id': [(6, 0, line.transit_move_line_id.id)],
3571+ }, context=context)
3572+ elif line.transit_move_line_id.reconcile_id:
3573+ reconcile_id = line.transit_move_line_id.reconcile_id.id
3574+ if len(line.transit_move_line_id.reconcile_id.line_id) == 2:
3575+ # reuse the simple reconcile for the storno transfer
3576+ reconcile_obj.write(
3577+ cr, uid, reconcile_id, {
3578+ 'line_id': [(6, 0, [line.transit_move_line_id.id])]
3579+ }, context=context)
3580+ else:
3581+ # split up the original reconcile in a partial one
3582+ # and a new one for reconciling the storno transfer
3583+ partial_ids = [
3584+ x.id for x in line.transit_move_line_id.reconcile_id.line_id
3585+ if x.id != line.transit_move_line_id.id
3586+ ]
3587+ reconcile_obj.write(
3588+ cr, uid, reconcile_id, {
3589+ 'line_partial_ids': [(6, 0, partial_ids)],
3590+ 'line_id': [(6, 0, [])],
3591+ }, context=context)
3592+ reconcile_id = reconcile_obj.create(
3593+ cr, uid, {
3594+ 'type': 'auto',
3595+ 'line_id': [(6, 0, line.transit_move_line_id.id)],
3596+ }, context=context)
3597+ # mark the payment line for storno processed
3598+ if reconcile_id:
3599+ self.write(cr, uid, [payment_line_id],
3600+ {'storno': True}, context=context)
3601+ # put forth the invoice workflow
3602+ if line.move_line_id.invoice:
3603+ activity = (storno_retry and 'open_test'
3604+ or 'invoice_debit_denied')
3605+ netsvc.LocalService("workflow").trg_validate(
3606+ uid, 'account.invoice', line.move_line_id.invoice.id,
3607+ activity, cr)
3608+ return reconcile_id
3609+
3610+ def get_storno_account_id(self, cr, uid, payment_line_id, amount,
3611+ currency, context=None):
3612+ """
3613+ Check the match of the arguments, and return the account associated
3614+ with the storno.
3615+ Used in account_banking interactive mode
3616+
3617+ :param payment_line_id: the single payment line id
3618+ :param amount: the (signed) amount debited from the bank account
3619+ :param currency: the bank account's currency *browse object*
3620+ :return: an account if there is a full match, False otherwise
3621+ :rtype: database id of an account.account resource.
3622+ """
3623+
3624+ line = self.browse(cr, uid, payment_line_id)
3625+ account_id = False
3626+ if (line.transit_move_line_id and not line.storno and
3627+ self.pool.get('res.currency').is_zero(
3628+ cr, uid, currency, (
3629+ (line.transit_move_line_id.credit or 0.0) -
3630+ (line.transit_move_line_id.debit or 0.0) + amount))):
3631+ account_id = line.transit_move_line_id.account_id.id
3632+ return account_id
3633+
3634+ def debit_reconcile(self, cr, uid, payment_line_id, context=None):
3635+ """
3636+ Raise if a payment line is passed for which storno is True
3637+ """
3638+ if isinstance(payment_line_id, (list, tuple)):
3639+ payment_line_id = payment_line_id[0]
3640+ payment_line = self.read(
3641+ cr, uid, payment_line_id, ['storno', 'name'], context=context)
3642+ if payment_line['storno']:
3643+ raise orm.except_orm(
3644+ _('Can not reconcile'),
3645+ _('Cancelation of payment line \'%s\' has already been '
3646+ 'processed') % payment_line['name'])
3647+ return super(self, payment_line).debit_reconcile(
3648+ cr, uid, payment_line_id, context=context)
3649+
3650+ _columns = {
3651+ 'storno': fields.boolean(
3652+ 'Storno',
3653+ readonly=True,
3654+ help=("If this is true, the debit order has been canceled "
3655+ "by the bank or by the customer")),
3656+ }
3657
3658=== added file 'account_direct_debit/model/payment_order_create.py'
3659--- account_direct_debit/model/payment_order_create.py 1970-01-01 00:00:00 +0000
3660+++ account_direct_debit/model/payment_order_create.py 2013-06-26 21:16:23 +0000
3661@@ -0,0 +1,41 @@
3662+# -*- coding: utf-8 -*-
3663+##############################################################################
3664+#
3665+# Copyright (C) 2013 Therp BV (<http://therp.nl>).
3666+#
3667+# All other contributions are (C) by their respective contributors
3668+#
3669+# All Rights Reserved
3670+#
3671+# This program is free software: you can redistribute it and/or modify
3672+# it under the terms of the GNU Affero General Public License as
3673+# published by the Free Software Foundation, either version 3 of the
3674+# License, or (at your option) any later version.
3675+#
3676+# This program is distributed in the hope that it will be useful,
3677+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3678+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3679+# GNU Affero General Public License for more details.
3680+#
3681+# You should have received a copy of the GNU Affero General Public License
3682+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3683+#
3684+##############################################################################
3685+
3686+from openerp.osv import orm
3687+
3688+
3689+class payment_order_create(orm.TransientModel):
3690+ _inherit = 'payment.order.create'
3691+
3692+ def extend_payment_order_domain(
3693+ self, cr, uid, payment_order, domain, context=None):
3694+ super(payment_order_create, self).extend_payment_order_domain(
3695+ cr, uid, payment_order, domain, context=context)
3696+ if payment_order.payment_order_type == 'debit':
3697+ domain += [
3698+ ('account_id.type', '=', 'receivable'),
3699+ ('invoice.state', '!=', 'debit_denied'),
3700+ ('amount_to_receive', '>', 0),
3701+ ]
3702+ return True
3703
3704=== modified file 'account_direct_debit/view/account_invoice.xml'
3705--- account_direct_debit/view/account_invoice.xml 2012-05-01 20:36:44 +0000
3706+++ account_direct_debit/view/account_invoice.xml 2013-06-26 21:16:23 +0000
3707@@ -4,7 +4,6 @@
3708 <record id="invoice_form" model="ir.ui.view">
3709 <field name="name">account.invoice.form</field>
3710 <field name="model">account.invoice</field>
3711- <field name="type">form</field>
3712 <field name="inherit_id" ref="account.invoice_form"/>
3713 <field name="arch" type="xml">
3714 <data>
3715@@ -16,14 +15,6 @@
3716 <!-- button name="invoice_open" position="attributes">
3717 <attribute name="states">draft,proforma2,debit_denied</attribute>
3718 </button -->
3719- <button string='Re-Open' position="attributes">
3720- <attribute name="states">paid,debit_denied</attribute>
3721- <!--
3722- unintentional fix of
3723- https://bugs.launchpad.net/openobject-addons/+bug/807543
3724- -->
3725- <attribute name="groups"/>
3726- </button>
3727 <button name="invoice_open" position="after">
3728 <button name="invoice_debit_denied" states="paid"
3729 string="Debit Denied" icon="gtk-cancel"/>
3730@@ -38,7 +29,11 @@
3731 <field name="inherit_id" ref="account.view_account_invoice_filter"/>
3732 <field name="arch" type="xml">
3733 <filter name="invoices" position="after">
3734- <filter name="debit_denied" icon="terp-dolar_ok!" string="Debit denied" domain="[('state','=','debit_denied')]" help="Show only invoices with state Debit denied"/>
3735+ <filter name="debit_denied" icon="terp-dolar_ok!"
3736+ string="Debit denied"
3737+ domain="[('state','=','debit_denied')]"
3738+ help="Show only invoices with state Debit denied"
3739+ />
3740 </filter>
3741 </field>
3742 </record>
3743
3744=== modified file 'account_direct_debit/view/account_payment.xml'
3745--- account_direct_debit/view/account_payment.xml 2013-01-21 11:19:04 +0000
3746+++ account_direct_debit/view/account_payment.xml 2013-06-26 21:16:23 +0000
3747@@ -1,6 +1,7 @@
3748 <?xml version="1.0" encoding="utf-8"?>
3749 <openerp>
3750 <data>
3751+
3752 <!-- distinguish between payment orders and debit orders in the menu -->
3753 <record id="account_payment.action_payment_order_tree" model="ir.actions.act_window">
3754 <field name="domain">[('payment_order_type', '=', 'payment')]</field>
3755@@ -24,7 +25,6 @@
3756 <record id="view_payment_order_form" model="ir.ui.view">
3757 <field name="name">payment.order.form</field>
3758 <field name="model">payment.order</field>
3759- <field name="type">form</field>
3760 <field name="inherit_id" ref="account_payment.view_payment_order_form"/>
3761 <field name="priority" eval="60"/>
3762 <field name="arch" type="xml">
3763@@ -46,40 +46,13 @@
3764 icon="gtk-find"
3765 />
3766 </xpath>
3767- <!-- the attrs do not work like this, apparently
3768- <xpath expr="//tree[@string='Payment Line']" position="inside">
3769- <field name="storno" attrs="{'invisible': [(parent.payment_order_type, '!=', 'debit')]}"/>
3770- </xpath>
3771- -->
3772 </data>
3773 </field>
3774 </record>
3775
3776- <!-- Add transfer account for debit type modes -->
3777- <record model="ir.ui.view" id="view_payment_mode_form">
3778- <field name="name">payment.mode.form add transfer account</field>
3779- <field name="model">payment.mode</field>
3780- <field name="inherit_id" ref="account_banking.view_payment_mode_form_inherit"/>
3781- <field name="type">form</field>
3782- <field name="arch" type="xml">
3783- <field name="type" position="after">
3784- <field name="transfer_account_id"
3785- domain="[('type', '=', 'other'),
3786- ('reconcile', '=', True),
3787- ('company_id', '=', company_id)]"
3788- />
3789- <field name="transfer_journal_id"
3790- domain="[('company_id', '=', company_id)]"
3791- />
3792- <field name="payment_term_ids"/>
3793- </field>
3794- </field>
3795- </record>
3796-
3797 <record id="view_payment_line_tree" model="ir.ui.view">
3798 <field name="name">Payment Lines</field>
3799 <field name="model">payment.line</field>
3800- <field name="type">tree</field>
3801 <field name="inherit_id" ref="account_payment.view_payment_line_tree"/>
3802 <field eval="4" name="priority"/>
3803 <field name="arch" type="xml">
3804@@ -88,5 +61,6 @@
3805 </field>
3806 </field>
3807 </record>
3808+
3809 </data>
3810 </openerp>
3811
3812=== removed file 'account_direct_debit/workflow/account_payment.xml'
3813--- account_direct_debit/workflow/account_payment.xml 2013-01-02 14:45:02 +0000
3814+++ account_direct_debit/workflow/account_payment.xml 1970-01-01 00:00:00 +0000
3815@@ -1,25 +0,0 @@
3816-<?xml version="1.0" encoding="utf-8"?>
3817-<openerp>
3818- <data>
3819- <!--
3820- Transition to undo the payment order and reset to
3821- sent, triggered by
3822- cancelling a bank transaction with which the order
3823- was reconciled.
3824- For this, we need to cancel the flow stop on the done state,
3825- unfortunately.
3826- TODO: what is below is not enough. We need to inject
3827- another state, 'sent_wait' between sent and done.
3828- -->
3829- <record id="account_payment.act_done" model="workflow.activity">
3830- <field name="flow_stop" eval="False"/>
3831- </record>
3832-
3833- <record id="trans_done_sent" model="workflow.transition">
3834- <field name="act_from" ref="account_payment.act_done"/>
3835- <field name="act_to" ref="account_banking.act_sent"/>
3836- <field name="condition">test_undo_done()</field>
3837- <field name="signal">undo_done</field>
3838- </record>
3839- </data>
3840-</openerp>
3841
3842=== modified file 'account_payment_shortcut/__init__.py'
3843--- account_payment_shortcut/__init__.py 2011-12-09 13:07:52 +0000
3844+++ account_payment_shortcut/__init__.py 2013-06-26 21:16:23 +0000
3845@@ -1,1 +1,2 @@
3846+# -*- coding: utf-8 -*-
3847 import payment_order
3848
3849=== modified file 'account_payment_shortcut/payment_order.py'
3850--- account_payment_shortcut/payment_order.py 2011-12-09 13:07:52 +0000
3851+++ account_payment_shortcut/payment_order.py 2013-06-26 21:16:23 +0000
3852@@ -1,8 +1,30 @@
3853 # -*- coding: utf-8 -*-
3854-from osv import osv, fields
3855-
3856-class payment_order_create(osv.osv_memory):
3857-
3858+##############################################################################
3859+#
3860+# Copyright (C) 2011 - 2013 Therp BV (<http://therp.nl>).
3861+#
3862+# All other contributions are (C) by their respective contributors
3863+#
3864+# All Rights Reserved
3865+#
3866+# This program is free software: you can redistribute it and/or modify
3867+# it under the terms of the GNU Affero General Public License as
3868+# published by the Free Software Foundation, either version 3 of the
3869+# License, or (at your option) any later version.
3870+#
3871+# This program is distributed in the hope that it will be useful,
3872+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3873+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3874+# GNU Affero General Public License for more details.
3875+#
3876+# You should have received a copy of the GNU Affero General Public License
3877+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3878+#
3879+##############################################################################
3880+
3881+from osv import orm
3882+
3883+class payment_order_create(orm.TransientModel):
3884 _inherit = 'payment.order.create'
3885
3886 def default_get(self, cr, uid, fields_list, context=None):
3887@@ -27,5 +49,3 @@
3888 res['entries'] = context['line_ids']
3889
3890 return res
3891-
3892-payment_order_create()
3893
3894=== modified file 'bank_statement_instant_voucher/model/account_bank_statement_line.py'
3895--- bank_statement_instant_voucher/model/account_bank_statement_line.py 2012-12-05 20:16:14 +0000
3896+++ bank_statement_instant_voucher/model/account_bank_statement_line.py 2013-06-26 21:16:23 +0000
3897@@ -2,7 +2,7 @@
3898 ##############################################################################
3899 #
3900 # OpenERP, Open Source Management Solution
3901-# This module copyright (C) 2012 Therp BV (<http://therp.nl>).
3902+# This module copyright (C) 2012 - 2013 Therp BV (<http://therp.nl>).
3903 #
3904 # This program is free software: you can redistribute it and/or modify
3905 # it under the terms of the GNU Affero General Public License as
3906@@ -19,10 +19,10 @@
3907 #
3908 ##############################################################################
3909
3910-from openerp.osv import osv, fields
3911-
3912-
3913-class account_bank_statement_line(osv.Model):
3914+from openerp.osv import orm, fields
3915+
3916+
3917+class account_bank_statement_line(orm.Model):
3918 _inherit = 'account.bank.statement.line'
3919 def create_instant_voucher(self, cr, uid, ids, context=None):
3920 res = False
3921
3922=== modified file 'bank_statement_instant_voucher/model/account_voucher_instant.py'
3923--- bank_statement_instant_voucher/model/account_voucher_instant.py 2012-12-05 20:16:14 +0000
3924+++ bank_statement_instant_voucher/model/account_voucher_instant.py 2013-06-26 21:16:23 +0000
3925@@ -2,7 +2,7 @@
3926 ##############################################################################
3927 #
3928 # OpenERP, Open Source Management Solution
3929-# This module copyright (C) 2012 Therp BV (<http://therp.nl>).
3930+# This module copyright (C) 2012 - 2013 Therp BV (<http://therp.nl>).
3931 #
3932 # This program is free software: you can redistribute it and/or modify
3933 # it under the terms of the GNU Affero General Public License as
3934@@ -19,12 +19,12 @@
3935 #
3936 ##############################################################################
3937
3938-from openerp.osv import osv, fields
3939+from openerp.osv import orm, fields
3940 from openerp.tools.translate import _
3941 from openerp.addons.decimal_precision import decimal_precision as dp
3942
3943
3944-class instant_voucher(osv.TransientModel):
3945+class instant_voucher(orm.TransientModel):
3946 _name = 'account.voucher.instant'
3947 _description = 'Instant Voucher'
3948
3949@@ -76,7 +76,7 @@
3950 cr, uid, [('company_id', '=', line.company_id.id),
3951 ('type', '=', voucher_type)])
3952 if not journal_ids:
3953- osv.exept_osv(
3954+ orm.exept_orm(
3955 _('Error'),
3956 _('No %s journal defined') % voucher_type)
3957
3958@@ -156,7 +156,7 @@
3959 context.get('active_id') or
3960 context.get('active_ids') and context.get('active_ids')[0])
3961 if not res['statement_line_id']:
3962- raise osv.except_osv(
3963+ raise orm.except_orm(
3964 _('Error'),
3965 _('Cannot determine statement line'))
3966 line = self.pool.get('account.bank.statement.line').browse(
3967@@ -212,7 +212,7 @@
3968 instant.voucher_id.company_id.currency_id)
3969 if (instant.statement_line_id.statement_id.currency.id !=
3970 voucher_currency.id):
3971- raise osv.except_osv(
3972+ raise orm.except_orm(
3973 _("Error"),
3974 _("Currency on the bank statement line needs to be the "
3975 "same as on the voucher. Currency conversion is not yet "
3976@@ -222,7 +222,7 @@
3977 cr, uid, [instant.voucher_id.id], context=context)
3978 instant.refresh()
3979 if instant.voucher_id.state != 'posted':
3980- raise osv.except_osv(
3981+ raise orm.except_orm(
3982 _("Error"),
3983 _("The voucher could not be posted."))
3984 if instant.voucher_id.move_id.state != 'posted':
3985@@ -230,12 +230,12 @@
3986 cr, uid, [instant.voucher_id.move_id.id], context=context)
3987 instant.refresh()
3988 if instant.voucher_id.move_id.state != 'posted':
3989- raise osv.except_osv(
3990+ raise orm.except_orm(
3991 _("Error"),
3992 _("The voucher's move line could not be posted."))
3993 if not self.pool.get('res.currency').is_zero(
3994 cr, uid, voucher_currency, instant.balance):
3995- raise osv.except_osv(
3996+ raise orm.except_orm(
3997 _("Error"),
3998 _("The amount on the bank statement line needs to be the "
3999 "same as on the voucher. Write-off is not yet "
4000@@ -245,7 +245,7 @@
4001 # and trigger its posting and reconciliation.
4002 if 'import_transaction_id' in statement_line_obj._columns:
4003 if instant.statement_line_id.state == 'confirmed':
4004- raise osv.except_osv(
4005+ raise orm.except_orm(
4006 _("Error"),
4007 _("Cannot match a confirmed statement line"))
4008 if not instant.statement_line_id.import_transaction_id:
4009
4010=== modified file 'bank_statement_instant_voucher/view/account_bank_statement_line.xml'
4011--- bank_statement_instant_voucher/view/account_bank_statement_line.xml 2012-11-28 13:49:36 +0000
4012+++ bank_statement_instant_voucher/view/account_bank_statement_line.xml 2013-06-26 21:16:23 +0000
4013@@ -5,7 +5,6 @@
4014 <field name="name">Add instant voucher button to bank statement line on statement form</field>
4015 <field name="inherit_id" ref="account.view_bank_statement_form" />
4016 <field name="model">account.bank.statement</field>
4017- <field name="type">form</field>
4018 <field name="priority" eval="30"/>
4019 <field name="arch" type="xml">
4020 <xpath expr="/form/notebook/page/field[@name='line_ids']/tree/field[@name='voucher_id']"
4021
4022=== modified file 'bank_statement_instant_voucher/view/account_voucher_instant.xml'
4023--- bank_statement_instant_voucher/view/account_voucher_instant.xml 2012-11-28 13:49:36 +0000
4024+++ bank_statement_instant_voucher/view/account_voucher_instant.xml 2013-06-26 21:16:23 +0000
4025@@ -4,7 +4,6 @@
4026 <record id="instant_voucher_form" model="ir.ui.view">
4027 <field name="name">Instant voucher form view</field>
4028 <field name="model">account.voucher.instant</field>
4029- <field name="type">form</field>
4030 <field name="arch" type="xml">
4031 <form>
4032 <field name="state" invisible="1" readonly="1"/>

Subscribers

People subscribed via source and target branches

to status/vote changes: