Merge lp:~camptocamp/banking-addons/bank-statement-reconcile_vre into lp:banking-addons/bank-statement-reconcile-61

Proposed by Guewen Baconnier @ Camptocamp
Status: Superseded
Proposed branch: lp:~camptocamp/banking-addons/bank-statement-reconcile_vre
Merge into: lp:banking-addons/bank-statement-reconcile-61
Diff against target: 6036 lines (+2363/-1302) (has conflicts)
67 files modified
account_advanced_reconcile/__openerp__.py (+10/-14)
account_advanced_reconcile/advanced_reconciliation.py (+5/-7)
account_advanced_reconcile/base_advanced_reconciliation.py (+22/-24)
account_advanced_reconcile/easy_reconcile.py (+5/-6)
account_advanced_reconcile/easy_reconcile_view.xml (+1/-2)
account_advanced_reconcile/i18n/fr.po (+15/-14)
account_easy_reconcile/__openerp__.py (+16/-10)
account_easy_reconcile/base_reconciliation.py (+24/-23)
account_easy_reconcile/easy_reconcile.py (+78/-41)
account_easy_reconcile/easy_reconcile.xml (+71/-51)
account_easy_reconcile/easy_reconcile_history.py (+7/-0)
account_easy_reconcile/easy_reconcile_history_view.xml (+24/-24)
account_easy_reconcile/i18n/fr.po (+251/-47)
account_easy_reconcile/security/ir_rule.xml (+25/-0)
account_easy_reconcile/simple_reconciliation.py (+3/-5)
account_statement_base_completion/__init__.py (+1/-1)
account_statement_base_completion/__openerp__.py (+5/-2)
account_statement_base_completion/data.xml (+8/-3)
account_statement_base_completion/partner.py (+7/-7)
account_statement_base_completion/statement.py (+348/-209)
account_statement_base_completion/statement_view.xml (+7/-7)
account_statement_base_import/__init__.py (+1/-1)
account_statement_base_import/__openerp__.py (+8/-6)
account_statement_base_import/parser/__init__.py (+1/-1)
account_statement_base_import/parser/file_parser.py (+74/-36)
account_statement_base_import/parser/generic_file_parser.py (+21/-21)
account_statement_base_import/parser/parser.py (+26/-25)
account_statement_base_import/statement.py (+176/-108)
account_statement_base_import/statement_view.xml (+7/-5)
account_statement_base_import/wizard/import_statement.py (+46/-42)
account_statement_completion_voucher/__init__.py (+0/-1)
account_statement_completion_voucher/__openerp__.py (+7/-5)
account_statement_completion_voucher/statement_view.xml (+3/-3)
account_statement_ext/__init__.py (+2/-1)
account_statement_ext/__openerp__.py (+16/-10)
account_statement_ext/account.py (+6/-8)
account_statement_ext/i18n/fr.po (+2/-2)
account_statement_ext/report/__init__.py (+2/-7)
account_statement_ext/report/bank_statement_report.py (+19/-18)
account_statement_ext/security/ir.model.access.csv (+1/-1)
account_statement_ext/security/ir_rule.xml (+10/-0)
account_statement_ext/statement.py (+407/-342)
account_statement_ext/statement_view.xml (+78/-3)
account_statement_ext/voucher.py (+49/-0)
account_statement_ext_voucher/__init__.py (+0/-2)
account_statement_ext_voucher/__openerp__.py (+13/-6)
account_statement_ext_voucher/statement_voucher.py (+4/-3)
account_statement_ext_voucher/statement_voucher_view.xml (+0/-17)
account_statement_transactionid_completion/__openerp__.py (+5/-3)
account_statement_transactionid_completion/statement.py (+40/-36)
account_statement_transactionid_completion/statement_view.xml (+1/-1)
account_statement_transactionid_import/__init__.py (+1/-1)
account_statement_transactionid_import/__openerp__.py (+10/-8)
account_statement_transactionid_import/parser/__init__.py (+1/-3)
account_statement_transactionid_import/parser/transactionid_file_parser.py (+17/-30)
account_statement_transactionid_import/statement.py (+16/-12)
base_transaction_id/__openerp__.py (+19/-8)
base_transaction_id/invoice.py (+8/-7)
base_transaction_id/sale.py (+14/-10)
base_transaction_id/stock.py (+16/-12)
invoicing_voucher_killer/__init__.py (+20/-0)
invoicing_voucher_killer/__openerp__.py (+39/-0)
invoicing_voucher_killer/invoice_data.xml (+7/-0)
invoicing_voucher_killer/invoice_view.xml (+48/-0)
statement_voucher_killer/__init__.py (+21/-0)
statement_voucher_killer/__openerp__.py (+40/-0)
statement_voucher_killer/voucher.py (+128/-0)
Text conflict in account_easy_reconcile/__openerp__.py
Text conflict in account_easy_reconcile/easy_reconcile.py
Text conflict in account_statement_ext/__openerp__.py
Text conflict in account_statement_ext/statement.py
Text conflict in account_statement_ext/statement_view.xml
To merge this branch: bzr merge lp:~camptocamp/banking-addons/bank-statement-reconcile_vre
Reviewer Review Type Date Requested Status
Banking Addons Core Editors Pending
Review via email: mp+168363@code.launchpad.net

This proposal supersedes a proposal from 2013-06-10.

This proposal has been superseded by a proposal from 2013-06-10.

Description of the change

This fix prevent account_advanced_reconcile to crash if ref in the move is not set

To post a comment you must log in.

Unmerged revisions

93. By Vincent Renaville@camptocamp

[FIX] prevent crash if move ref is null

92. By Nicolas Bessi - Camptocamp

[MRG] [FIX] Restore default period in bank statement by taking context in account

91. By Nicolas Bessi - Camptocamp

[MRG] [IMP] Refine partner label lookup error message
[FIX] Restore error message in log
[FIX] Fix partner label lookup regexp

90. By Nicolas Bessi - Camptocamp

[MRG] Major refactoring of statement import and completion:

Fix the way to look default account on partner:
- If master account is provided in profile it will be forced
 - If the customer checkbox is checked on the found partner, type and account will be customer and receivable
 - If the supplier checkbox is checked on the found partner, type and account will be supplier and payable
 - If both checkbox are checked or none of them, it'll be based on the amount :
    If amount is positif the type and account will be customer and receivable,
    If amount is negativ, the type and account will be supplier and payable

Completion:
-Fix and refactor the the invoices lookup for completion
-Various fixes in completion rules
- Non matches lines are not mark as completed.

Optimisation:

Refactoring of statement import:
We by pass ORM to increase performances.
TODO support of sparse field

Refactoring of completion.
We have done some structural changes in order to avoid a lot of un needed call to ORM.
Bypass orm when writing to database.

These merge is required in order to fix transaction id completion rules and import +
Handle the new semantic change in OpenERP partner model

89. By Alexandre Fayolle - camptocamp

[FIX] permissions on account.statement.profil: an accountant needs write access on the model to be able to import a bank statement

  otherwise, the message.post call fails

88. By Nicolas Bessi - Camptocamp

[MRG] Fixes performance trouble when using bank_statement_label based completion rules by using memoizer pattern.

Add lines in context to be able to acces them in completion rules. It is not
mandatory as we can do line.satement_id.line_ids but it is more efficient.

Some minor cleanup

87. By Nicolas Bessi - Camptocamp

[MRG] Improve statement import wizard by using mass writing of statement line in order to avoid store field loop computation.

86. By Nicolas Bessi - Camptocamp

[MRG] Improve statement import global usability by retuning usable error message while parsing files.
Allows empty value for float in parsed CSV.

Added note about the additional dependency on python-xlrd in the description.

85. By Nicolas Bessi - Camptocamp

[MRG] Add a completion rule to allows supplier invoice completion baser on invoice number

84. By Nicolas Bessi - Camptocamp

[MRG] Adds two add-ons that prevent voucher to interfer with the statement ext related add-ons flow

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'account_advanced_reconcile/__openerp__.py'
--- account_advanced_reconcile/__openerp__.py 2012-11-28 13:22:22 +0000
+++ account_advanced_reconcile/__openerp__.py 2013-06-10 07:05:32 +0000
@@ -30,8 +30,6 @@
30 'description': """30 'description': """
31Advanced reconciliation methods for the module account_easy_reconcile.31Advanced reconciliation methods for the module account_easy_reconcile.
3232
33account_easy_reconcile, which is a dependency, is available in the branch: lp:account-extra-addons
34
35In addition to the features implemented in account_easy_reconcile, which are:33In addition to the features implemented in account_easy_reconcile, which are:
36 - reconciliation facilities for big volume of transactions34 - reconciliation facilities for big volume of transactions
37 - setup different profiles of reconciliation by account35 - setup different profiles of reconciliation by account
@@ -39,31 +37,31 @@
39 - this module is also a base to create others reconciliation methods37 - this module is also a base to create others reconciliation methods
40 which can plug in the profiles38 which can plug in the profiles
41 - a profile a reconciliation can be run manually or by a cron39 - a profile a reconciliation can be run manually or by a cron
42 - monitoring of reconcilation runs with a few logs40 - monitoring of reconcilation runs with an history
4341
44It implements a basis to created advanced reconciliation methods in a few lines42It implements a basis to created advanced reconciliation methods in a few lines
45of code.43of code.
4644
47Typically, such a method can be:45Typically, such a method can be:
48 - Reconcile entries if the partner and the ref are equal46 - Reconcile Journal items if the partner and the ref are equal
49 - Reconcile entries if the partner is equal and the ref is the same than ref47 - Reconcile Journal items if the partner is equal and the ref
50 or name48 is the same than ref or name
51 - Reconcile entries if the partner is equal and the ref match with a pattern49 - Reconcile Journal items if the partner is equal and the ref
50 match with a pattern
5251
53And they allows:52And they allows:
54 - Reconciliations with multiple credit / multiple debit lines53 - Reconciliations with multiple credit / multiple debit lines
55 - Partial reconciliations54 - Partial reconciliations
56 - Write-off amount as well55 - Write-off amount as well
5756
58A method is already implemented in this module, it matches on entries:57A method is already implemented in this module, it matches on items:
59 * Partner58 - Partner
60 * Ref on credit move lines should be case insensitive equals to the ref or59 - Ref on credit move lines should be case insensitive equals to the ref or
61 the name of the debit move line60 the name of the debit move line
6261
63The base class to find the reconciliations is built to be as efficient as62The base class to find the reconciliations is built to be as efficient as
64possible.63possible.
6564
66
67So basically, if you have an invoice with 3 payments (one per month), the first65So basically, if you have an invoice with 3 payments (one per month), the first
68month, it will partial reconcile the debit move line with the first payment, the second66month, it will partial reconcile the debit move line with the first payment, the second
69month, it will partial reconcile the debit move line with 2 first payments,67month, it will partial reconcile the debit move line with 2 first payments,
@@ -75,9 +73,7 @@
7573
76 """,74 """,
77 'website': 'http://www.camptocamp.com',75 'website': 'http://www.camptocamp.com',
78 'init_xml': [],76 'data': ['easy_reconcile_view.xml'],
79 'update_xml': ['easy_reconcile_view.xml'],
80 'demo_xml': [],
81 'test': [],77 'test': [],
82 'images': [],78 'images': [],
83 'installable': True,79 'installable': True,
8480
=== modified file 'account_advanced_reconcile/advanced_reconciliation.py'
--- account_advanced_reconcile/advanced_reconciliation.py 2012-08-06 10:06:26 +0000
+++ account_advanced_reconcile/advanced_reconciliation.py 2013-06-10 07:05:32 +0000
@@ -19,14 +19,13 @@
19#19#
20##############################################################################20##############################################################################
2121
22from openerp.osv.orm import TransientModel22from openerp.osv import orm
2323
2424
25class easy_reconcile_advanced_ref(TransientModel):25class easy_reconcile_advanced_ref(orm.TransientModel):
2626
27 _name = 'easy.reconcile.advanced.ref'27 _name = 'easy.reconcile.advanced.ref'
28 _inherit = 'easy.reconcile.advanced'28 _inherit = 'easy.reconcile.advanced'
29 _auto = True # False when inherited from AbstractModel
3029
31 def _skip_line(self, cr, uid, rec, move_line, context=None):30 def _skip_line(self, cr, uid, rec, move_line, context=None):
32 """31 """
@@ -115,6 +114,5 @@
115 :yield: matchers as tuple ('matcher key', value(s))114 :yield: matchers as tuple ('matcher key', value(s))
116 """115 """
117 yield ('partner_id', move_line['partner_id'])116 yield ('partner_id', move_line['partner_id'])
118 yield ('ref', (move_line['ref'].lower().strip(),117 yield ('ref', ((move_line['ref'] or '').lower().strip(),
119 move_line['name'].lower().strip()))118 move_line['name'].lower().strip()))
120
121119
=== modified file 'account_advanced_reconcile/base_advanced_reconciliation.py'
--- account_advanced_reconcile/base_advanced_reconciliation.py 2012-08-06 10:06:26 +0000
+++ account_advanced_reconcile/base_advanced_reconciliation.py 2013-06-10 07:05:32 +0000
@@ -19,21 +19,17 @@
19#19#
20##############################################################################20##############################################################################
2121
22from itertools import groupby, product22from itertools import product
23from operator import itemgetter23from openerp.osv import orm
24from openerp.osv.orm import Model, AbstractModel, TransientModel24
25from openerp.osv import fields25
2626class easy_reconcile_advanced(orm.AbstractModel):
27
28class easy_reconcile_advanced(AbstractModel):
2927
30 _name = 'easy.reconcile.advanced'28 _name = 'easy.reconcile.advanced'
31 _inherit = 'easy.reconcile.base'29 _inherit = 'easy.reconcile.base'
3230
33 def _query_debit(self, cr, uid, rec, context=None):31 def _query_debit(self, cr, uid, rec, context=None):
34 """Select all move (debit>0) as candidate. Optional choice on invoice32 """Select all move (debit>0) as candidate. """
35 will filter with an inner join on the related moves.
36 """
37 select = self._select(rec)33 select = self._select(rec)
38 sql_from = self._from(rec)34 sql_from = self._from(rec)
39 where, params = self._where(rec)35 where, params = self._where(rec)
@@ -47,9 +43,7 @@
47 return cr.dictfetchall()43 return cr.dictfetchall()
4844
49 def _query_credit(self, cr, uid, rec, context=None):45 def _query_credit(self, cr, uid, rec, context=None):
50 """Select all move (credit>0) as candidate. Optional choice on invoice46 """Select all move (credit>0) as candidate. """
51 will filter with an inner join on the related moves.
52 """
53 select = self._select(rec)47 select = self._select(rec)
54 sql_from = self._from(rec)48 sql_from = self._from(rec)
55 where, params = self._where(rec)49 where, params = self._where(rec)
@@ -176,9 +170,9 @@
176 """170 """
177 mkey, mvalue = matcher171 mkey, mvalue = matcher
178 omkey, omvalue = opposite_matcher172 omkey, omvalue = opposite_matcher
179 assert mkey == omkey, "A matcher %s is compared with a matcher %s, " \173 assert mkey == omkey, ("A matcher %s is compared with a matcher %s, "
180 " the _matchers and _opposite_matchers are probably wrong" % \174 " the _matchers and _opposite_matchers are probably wrong" %
181 (mkey, omkey)175 (mkey, omkey))
182 if not isinstance(mvalue, (list, tuple)):176 if not isinstance(mvalue, (list, tuple)):
183 mvalue = mvalue,177 mvalue = mvalue,
184 if not isinstance(omvalue, (list, tuple)):178 if not isinstance(omvalue, (list, tuple)):
@@ -186,7 +180,13 @@
186 return easy_reconcile_advanced._compare_matcher_values(mkey, mvalue, omvalue)180 return easy_reconcile_advanced._compare_matcher_values(mkey, mvalue, omvalue)
187181
188 def _compare_opposite(self, cr, uid, rec, move_line, opposite_move_line,182 def _compare_opposite(self, cr, uid, rec, move_line, opposite_move_line,
189 matchers, context=None):183 matchers, context=None):
184 """ Iterate over the matchers of the move lines vs opposite move lines
185 and if they all match, return True.
186
187 If all the matchers match for a move line and an opposite move line,
188 they are candidate for a reconciliation.
189 """
190 opp_matchers = self._opposite_matchers(cr, uid, rec, opposite_move_line,190 opp_matchers = self._opposite_matchers(cr, uid, rec, opposite_move_line,
191 context=context)191 context=context)
192 for matcher in matchers:192 for matcher in matchers:
@@ -216,14 +216,15 @@
216 :return: list of matching lines216 :return: list of matching lines
217 """217 """
218 matchers = self._matchers(cr, uid, rec, move_line, context=context)218 matchers = self._matchers(cr, uid, rec, move_line, context=context)
219 return [op for op in opposite_move_lines if \219 return [op for op in opposite_move_lines if
220 self._compare_opposite(cr, uid, rec, move_line, op, matchers, context=context)]220 self._compare_opposite(
221 cr, uid, rec, move_line, op, matchers, context=context)]
221222
222 def _action_rec(self, cr, uid, rec, context=None):223 def _action_rec(self, cr, uid, rec, context=None):
223 credit_lines = self._query_credit(cr, uid, rec, context=context)224 credit_lines = self._query_credit(cr, uid, rec, context=context)
224 debit_lines = self._query_debit(cr, uid, rec, context=context)225 debit_lines = self._query_debit(cr, uid, rec, context=context)
225 return self._rec_auto_lines_advanced(226 return self._rec_auto_lines_advanced(
226 cr, uid, rec, credit_lines, debit_lines, context=context)227 cr, uid, rec, credit_lines, debit_lines, context=context)
227228
228 def _skip_line(self, cr, uid, rec, move_line, context=None):229 def _skip_line(self, cr, uid, rec, move_line, context=None):
229 """230 """
@@ -234,9 +235,7 @@
234 return False235 return False
235236
236 def _rec_auto_lines_advanced(self, cr, uid, rec, credit_lines, debit_lines, context=None):237 def _rec_auto_lines_advanced(self, cr, uid, rec, credit_lines, debit_lines, context=None):
237 if context is None:238 """ Advanced reconciliation main loop """
238 context = {}
239
240 reconciled_ids = []239 reconciled_ids = []
241 partial_reconciled_ids = []240 partial_reconciled_ids = []
242 reconcile_groups = []241 reconcile_groups = []
@@ -271,4 +270,3 @@
271 partial_reconciled_ids += reconcile_group_ids270 partial_reconciled_ids += reconcile_group_ids
272271
273 return reconciled_ids, partial_reconciled_ids272 return reconciled_ids, partial_reconciled_ids
274
275273
=== modified file 'account_advanced_reconcile/easy_reconcile.py'
--- account_advanced_reconcile/easy_reconcile.py 2012-06-20 14:10:01 +0000
+++ account_advanced_reconcile/easy_reconcile.py 2013-06-10 07:05:32 +0000
@@ -19,10 +19,10 @@
19#19#
20##############################################################################20##############################################################################
2121
22from openerp.osv.orm import Model22from openerp.osv import orm
2323
2424
25class account_easy_reconcile_method(Model):25class account_easy_reconcile_method(orm.Model):
2626
27 _inherit = 'account.easy.reconcile.method'27 _inherit = 'account.easy.reconcile.method'
2828
@@ -31,7 +31,6 @@
31 _get_all_rec_method(cr, uid, context=context)31 _get_all_rec_method(cr, uid, context=context)
32 methods += [32 methods += [
33 ('easy.reconcile.advanced.ref',33 ('easy.reconcile.advanced.ref',
34 'Advanced. Partner and Ref.'),34 'Advanced. Partner and Ref.'),
35 ]35 ]
36 return methods36 return methods
37
3837
=== modified file 'account_advanced_reconcile/easy_reconcile_view.xml'
--- account_advanced_reconcile/easy_reconcile_view.xml 2012-06-27 07:58:32 +0000
+++ account_advanced_reconcile/easy_reconcile_view.xml 2013-06-10 07:05:32 +0000
@@ -4,13 +4,12 @@
4 <record id="view_easy_reconcile_form" model="ir.ui.view">4 <record id="view_easy_reconcile_form" model="ir.ui.view">
5 <field name="name">account.easy.reconcile.form</field>5 <field name="name">account.easy.reconcile.form</field>
6 <field name="model">account.easy.reconcile</field>6 <field name="model">account.easy.reconcile</field>
7 <field name="type">form</field>
8 <field name="inherit_id" ref="account_easy_reconcile.account_easy_reconcile_form"/>7 <field name="inherit_id" ref="account_easy_reconcile.account_easy_reconcile_form"/>
9 <field name="arch" type="xml">8 <field name="arch" type="xml">
10 <page name="information" position="inside">9 <page name="information" position="inside">
11 <group colspan="2" col="2">10 <group colspan="2" col="2">
12 <separator colspan="4" string="Advanced. Partner and Ref"/>11 <separator colspan="4" string="Advanced. Partner and Ref"/>
13 <label string="Match multiple debit vs multiple credit entries. Allow partial reconcilation.12 <label string="Match multiple debit vs multiple credit entries. Allow partial reconciliation.
14The lines should have the partner, the credit entry ref. is matched vs the debit entry ref. or name." colspan="4"/>13The lines should have the partner, the credit entry ref. is matched vs the debit entry ref. or name." colspan="4"/>
15 </group>14 </group>
16 </page>15 </page>
1716
=== modified file 'account_advanced_reconcile/i18n/fr.po'
--- account_advanced_reconcile/i18n/fr.po 2012-12-13 13:57:29 +0000
+++ account_advanced_reconcile/i18n/fr.po 2013-06-10 07:05:32 +0000
@@ -1,18 +1,19 @@
1# Translation of OpenERP Server.1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:2# This file contains the translation of the following modules:
3# * account_advanced_reconcile3# * account_advanced_reconcile
4#4#
5msgid ""5msgid ""
6msgstr ""6msgstr ""
7"Project-Id-Version: OpenERP Server 6.1\n"7"Project-Id-Version: OpenERP Server 6.1\n"
8"Report-Msgid-Bugs-To: \n"8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2012-11-07 12:34+0000\n"9"POT-Creation-Date: 2013-01-04 08:25+0000\n"
10"PO-Revision-Date: 2012-11-07 12:34+0000\n"10"PO-Revision-Date: 2013-01-04 09:27+0100\n"
11"Last-Translator: <>\n"11"Last-Translator: Guewen Baconnier <guewen.baconnier@camptocamp.com>\n"
12"Language-Team: \n"12"Language-Team: \n"
13"Language: \n"
13"MIME-Version: 1.0\n"14"MIME-Version: 1.0\n"
14"Content-Type: text/plain; charset=UTF-8\n"15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Transfer-Encoding: \n"16"Content-Transfer-Encoding: 8bit\n"
16"Plural-Forms: \n"17"Plural-Forms: \n"
1718
18#. module: account_advanced_reconcile19#. module: account_advanced_reconcile
@@ -33,12 +34,6 @@
33msgstr "Méthode de lettrage pour le module account_easy_reconcile"34msgstr "Méthode de lettrage pour le module account_easy_reconcile"
3435
35#. module: account_advanced_reconcile36#. module: account_advanced_reconcile
36#: field:easy.reconcile.advanced,date_base_on:0
37#: field:easy.reconcile.advanced.ref,date_base_on:0
38msgid "Date of reconcilation"
39msgstr "Date de lettrage"
40
41#. module: account_advanced_reconcile
42#: field:easy.reconcile.advanced,journal_id:037#: field:easy.reconcile.advanced,journal_id:0
43#: field:easy.reconcile.advanced.ref,journal_id:038#: field:easy.reconcile.advanced.ref,journal_id:0
44msgid "Journal"39msgid "Journal"
@@ -51,6 +46,11 @@
51msgstr "Compte de produit"46msgstr "Compte de produit"
5247
53#. module: account_advanced_reconcile48#. module: account_advanced_reconcile
49#: view:account.easy.reconcile:0
50msgid "Match multiple debit vs multiple credit entries. Allow partial reconciliation. The lines should have the partner, the credit entry ref. is matched vs the debit entry ref. or name."
51msgstr "Le Lettrage peut s'effectuer sur plusieurs écritures de débit et crédit. Le Lettrage partiel est autorisé. Les écritures doivent avoir le même partenaire et la référence sur les écritures de crédit doit se retrouver dans la référence ou la description sur les écritures de débit."
52
53#. module: account_advanced_reconcile
54#: field:easy.reconcile.advanced,filter:054#: field:easy.reconcile.advanced,filter:0
55#: field:easy.reconcile.advanced.ref,filter:055#: field:easy.reconcile.advanced.ref,filter:0
56msgid "Filter"56msgid "Filter"
@@ -62,9 +62,10 @@
62msgstr "Avancé. Partenaire et Réf."62msgstr "Avancé. Partenaire et Réf."
6363
64#. module: account_advanced_reconcile64#. module: account_advanced_reconcile
65#: view:account.easy.reconcile:065#: field:easy.reconcile.advanced,date_base_on:0
66msgid "Match multiple debit vs multiple credit entries. Allow partial reconcilation. The lines should have the partner, the credit entry ref. is matched vs the debit entry ref. or name."66#: field:easy.reconcile.advanced.ref,date_base_on:0
67msgstr "Le Lettrage peut s'effectuer sur plusieurs écritures de débit et crédit. Le Lettrage partiel est autorisé. Les écritures doivent avoir le même partenaire et la référence sur les écritures de crédit doit se retrouver dans la référence ou la description sur les écritures de débit."67msgid "Date of reconciliation"
68msgstr "Date de lettrage"
6869
69#. module: account_advanced_reconcile70#. module: account_advanced_reconcile
70#: model:ir.model,name:account_advanced_reconcile.model_easy_reconcile_advanced71#: model:ir.model,name:account_advanced_reconcile.model_easy_reconcile_advanced
7172
=== modified file 'account_easy_reconcile/__openerp__.py'
--- account_easy_reconcile/__openerp__.py 2013-03-12 12:11:33 +0000
+++ account_easy_reconcile/__openerp__.py 2013-06-10 07:05:32 +0000
@@ -20,12 +20,19 @@
20##############################################################################20##############################################################################
2121
22{22{
23<<<<<<< TREE
23 "name" : "Easy Reconcile",24 "name" : "Easy Reconcile",
24 "version" : "1.1",25 "version" : "1.1",
25 "depends" : ["account",26 "depends" : ["account",
26 "base_scheduler_creator"27 "base_scheduler_creator"
27 ],28 ],
28 "author" : "Akretion,Camptocamp",29 "author" : "Akretion,Camptocamp",
30=======
31 "name": "Easy Reconcile",
32 "version": "1.3.0",
33 "depends": ["account"],
34 "author": "Akretion,Camptocamp",
35>>>>>>> MERGE-SOURCE
29 "description": """36 "description": """
30Easy Reconcile37Easy Reconcile
31==============38==============
@@ -44,13 +51,13 @@
44 reconciliation methods which can plug in the profiles51 reconciliation methods which can plug in the profiles
45 - a profile a reconciliation can be run manually or by a cron52 - a profile a reconciliation can be run manually or by a cron
46 - monitoring of reconciliation runs with an history53 - monitoring of reconciliation runs with an history
47 which keep track of the reconciled entries54 which keep track of the reconciled Journal items
4855
492 simple reconciliation methods are integrated562 simple reconciliation methods are integrated
50in this module, the simple reconciliations works57in this module, the simple reconciliations works
51on 2 lines (1 debit / 1 credit) and do not allow58on 2 lines (1 debit / 1 credit) and do not allow
52partial reconcilation, they also match on 1 key,59partial reconcilation, they also match on 1 key,
53partner or entry name.60partner or Journal item name.
5461
55You may be interested to install also the62You may be interested to install also the
56``account_advanced_reconciliation`` module.63``account_advanced_reconciliation`` module.
@@ -58,14 +65,13 @@
58allows multiple lines and partial.65allows multiple lines and partial.
5966
60""",67""",
61 "website" : "http://www.akretion.com/",68 "website": "http://www.akretion.com/",
62 "category" : "Finance",69 "category": "Finance",
63 "init_xml" : [],70 "demo_xml": [],
64 "demo_xml" : [],71 "data": ["easy_reconcile.xml",
65 "update_xml" : [72 "easy_reconcile_history_view.xml",
66 "easy_reconcile.xml",73 "security/ir_rule.xml",
67 "easy_reconcile_history_view.xml",74 "security/ir.model.access.csv"],
68 ],
69 'license': 'AGPL-3',75 'license': 'AGPL-3',
70 "auto_install": False,76 "auto_install": False,
71 "installable": True,77 "installable": True,
7278
=== modified file 'account_easy_reconcile/base_reconciliation.py'
--- account_easy_reconcile/base_reconciliation.py 2012-11-01 16:12:50 +0000
+++ account_easy_reconcile/base_reconciliation.py 2013-06-10 07:05:32 +0000
@@ -1,7 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright 2012 Camptocamp SA (Guewen Baconnier)4# Copyright 2012-2013 Camptocamp SA (Guewen Baconnier)
5# Copyright (C) 2010 Sébastien Beau5# Copyright (C) 2010 Sébastien Beau
6#6#
7# This program is free software: you can redistribute it and/or modify7# This program is free software: you can redistribute it and/or modify
@@ -19,29 +19,29 @@
19#19#
20##############################################################################20##############################################################################
2121
22from openerp.osv.orm import AbstractModel22from openerp.osv import fields, orm
23from openerp.osv import fields
24from operator import itemgetter, attrgetter23from operator import itemgetter, attrgetter
2524
2625
27class easy_reconcile_base(AbstractModel):26class easy_reconcile_base(orm.AbstractModel):
28 """Abstract Model for reconciliation methods"""27 """Abstract Model for reconciliation methods"""
2928
30 _name = 'easy.reconcile.base'29 _name = 'easy.reconcile.base'
3130
32 _inherit = 'easy.reconcile.options'31 _inherit = 'easy.reconcile.options'
33 _auto = True # restore property set to False by AbstractModel
3432
35 _columns = {33 _columns = {
36 'account_id': fields.many2one('account.account', 'Account', required=True),34 'account_id': fields.many2one(
37 'partner_ids': fields.many2many('res.partner',35 'account.account', 'Account', required=True),
38 string="Restrict on partners"),36 'partner_ids': fields.many2many(
37 'res.partner', string="Restrict on partners"),
39 # other columns are inherited from easy.reconcile.options38 # other columns are inherited from easy.reconcile.options
40 }39 }
4140
42 def automatic_reconcile(self, cr, uid, ids, context=None):41 def automatic_reconcile(self, cr, uid, ids, context=None):
43 """42 """ Reconciliation method called from the view.
44 :return: list of reconciled ids, list of partially reconciled entries43
44 :return: list of reconciled ids, list of partially reconciled items
45 """45 """
46 if isinstance(ids, (int, long)):46 if isinstance(ids, (int, long)):
47 ids = [ids]47 ids = [ids]
@@ -50,14 +50,15 @@
50 return self._action_rec(cr, uid, rec, context=context)50 return self._action_rec(cr, uid, rec, context=context)
5151
52 def _action_rec(self, cr, uid, rec, context=None):52 def _action_rec(self, cr, uid, rec, context=None):
53 """Must be inherited to implement the reconciliation53 """ Must be inherited to implement the reconciliation
54
54 :return: list of reconciled ids55 :return: list of reconciled ids
55 """56 """
56 raise NotImplementedError57 raise NotImplementedError
5758
58 def _base_columns(self, rec):59 def _base_columns(self, rec):
59 """Mandatory columns for move lines queries60 """ Mandatory columns for move lines queries
60 An extra column aliased as `key` should be defined61 An extra column aliased as ``key`` should be defined
61 in each query."""62 in each query."""
62 aml_cols = (63 aml_cols = (
63 'id',64 'id',
@@ -104,7 +105,7 @@
104 return where, params105 return where, params
105106
106 def _below_writeoff_limit(self, cr, uid, rec, lines,107 def _below_writeoff_limit(self, cr, uid, rec, lines,
107 writeoff_limit, context=None):108 writeoff_limit, context=None):
108 precision = self.pool.get('decimal.precision').precision_get(109 precision = self.pool.get('decimal.precision').precision_get(
109 cr, uid, 'Account')110 cr, uid, 'Account')
110 keys = ('debit', 'credit')111 keys = ('debit', 'credit')
@@ -119,7 +120,8 @@
119 writeoff_amount = round(debit - credit, precision)120 writeoff_amount = round(debit - credit, precision)
120 return bool(writeoff_limit >= abs(writeoff_amount)), debit, credit121 return bool(writeoff_limit >= abs(writeoff_amount)), debit, credit
121122
122 def _get_rec_date(self, cr, uid, rec, lines, based_on='end_period_last_credit', context=None):123 def _get_rec_date(self, cr, uid, rec, lines,
124 based_on='end_period_last_credit', context=None):
123 period_obj = self.pool.get('account.period')125 period_obj = self.pool.get('account.period')
124126
125 def last_period(mlines):127 def last_period(mlines):
@@ -155,12 +157,14 @@
155 """ Try to reconcile given lines157 """ Try to reconcile given lines
156158
157 :param list lines: list of dict of move lines, they must at least159 :param list lines: list of dict of move lines, they must at least
158 contain values for : id, debit, credit160 contain values for : id, debit, credit
159 :param boolean allow_partial: if True, partial reconciliation will be161 :param boolean allow_partial: if True, partial reconciliation will be
160 created, otherwise only Full reconciliation will be created162 created, otherwise only Full
161 :return: tuple of boolean values, first item is wether the the entries163 reconciliation will be created
162 have been reconciled or not, the second is wether the reconciliation164 :return: tuple of boolean values, first item is wether the items
163 is full (True) or partial (False)165 have been reconciled or not,
166 the second is wether the reconciliation is full (True)
167 or partial (False)
164 """168 """
165 if context is None:169 if context is None:
166 context = {}170 context = {}
@@ -168,8 +172,6 @@
168 ml_obj = self.pool.get('account.move.line')172 ml_obj = self.pool.get('account.move.line')
169 writeoff = rec.write_off173 writeoff = rec.write_off
170174
171 keys = ('debit', 'credit')
172
173 line_ids = [l['id'] for l in lines]175 line_ids = [l['id'] for l in lines]
174 below_writeoff, sum_debit, sum_credit = self._below_writeoff_limit(176 below_writeoff, sum_debit, sum_credit = self._below_writeoff_limit(
175 cr, uid, rec, lines, writeoff, context=context)177 cr, uid, rec, lines, writeoff, context=context)
@@ -204,4 +206,3 @@
204 return True, False206 return True, False
205207
206 return False, False208 return False, False
207
208209
=== modified file 'account_easy_reconcile/easy_reconcile.py'
--- account_easy_reconcile/easy_reconcile.py 2013-01-03 15:27:55 +0000
+++ account_easy_reconcile/easy_reconcile.py 2013-06-10 07:05:32 +0000
@@ -1,7 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright 2012 Camptocamp SA (Guewen Baconnier)4# Copyright 2012-2013 Camptocamp SA (Guewen Baconnier)
5# Copyright (C) 2010 Sébastien Beau5# Copyright (C) 2010 Sébastien Beau
6#6#
7# This program is free software: you can redistribute it and/or modify7# This program is free software: you can redistribute it and/or modify
@@ -19,19 +19,18 @@
19#19#
20##############################################################################20##############################################################################
2121
22import time22from openerp.osv import fields, osv, orm
23from openerp.osv.orm import Model, AbstractModel
24from openerp.osv import fields
25from openerp.tools.translate import _23from openerp.tools.translate import _
26from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT24from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
2725
2826
29class easy_reconcile_options(AbstractModel):27class easy_reconcile_options(orm.AbstractModel):
30 """Options of a reconciliation profile, columns28 """Options of a reconciliation profile
31 shared by the configuration of methods and by the29
32 reconciliation wizards. This allows decoupling30 Columns shared by the configuration of methods
33 of the methods with the wizards and allows to31 and by the reconciliation wizards.
34 launch the wizards alone32 This allows decoupling of the methods and the
33 wizards and allows to launch the wizards alone
35 """34 """
3635
37 _name = 'easy.reconcile.options'36 _name = 'easy.reconcile.options'
@@ -46,12 +45,16 @@
4645
47 _columns = {46 _columns = {
48 'write_off': fields.float('Write off allowed'),47 'write_off': fields.float('Write off allowed'),
49 'account_lost_id': fields.many2one('account.account', 'Account Lost'),48 'account_lost_id': fields.many2one(
50 'account_profit_id': fields.many2one('account.account', 'Account Profit'),49 'account.account', 'Account Lost'),
51 'journal_id': fields.many2one('account.journal', 'Journal'),50 'account_profit_id': fields.many2one(
52 'date_base_on': fields.selection(_get_rec_base_date,51 'account.account', 'Account Profit'),
52 'journal_id': fields.many2one(
53 'account.journal', 'Journal'),
54 'date_base_on': fields.selection(
55 _get_rec_base_date,
53 required=True,56 required=True,
54 string='Date of reconcilation'),57 string='Date of reconciliation'),
55 'filter': fields.char('Filter', size=128),58 'filter': fields.char('Filter', size=128),
56 }59 }
5760
@@ -61,13 +64,12 @@
61 }64 }
6265
6366
64class account_easy_reconcile_method(Model):67class account_easy_reconcile_method(orm.Model):
6568
66 _name = 'account.easy.reconcile.method'69 _name = 'account.easy.reconcile.method'
67 _description = 'reconcile method for account_easy_reconcile'70 _description = 'reconcile method for account_easy_reconcile'
6871
69 _inherit = 'easy.reconcile.options'72 _inherit = 'easy.reconcile.options'
70 _auto = True # restore property set to False by AbstractModel
7173
72 _order = 'sequence'74 _order = 'sequence'
7375
@@ -82,11 +84,24 @@
82 return self._get_all_rec_method(cr, uid, context=None)84 return self._get_all_rec_method(cr, uid, context=None)
8385
84 _columns = {86 _columns = {
85 'name': fields.selection(_get_rec_method, 'Type', size=128, required=True),87 'name': fields.selection(
86 'sequence': fields.integer('Sequence', required=True,88 _get_rec_method, 'Type', required=True),
87 help="The sequence field is used to order the reconcile method"),89 'sequence': fields.integer(
88 'task_id': fields.many2one('account.easy.reconcile', 'Task',90 'Sequence',
89 required=True, ondelete='cascade'),91 required=True,
92 help="The sequence field is used to order "
93 "the reconcile method"),
94 'task_id': fields.many2one(
95 'account.easy.reconcile',
96 string='Task',
97 required=True,
98 ondelete='cascade'),
99 'company_id': fields.related('task_id','company_id',
100 relation='res.company',
101 type='many2one',
102 string='Company',
103 store=True,
104 readonly=True),
90 }105 }
91106
92 _defaults = {107 _defaults = {
@@ -94,8 +109,11 @@
94 }109 }
95110
96 def init(self, cr):111 def init(self, cr):
97 """ Migration stuff, name is not anymore methods names112 """ Migration stuff
98 but models name"""113
114 Name is not anymore methods names but the name
115 of the model which does the reconciliation
116 """
99 cr.execute("""117 cr.execute("""
100 UPDATE account_easy_reconcile_method118 UPDATE account_easy_reconcile_method
101 SET name = 'easy.reconcile.simple.partner'119 SET name = 'easy.reconcile.simple.partner'
@@ -108,7 +126,7 @@
108 """)126 """)
109127
110128
111class account_easy_reconcile(Model):129class account_easy_reconcile(orm.Model):
112130
113 _name = 'account.easy.reconcile'131 _name = 'account.easy.reconcile'
114 _description = 'account easy reconcile'132 _description = 'account easy reconcile'
@@ -147,17 +165,22 @@
147 return result165 return result
148166
149 _columns = {167 _columns = {
150 'name': fields.char('Name', size=64, required=True),168 'name': fields.char('Name', required=True),
151 'account': fields.many2one('account.account', 'Account', required=True),169 'account': fields.many2one(
152 'reconcile_method': fields.one2many('account.easy.reconcile.method', 'task_id', 'Method'),170 'account.account', 'Account', required=True),
153 'unreconciled_count': fields.function(_get_total_unrec,171 'reconcile_method': fields.one2many(
154 type='integer', string='Unreconciled Entries'),172 'account.easy.reconcile.method', 'task_id', 'Method'),
155 'reconciled_partial_count': fields.function(_get_partial_rec,173 'unreconciled_count': fields.function(
156 type='integer', string='Partially Reconciled Entries'),174 _get_total_unrec, type='integer', string='Unreconciled Items'),
175 'reconciled_partial_count': fields.function(
176 _get_partial_rec,
177 type='integer',
178 string='Partially Reconciled Items'),
157 'history_ids': fields.one2many(179 'history_ids': fields.one2many(
158 'easy.reconcile.history',180 'easy.reconcile.history',
159 'easy_reconcile_id',181 'easy_reconcile_id',
160 string='History'),182 string='History',
183 readonly=True),
161 'last_history':184 'last_history':
162 fields.function(185 fields.function(
163 _last_history,186 _last_history,
@@ -165,16 +188,18 @@
165 type='many2one',188 type='many2one',
166 relation='easy.reconcile.history',189 relation='easy.reconcile.history',
167 readonly=True),190 readonly=True),
191 'company_id': fields.many2one('res.company', 'Company'),
168 }192 }
169193
170 def _prepare_run_transient(self, cr, uid, rec_method, context=None):194 def _prepare_run_transient(self, cr, uid, rec_method, context=None):
171 return {'account_id': rec_method.task_id.account.id,195 return {'account_id': rec_method.task_id.account.id,
172 'write_off': rec_method.write_off,196 'write_off': rec_method.write_off,
173 'account_lost_id': rec_method.account_lost_id and \197 'account_lost_id': (rec_method.account_lost_id and
174 rec_method.account_lost_id.id,198 rec_method.account_lost_id.id),
175 'account_profit_id': rec_method.account_profit_id and \199 'account_profit_id': (rec_method.account_profit_id and
176 rec_method.account_profit_id.id,200 rec_method.account_profit_id.id),
177 'journal_id': rec_method.journal_id and rec_method.journal_id.id,201 'journal_id': (rec_method.journal_id and
202 rec_method.journal_id.id),
178 'date_base_on': rec_method.date_base_on,203 'date_base_on': rec_method.date_base_on,
179 'filter': rec_method.filter}204 'filter': rec_method.filter}
180205
@@ -190,8 +215,6 @@
190 res = cr.fetchall()215 res = cr.fetchall()
191 return [row[0] for row in res]216 return [row[0] for row in res]
192217
193 if context is None:
194 context = {}
195 for rec in self.browse(cr, uid, ids, context=context):218 for rec in self.browse(cr, uid, ids, context=context):
196 all_ml_rec_ids = []219 all_ml_rec_ids = []
197 all_ml_partial_ids = []220 all_ml_partial_ids = []
@@ -200,7 +223,8 @@
200 rec_model = self.pool.get(method.name)223 rec_model = self.pool.get(method.name)
201 auto_rec_id = rec_model.create(224 auto_rec_id = rec_model.create(
202 cr, uid,225 cr, uid,
203 self._prepare_run_transient(cr, uid, method, context=context),226 self._prepare_run_transient(
227 cr, uid, method, context=context),
204 context=context)228 context=context)
205229
206 ml_rec_ids, ml_partial_ids = rec_model.automatic_reconcile(230 ml_rec_ids, ml_partial_ids = rec_model.automatic_reconcile(
@@ -224,6 +248,7 @@
224 context=context)248 context=context)
225 return True249 return True
226250
251<<<<<<< TREE
227 def _no_history(self, cr, uid, rec, context=None):252 def _no_history(self, cr, uid, rec, context=None):
228 """ Raise an `osv.except_osv` error, supposed to253 """ Raise an `osv.except_osv` error, supposed to
229 be called when there is no history on the reconciliation254 be called when there is no history on the reconciliation
@@ -234,6 +259,18 @@
234 _('There is no history of reconciled '259 _('There is no history of reconciled '
235 'entries on the task: %s.') % rec.name)260 'entries on the task: %s.') % rec.name)
236261
262=======
263 def _no_history(self, cr, uid, rec, context=None):
264 """ Raise an `osv.except_osv` error, supposed to
265 be called when there is no history on the reconciliation
266 task.
267 """
268 raise osv.except_osv(
269 _('Error'),
270 _('There is no history of reconciled '
271 'items on the task: %s.') % rec.name)
272
273>>>>>>> MERGE-SOURCE
237 def last_history_reconcile(self, cr, uid, rec_id, context=None):274 def last_history_reconcile(self, cr, uid, rec_id, context=None):
238 """ Get the last history record for this reconciliation profile275 """ Get the last history record for this reconciliation profile
239 and return the action which opens move lines reconciled276 and return the action which opens move lines reconciled
240277
=== modified file 'account_easy_reconcile/easy_reconcile.xml'
--- account_easy_reconcile/easy_reconcile.xml 2012-12-20 11:05:28 +0000
+++ account_easy_reconcile/easy_reconcile.xml 2013-06-10 07:05:32 +0000
@@ -1,4 +1,4 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<openerp>2<openerp>
3<data>3<data>
44
@@ -7,52 +7,64 @@
7 <field name="name">account.easy.reconcile.form</field>7 <field name="name">account.easy.reconcile.form</field>
8 <field name="priority">20</field>8 <field name="priority">20</field>
9 <field name="model">account.easy.reconcile</field>9 <field name="model">account.easy.reconcile</field>
10 <field name="type">form</field>
11 <field name="arch" type="xml">10 <field name="arch" type="xml">
12 <form string="Automatic Easy Reconcile">11 <form string="Automatic Easy Reconcile" version="7.0">
13 <separator colspan="4" string="Task Information" />12 <header>
14 <field name="name" select="1"/>13 <button name="run_reconcile" class="oe_highlight"
15 <field name="account"/>14 string="Start Auto Reconciliation" type="object"/>
16 <field name="unreconciled_count"/>15 <button icon="STOCK_JUMP_TO" name="last_history_reconcile"
17 <field name="reconciled_partial_count"/>16 class="oe_highlight"
18 <separator colspan="4" string="Reconcile Method" />17 string="Display items reconciled on the last run"
19 <notebook colspan="4">18 type="object"/>
20 <page name="methods" string="Configuration">19 <button icon="STOCK_JUMP_TO" name="last_history_partial"
21 <field name="reconcile_method" colspan = "4" nolabel="1"/>20 class="oe_highlight"
22 <button icon="gtk-ok" name="run_reconcile" colspan="4"21 string="Display items partially reconciled on the last run"
23 string="Start Auto Reconcilation" type="object"/>22 type="object"/>
24 <button icon="STOCK_JUMP_TO" name="last_history_reconcile" colspan="2"23 </header>
25 string="Display items reconciled on the last run" type="object"/>24 <sheet>
26 <button icon="STOCK_JUMP_TO" name="last_history_partial" colspan="2"25 <separator colspan="4" string="Profile Information" />
27 string="Display items partially reconciled on the last run"26 <group>
28 type="object"/>27 <group>
29 </page>28 <field name="name" select="1"/>
30 <page name="history" string="History">29 <field name="account"/>
31 <field name="history_ids" nolabel="1">30 <field name="company_id" groups="base.group_multi_company"/>
32 <tree string="Automatic Easy Reconcile History">31 </group>
33 <field name="date"/>32 <group>
34 <!-- display the count of lines -->33 <field name="unreconciled_count"/>
35 <field name="reconcile_line_ids"/>34 <field name="reconciled_partial_count"/>
36 <button icon="STOCK_JUMP_TO" name="open_reconcile"35 </group>
37 string="Go to reconciled items" type="object"/>36 </group>
38 <!-- display the count of lines -->37 <notebook colspan="4">
39 <field name="partial_line_ids"/>38 <page name="methods" string="Configuration">
40 <button icon="STOCK_JUMP_TO" name="open_partial"39 <field name="reconcile_method" colspan = "4" nolabel="1"/>
41 string="Go to partially reconciled items" type="object"/>40 </page>
42 </tree>41 <page name="history" string="History">
43 </field>42 <field name="history_ids" nolabel="1">
44 </page>43 <tree string="Automatic Easy Reconcile History">
45 <page name="information" string="Information">44 <field name="date"/>
46 <separator colspan="4" string="Simple. Amount and Name"/>45 <button icon="STOCK_JUMP_TO" name="open_reconcile"
47 <label string="Match one debit line vs one credit line. Do not allow partial reconcilation.46 string="Go to reconciled items" type="object"/>
47 <button icon="STOCK_JUMP_TO" name="open_partial"
48 string="Go to partially reconciled items" type="object"/>
49 </tree>
50 </field>
51 </page>
52 <page name="information" string="Information">
53 <separator colspan="4" string="Simple. Amount and Name"/>
54 <label string="Match one debit line vs one credit line. Do not allow partial reconciliation.
48The lines should have the same amount (with the write-off) and the same name to be reconciled." colspan="4"/>55The lines should have the same amount (with the write-off) and the same name to be reconciled." colspan="4"/>
4956
50 <separator colspan="4" string="Simple. Amount and Name"/>57 <separator colspan="4" string="Simple. Amount and Partner"/>
51 <label string="Match one debit line vs one credit line. Do not allow partial reconcilation.58 <label string="Match one debit line vs one credit line. Do not allow partial reconciliation.
52The lines should have the same amount (with the write-off) and the same partner to be reconciled." colspan="4"/>59The lines should have the same amount (with the write-off) and the same partner to be reconciled." colspan="4"/>
5360
54 </page>61 <separator colspan="4" string="Simple. Amount and Reference"/>
55 </notebook>62 <label string="Match one debit line vs one credit line. Do not allow partial reconciliation.
63The lines should have the same amount (with the write-off) and the same reference to be reconciled." colspan="4"/>
64
65 </page>
66 </notebook>
67 </sheet>
56 </form>68 </form>
57 </field>69 </field>
58 </record>70 </record>
@@ -61,11 +73,11 @@
61 <field name="name">account.easy.reconcile.tree</field>73 <field name="name">account.easy.reconcile.tree</field>
62 <field name="priority">20</field>74 <field name="priority">20</field>
63 <field name="model">account.easy.reconcile</field>75 <field name="model">account.easy.reconcile</field>
64 <field name="type">tree</field>
65 <field name="arch" type="xml">76 <field name="arch" type="xml">
66 <tree string="Automatic Easy Reconcile">77 <tree string="Automatic Easy Reconcile">
67 <field name="name"/>78 <field name="name"/>
68 <field name="account"/>79 <field name="account"/>
80 <field name="company_id" groups="base.group_multi_company"/>
69 <field name="unreconciled_count"/>81 <field name="unreconciled_count"/>
70 <field name="reconciled_partial_count"/>82 <field name="reconciled_partial_count"/>
71 <button icon="gtk-ok" name="run_reconcile" colspan="4"83 <button icon="gtk-ok" name="run_reconcile" colspan="4"
@@ -85,16 +97,25 @@
85 <field name="res_model">account.easy.reconcile</field>97 <field name="res_model">account.easy.reconcile</field>
86 <field name="view_type">form</field>98 <field name="view_type">form</field>
87 <field name="view_mode">tree,form</field>99 <field name="view_mode">tree,form</field>
100 <field name="help" type="html">
101 <p class="oe_view_nocontent_create">
102 Click to add a reconciliation profile.
103 </p><p>
104 A reconciliation profile specifies, for one account, how
105 the entries should be reconciled.
106 You can select one or many reconciliation methods which will
107 be run sequentially to match the entries between them.
108 </p>
109 </field>
88 </record>110 </record>
89111
90112
91<!-- account.easy.reconcile.method view -->113 <!-- account.easy.reconcile.method view -->
92114
93 <record id="account_easy_reconcile_method_form" model="ir.ui.view">115 <record id="account_easy_reconcile_method_form" model="ir.ui.view">
94 <field name="name">account.easy.reconcile.method.form</field>116 <field name="name">account.easy.reconcile.method.form</field>
95 <field name="priority">20</field>117 <field name="priority">20</field>
96 <field name="model">account.easy.reconcile.method</field>118 <field name="model">account.easy.reconcile.method</field>
97 <field name="type">form</field>
98 <field name="arch" type="xml">119 <field name="arch" type="xml">
99 <form string="Automatic Easy Reconcile Method">120 <form string="Automatic Easy Reconcile Method">
100 <field name="sequence"/>121 <field name="sequence"/>
@@ -104,7 +125,6 @@
104 <field name="account_profit_id" attrs="{'required':[('write_off','>',0)]}"/>125 <field name="account_profit_id" attrs="{'required':[('write_off','>',0)]}"/>
105 <field name="journal_id" attrs="{'required':[('write_off','>',0)]}"/>126 <field name="journal_id" attrs="{'required':[('write_off','>',0)]}"/>
106 <field name="date_base_on"/>127 <field name="date_base_on"/>
107 <field name="filter" groups="base.group_extended"/>
108 </form>128 </form>
109 </field>129 </field>
110 </record>130 </record>
@@ -113,8 +133,7 @@
113 <field name="name">account.easy.reconcile.method.tree</field>133 <field name="name">account.easy.reconcile.method.tree</field>
114 <field name="priority">20</field>134 <field name="priority">20</field>
115 <field name="model">account.easy.reconcile.method</field>135 <field name="model">account.easy.reconcile.method</field>
116 <field name="type">tree</field>136 <field name="arch" type="xml">
117 <field name="arch" type="xml">
118 <tree editable="top" string="Automatic Easy Reconcile Method">137 <tree editable="top" string="Automatic Easy Reconcile Method">
119 <field name="sequence"/>138 <field name="sequence"/>
120 <field name="name"/>139 <field name="name"/>
@@ -123,14 +142,15 @@
123 <field name="account_profit_id" attrs="{'required':[('write_off','>',0)]}"/>142 <field name="account_profit_id" attrs="{'required':[('write_off','>',0)]}"/>
124 <field name="journal_id" attrs="{'required':[('write_off','>',0)]}"/>143 <field name="journal_id" attrs="{'required':[('write_off','>',0)]}"/>
125 <field name="date_base_on"/>144 <field name="date_base_on"/>
126 <field name="filter"/>
127 </tree>145 </tree>
128 </field>146 </field>
129 </record>147 </record>
130148
131<!-- menu item -->149 <!-- menu item -->
132150
133 <menuitem action="action_account_easy_reconcile" id="menu_easy_reconcile" parent="account.periodical_processing_reconciliation"/>151 <menuitem action="action_account_easy_reconcile"
152 id="menu_easy_reconcile"
153 parent="account.periodical_processing_reconciliation"/>
134154
135</data>155</data>
136</openerp>156</openerp>
137157
=== modified file 'account_easy_reconcile/easy_reconcile_history.py'
--- account_easy_reconcile/easy_reconcile_history.py 2012-12-20 10:15:12 +0000
+++ account_easy_reconcile/easy_reconcile_history.py 2013-06-10 07:05:32 +0000
@@ -81,6 +81,13 @@
81 relation='account.move.line',81 relation='account.move.line',
82 readonly=True,82 readonly=True,
83 multi='lines'),83 multi='lines'),
84 'company_id': fields.related('easy_reconcile_id','company_id',
85 relation='res.company',
86 type='many2one',
87 string='Company',
88 store=True,
89 readonly=True),
90
84 }91 }
8592
86 def _open_move_lines(self, cr, uid, history_id, rec_type='full', context=None):93 def _open_move_lines(self, cr, uid, history_id, rec_type='full', context=None):
8794
=== modified file 'account_easy_reconcile/easy_reconcile_history_view.xml'
--- account_easy_reconcile/easy_reconcile_history_view.xml 2012-12-19 15:40:41 +0000
+++ account_easy_reconcile/easy_reconcile_history_view.xml 2013-06-10 07:05:32 +0000
@@ -5,7 +5,6 @@
5 <record id="view_easy_reconcile_history_search" model="ir.ui.view">5 <record id="view_easy_reconcile_history_search" model="ir.ui.view">
6 <field name="name">easy.reconcile.history.search</field>6 <field name="name">easy.reconcile.history.search</field>
7 <field name="model">easy.reconcile.history</field>7 <field name="model">easy.reconcile.history</field>
8 <field name="type">search</field>
9 <field name="arch" type="xml">8 <field name="arch" type="xml">
10 <search string="Automatic Easy Reconcile History">9 <search string="Automatic Easy Reconcile History">
11 <filter icon="terp-go-today" string="Today"10 <filter icon="terp-go-today" string="Today"
@@ -34,44 +33,45 @@
3433
35 <record id="easy_reconcile_history_form" model="ir.ui.view">34 <record id="easy_reconcile_history_form" model="ir.ui.view">
36 <field name="name">easy.reconcile.history.form</field>35 <field name="name">easy.reconcile.history.form</field>
37 <field name="priority">16</field>
38 <field name="model">easy.reconcile.history</field>36 <field name="model">easy.reconcile.history</field>
39 <field name="type">form</field>
40 <field name="arch" type="xml">37 <field name="arch" type="xml">
41 <form string="Automatic Easy Reconcile History">38 <form string="Automatic Easy Reconcile History" version="7.0">
42 <field name="easy_reconcile_id"/>39 <header>
43 <field name="date"/>40 <button name="open_reconcile"
44 <group colspan="2" col="2">41 string="Go to reconciled items"
45 <separator colspan="2" string="Reconcilations"/>42 icon="STOCK_JUMP_TO" type="object"/>
46 <field name="reconcile_ids" nolabel="1"/>43 <button name="open_partial"
47 </group>44 string="Go to partially reconciled items"
48 <group colspan="2" col="2">45 icon="STOCK_JUMP_TO" type="object"/>
49 <separator colspan="2" string="Partial Reconcilations"/>46 </header>
50 <field name="reconcile_partial_ids" nolabel="1"/>47 <sheet>
51 </group>48 <group>
52 <group col="2" colspan="4">49 <field name="easy_reconcile_id"/>
53 <button icon="STOCK_JUMP_TO" name="open_reconcile" string="Go to reconciled items" type="object"/>50 <field name="date"/>
54 <button icon="STOCK_JUMP_TO" name="open_partial" string="Go to partially reconciled items" type="object"/>51 <field name="company_id" groups="base.group_multi_company"/>
55 </group>52 </group>
53 <group col="2">
54 <separator colspan="2" string="Reconciliations"/>
55 <field name="reconcile_ids" nolabel="1"/>
56 </group>
57 <group col="2">
58 <separator colspan="2" string="Partial Reconciliations"/>
59 <field name="reconcile_partial_ids" nolabel="1"/>
60 </group>
61 </sheet>
56 </form>62 </form>
57 </field>63 </field>
58 </record>64 </record>
5965
60 <record id="easy_reconcile_history_tree" model="ir.ui.view">66 <record id="easy_reconcile_history_tree" model="ir.ui.view">
61 <field name="name">easy.reconcile.history.tree</field>67 <field name="name">easy.reconcile.history.tree</field>
62 <field name="priority">16</field>
63 <field name="model">easy.reconcile.history</field>68 <field name="model">easy.reconcile.history</field>
64 <field name="type">tree</field>
65 <field name="arch" type="xml">69 <field name="arch" type="xml">
66 <tree string="Automatic Easy Reconcile History">70 <tree string="Automatic Easy Reconcile History">
67 <field name="easy_reconcile_id"/>71 <field name="easy_reconcile_id"/>
68 <field name="date"/>72 <field name="date"/>
69 <!-- display the count of lines -->
70 <field name="reconcile_line_ids"/>
71 <button icon="STOCK_JUMP_TO" name="open_reconcile"73 <button icon="STOCK_JUMP_TO" name="open_reconcile"
72 string="Go to reconciled items" type="object"/>74 string="Go to reconciled items" type="object"/>
73 <!-- display the count of lines -->
74 <field name="partial_line_ids"/>
75 <button icon="STOCK_JUMP_TO" name="open_partial"75 <button icon="STOCK_JUMP_TO" name="open_partial"
76 string="Go to partially reconciled items" type="object"/>76 string="Go to partially reconciled items" type="object"/>
77 </tree>77 </tree>
7878
=== modified file 'account_easy_reconcile/i18n/fr.po'
--- account_easy_reconcile/i18n/fr.po 2013-03-12 12:11:33 +0000
+++ account_easy_reconcile/i18n/fr.po 2013-06-10 07:05:32 +0000
@@ -6,45 +6,73 @@
6msgstr ""6msgstr ""
7"Project-Id-Version: OpenERP Server 6.1\n"7"Project-Id-Version: OpenERP Server 6.1\n"
8"Report-Msgid-Bugs-To: \n"8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2012-12-20 08:54+0000\n"9"POT-Creation-Date: 2013-01-04 08:39+0000\n"
10"PO-Revision-Date: 2012-11-07 12:59+0000\n"10"PO-Revision-Date: 2013-01-04 09:55+0100\n"
11"Last-Translator: <>\n"11"Last-Translator: Guewen Baconnier <guewen.baconnier@camptocamp.com>\n"
12"Language-Team: \n"12"Language-Team: \n"
13"Language: \n"13"Language: \n"
14"MIME-Version: 1.0\n"14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: \n"16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: \n"17"Plural-Forms: \n"
1818
19#. module: account_easy_reconcile19#. module: account_easy_reconcile
20#: code:addons/account_easy_reconcile/easy_reconcile_history.py:10320#: code:addons/account_easy_reconcile/easy_reconcile_history.py:101
21#: view:easy.reconcile.history:0
22#: field:easy.reconcile.history,reconcile_ids:0
23#, python-format
21msgid "Reconciliations"24msgid "Reconciliations"
22msgstr "Lettrages"25msgstr "Lettrages"
2326
24#. module: account_easy_reconcile27#. module: account_easy_reconcile
25#: view:account.easy.reconcile:028#: view:account.easy.reconcile:0
29#: view:easy.reconcile.history:0
30msgid "Automatic Easy Reconcile History"
31msgstr "Historique des lettrages automatisés"
32
33#. module: account_easy_reconcile
34#: view:account.easy.reconcile:0
26msgid "Information"35msgid "Information"
27msgstr "Information"36msgstr "Information"
2837
29#. module: account_easy_reconcile38#. module: account_easy_reconcile
30#: view:account.easy.reconcile:0 view:easy.reconcile.history:039#: view:account.easy.reconcile:0
31msgid "Automatic Easy Reconcile History"40#: view:easy.reconcile.history:0
32msgstr "Historique des lettrages automatisés"
33
34#. module: account_easy_reconcile
35#: view:account.easy.reconcile:0 view:easy.reconcile.history:0
36msgid "Go to partially reconciled items"41msgid "Go to partially reconciled items"
37msgstr "Voir les entrées partiellement lettrées"42msgstr "Voir les entrées partiellement lettrées"
3843
39#. module: account_easy_reconcile44#. module: account_easy_reconcile
45#: help:account.easy.reconcile.method,sequence:0
46msgid "The sequence field is used to order the reconcile method"
47msgstr "La séquence détermine l'ordre des méthodes de lettrage"
48
49#. module: account_easy_reconcile
40#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_history50#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_history
41msgid "easy.reconcile.history"51msgid "easy.reconcile.history"
42msgstr "easy.reconcile.history"52msgstr "easy.reconcile.history"
4353
44#. module: account_easy_reconcile54#. module: account_easy_reconcile
45#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_name55#: model:ir.actions.act_window,help:account_easy_reconcile.action_account_easy_reconcile
46msgid "easy.reconcile.simple.name"56msgid ""
47msgstr "easy.reconcile.simple.name"57"<p class=\"oe_view_nocontent_create\">\n"
58" Click to add a reconciliation profile.\n"
59" </p><p>\n"
60" A reconciliation profile specifies, for one account, how\n"
61" the entries should be reconciled.\n"
62" You can select one or many reconciliation methods which will\n"
63" be run sequentially to match the entries between them.\n"
64" </p>\n"
65" "
66msgstr ""
67"<p class=\"oe_view_nocontent_create\">\n"
68" Cliquez pour ajouter un profil de lettrage.\n"
69" </p><p>\n"
70" Un profil de lettrage spécifie, pour un compte, comment\n"
71" les écritures doivent être lettrées.\n"
72" Vous pouvez sélectionner une ou plusieurs méthodes de lettrage\n"
73" qui seront lancées successivement pour identifier les écritures\n"
74" devant être lettrées. </p>\n"
75" "
4876
49#. module: account_easy_reconcile77#. module: account_easy_reconcile
50#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_options78#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_options
@@ -57,14 +85,9 @@
57msgstr "Grouper par..."85msgstr "Grouper par..."
5886
59#. module: account_easy_reconcile87#. module: account_easy_reconcile
60#: view:account.easy.reconcile:088#: field:account.easy.reconcile,unreconciled_count:0
61msgid "Task Information"89msgid "Unreconciled Items"
62msgstr "Information sur la tâche"90msgstr "Écritures non lettrées"
63
64#. module: account_easy_reconcile
65#: view:account.easy.reconcile:0
66msgid "Reconcile Method"
67msgstr "Méthode de lettrage"
6891
69#. module: account_easy_reconcile92#. module: account_easy_reconcile
70#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_base93#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_base
@@ -72,6 +95,16 @@
72msgstr "easy.reconcile.base"95msgstr "easy.reconcile.base"
7396
74#. module: account_easy_reconcile97#. module: account_easy_reconcile
98#: field:easy.reconcile.history,reconcile_line_ids:0
99msgid "Reconciled Items"
100msgstr "Écritures lettrées"
101
102#. module: account_easy_reconcile
103#: field:account.easy.reconcile,reconcile_method:0
104msgid "Method"
105msgstr "Méthode"
106
107#. module: account_easy_reconcile
75#: view:easy.reconcile.history:0108#: view:easy.reconcile.history:0
76msgid "7 Days"109msgid "7 Days"
77msgstr "7 jours"110msgstr "7 jours"
@@ -82,29 +115,51 @@
82msgstr "Lettrage automatisé"115msgstr "Lettrage automatisé"
83116
84#. module: account_easy_reconcile117#. module: account_easy_reconcile
118#: field:easy.reconcile.history,date:0
119msgid "Run date"
120msgstr "Date de lancement"
121
122#. module: account_easy_reconcile
123#: view:account.easy.reconcile:0
124msgid "Match one debit line vs one credit line. Do not allow partial reconciliation. The lines should have the same amount (with the write-off) and the same reference to be reconciled."
125msgstr "Lettre un débit avec un crédit ayant le même montant et la même référence. Le lettrage ne peut être partiel (écriture d'ajustement en cas d'écart)."
126
127#. module: account_easy_reconcile
85#: model:ir.actions.act_window,name:account_easy_reconcile.act_easy_reconcile_to_history128#: model:ir.actions.act_window,name:account_easy_reconcile.act_easy_reconcile_to_history
86msgid "History Details"129msgid "History Details"
87msgstr "Détails de l'historique"130msgstr "Détails de l'historique"
88131
89#. module: account_easy_reconcile132#. module: account_easy_reconcile
90#: view:account.easy.reconcile:0133#: view:account.easy.reconcile:0
91msgid ""
92"Match one debit line vs one credit line. Do not allow partial reconcilation. "
93"The lines should have the same amount (with the write-off) and the same name "
94"to be reconciled."
95msgstr ""
96"Lettre un débit avec un crédit ayant le même montant et la même description. "
97"Le lettrage ne peut être partiel (écriture d'ajustement en cas d'écart)."
98
99#. module: account_easy_reconcile
100#: view:account.easy.reconcile:0
101msgid "Display items reconciled on the last run"134msgid "Display items reconciled on the last run"
102msgstr "Voir les entrées lettrées au dernier lettrage"135msgstr "Voir les entrées lettrées au dernier lettrage"
103136
104#. module: account_easy_reconcile137#. module: account_easy_reconcile
105#: model:ir.model,name:account_easy_reconcile.model_account_easy_reconcile_method138#: field:account.easy.reconcile.method,name:0
106msgid "reconcile method for account_easy_reconcile"139msgid "Type"
107msgstr "Méthode de lettrage"140msgstr "Type"
141
142#. module: account_easy_reconcile
143#: field:account.easy.reconcile.method,journal_id:0
144#: field:easy.reconcile.base,journal_id:0
145#: field:easy.reconcile.options,journal_id:0
146#: field:easy.reconcile.simple,journal_id:0
147#: field:easy.reconcile.simple.name,journal_id:0
148#: field:easy.reconcile.simple.partner,journal_id:0
149#: field:easy.reconcile.simple.reference,journal_id:0
150msgid "Journal"
151msgstr "Journal"
152
153#. module: account_easy_reconcile
154#: field:account.easy.reconcile.method,account_profit_id:0
155#: field:easy.reconcile.base,account_profit_id:0
156#: field:easy.reconcile.options,account_profit_id:0
157#: field:easy.reconcile.simple,account_profit_id:0
158#: field:easy.reconcile.simple.name,account_profit_id:0
159#: field:easy.reconcile.simple.partner,account_profit_id:0
160#: field:easy.reconcile.simple.reference,account_profit_id:0
161msgid "Account Profit"
162msgstr "Compte de profits"
108163
109#. module: account_easy_reconcile164#. module: account_easy_reconcile
110#: view:easy.reconcile.history:0165#: view:easy.reconcile.history:0
@@ -117,6 +172,15 @@
117msgstr "Simple. Montant et description"172msgstr "Simple. Montant et description"
118173
119#. module: account_easy_reconcile174#. module: account_easy_reconcile
175#: field:easy.reconcile.base,partner_ids:0
176#: field:easy.reconcile.simple,partner_ids:0
177#: field:easy.reconcile.simple.name,partner_ids:0
178#: field:easy.reconcile.simple.partner,partner_ids:0
179#: field:easy.reconcile.simple.reference,partner_ids:0
180msgid "Restrict on partners"
181msgstr "Filtrer sur des partenaires"
182
183#. module: account_easy_reconcile
120#: model:ir.actions.act_window,name:account_easy_reconcile.action_account_easy_reconcile184#: model:ir.actions.act_window,name:account_easy_reconcile.action_account_easy_reconcile
121#: model:ir.ui.menu,name:account_easy_reconcile.menu_easy_reconcile185#: model:ir.ui.menu,name:account_easy_reconcile.menu_easy_reconcile
122msgid "Easy Automatic Reconcile"186msgid "Easy Automatic Reconcile"
@@ -133,54 +197,162 @@
133msgstr "Date"197msgstr "Date"
134198
135#. module: account_easy_reconcile199#. module: account_easy_reconcile
200#: field:account.easy.reconcile,last_history:0
201msgid "Last History"
202msgstr "Dernier historique"
203
204#. module: account_easy_reconcile
136#: view:account.easy.reconcile:0205#: view:account.easy.reconcile:0
137msgid "Configuration"206msgid "Configuration"
138msgstr "Configuration"207msgstr "Configuration"
139208
140#. module: account_easy_reconcile209#. module: account_easy_reconcile
210#: field:account.easy.reconcile,reconciled_partial_count:0
211#: field:easy.reconcile.history,partial_line_ids:0
212msgid "Partially Reconciled Items"
213msgstr "Écritures partiellement lettrées"
214
215#. module: account_easy_reconcile
141#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_partner216#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_partner
142msgid "easy.reconcile.simple.partner"217msgid "easy.reconcile.simple.partner"
143msgstr "easy.reconcile.simple.partner"218msgstr "easy.reconcile.simple.partner"
144219
145#. module: account_easy_reconcile220#. module: account_easy_reconcile
221#: field:account.easy.reconcile.method,write_off:0
222#: field:easy.reconcile.base,write_off:0
223#: field:easy.reconcile.options,write_off:0
224#: field:easy.reconcile.simple,write_off:0
225#: field:easy.reconcile.simple.name,write_off:0
226#: field:easy.reconcile.simple.partner,write_off:0
227#: field:easy.reconcile.simple.reference,write_off:0
228msgid "Write off allowed"
229msgstr "Écart autorisé"
230
231#. module: account_easy_reconcile
146#: view:account.easy.reconcile:0232#: view:account.easy.reconcile:0
147msgid "Automatic Easy Reconcile"233msgid "Automatic Easy Reconcile"
148msgstr "Lettrage automatisé"234msgstr "Lettrage automatisé"
149235
150#. module: account_easy_reconcile236#. module: account_easy_reconcile
237#: field:account.easy.reconcile,account:0
238#: field:easy.reconcile.base,account_id:0
239#: field:easy.reconcile.simple,account_id:0
240#: field:easy.reconcile.simple.name,account_id:0
241#: field:easy.reconcile.simple.partner,account_id:0
242#: field:easy.reconcile.simple.reference,account_id:0
243msgid "Account"
244msgstr "Compte"
245
246#. module: account_easy_reconcile
247#: field:account.easy.reconcile.method,task_id:0
248msgid "Task"
249msgstr "Tâche"
250
251#. module: account_easy_reconcile
252#: field:account.easy.reconcile,name:0
253msgid "Name"
254msgstr "Nom"
255
256#. module: account_easy_reconcile
257#: view:account.easy.reconcile:0
258msgid "Simple. Amount and Partner"
259msgstr "Simple. Montant et partenaire"
260
261#. module: account_easy_reconcile
151#: view:account.easy.reconcile:0262#: view:account.easy.reconcile:0
152msgid "Start Auto Reconcilation"263msgid "Start Auto Reconcilation"
153msgstr "Lancer le lettrage automatisé"264msgstr "Lancer le lettrage automatisé"
154265
155#. module: account_easy_reconcile266#. module: account_easy_reconcile
267#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_name
268msgid "easy.reconcile.simple.name"
269msgstr "easy.reconcile.simple.name"
270
271#. module: account_easy_reconcile
272#: field:account.easy.reconcile.method,filter:0
273#: field:easy.reconcile.base,filter:0
274#: field:easy.reconcile.options,filter:0
275#: field:easy.reconcile.simple,filter:0
276#: field:easy.reconcile.simple.name,filter:0
277#: field:easy.reconcile.simple.partner,filter:0
278#: field:easy.reconcile.simple.reference,filter:0
279msgid "Filter"
280msgstr "Filtre"
281
282#. module: account_easy_reconcile
283#: view:account.easy.reconcile:0
284msgid "Match one debit line vs one credit line. Do not allow partial reconciliation. The lines should have the same amount (with the write-off) and the same partner to be reconciled."
285msgstr "Lettre un débit avec un crédit ayant le même montant et le même partenaire. Le lettrage ne peut être partiel (écriture d'ajustement en cas d'écart)."
286
287#. module: account_easy_reconcile
288#: field:easy.reconcile.history,easy_reconcile_id:0
289msgid "Reconcile Profile"
290msgstr "Profil de réconciliation"
291
292#. module: account_easy_reconcile
293#: view:account.easy.reconcile:0
294msgid "Start Auto Reconciliation"
295msgstr "Lancer le lettrage automatisé"
296
297#. module: account_easy_reconcile
298#: code:addons/account_easy_reconcile/easy_reconcile.py:250
299#, python-format
300msgid "Error"
301msgstr "Erreur"
302
303#. module: account_easy_reconcile
304#: code:addons/account_easy_reconcile/easy_reconcile.py:251
305#, python-format
306msgid "There is no history of reconciled items on the task: %s."
307msgstr "Il n'y a pas d'historique d'écritures lettrées sur la tâche: %s."
308
309#. module: account_easy_reconcile
310#: view:account.easy.reconcile:0
311msgid "Match one debit line vs one credit line. Do not allow partial reconciliation. The lines should have the same amount (with the write-off) and the same name to be reconciled."
312msgstr "Lettre un débit avec un crédit ayant le même montant et la même description. Le lettrage ne peut être partiel (écriture d'ajustement en cas d'écart)."
313
314#. module: account_easy_reconcile
315#: field:account.easy.reconcile.method,account_lost_id:0
316#: field:easy.reconcile.base,account_lost_id:0
317#: field:easy.reconcile.options,account_lost_id:0
318#: field:easy.reconcile.simple,account_lost_id:0
319#: field:easy.reconcile.simple.name,account_lost_id:0
320#: field:easy.reconcile.simple.partner,account_lost_id:0
321#: field:easy.reconcile.simple.reference,account_lost_id:0
322msgid "Account Lost"
323msgstr "Compte de pertes"
324
325#. module: account_easy_reconcile
156#: view:easy.reconcile.history:0326#: view:easy.reconcile.history:0
157msgid "Reconciliation Profile"327msgid "Reconciliation Profile"
158msgstr "Profil de réconciliation"328msgstr "Profil de réconciliation"
159329
160#. module: account_easy_reconcile330#. module: account_easy_reconcile
161#: view:account.easy.reconcile:0331#: view:account.easy.reconcile:0
332#: field:account.easy.reconcile,history_ids:0
162msgid "History"333msgid "History"
163msgstr "Historique"334msgstr "Historique"
164335
165#. module: account_easy_reconcile336#. module: account_easy_reconcile
166#: view:account.easy.reconcile:0 view:easy.reconcile.history:0337#: view:account.easy.reconcile:0
338#: view:easy.reconcile.history:0
167msgid "Go to reconciled items"339msgid "Go to reconciled items"
168msgstr "Voir les entrées lettrées"340msgstr "Voir les entrées lettrées"
169341
170#. module: account_easy_reconcile342#. module: account_easy_reconcile
343#: view:account.easy.reconcile:0
344msgid "Profile Information"
345msgstr "Information sur le profil"
346
347#. module: account_easy_reconcile
171#: view:account.easy.reconcile.method:0348#: view:account.easy.reconcile.method:0
172msgid "Automatic Easy Reconcile Method"349msgid "Automatic Easy Reconcile Method"
173msgstr "Méthode de lettrage automatisé"350msgstr "Méthode de lettrage automatisé"
174351
175#. module: account_easy_reconcile352#. module: account_easy_reconcile
176#: view:account.easy.reconcile:0353#: view:account.easy.reconcile:0
177msgid ""354msgid "Simple. Amount and Reference"
178"Match one debit line vs one credit line. Do not allow partial reconcilation. "355msgstr "Simple. Montant et référence"
179"The lines should have the same amount (with the write-off) and the same "
180"partner to be reconciled."
181msgstr ""
182"Lettre un débit avec un crédit ayant le même montant et le même partenaire. "
183"Le lettrage ne peut être partiel (écriture d'ajustement en cas d'écart)."
184356
185#. module: account_easy_reconcile357#. module: account_easy_reconcile
186#: view:account.easy.reconcile:0358#: view:account.easy.reconcile:0
@@ -188,9 +360,9 @@
188msgstr "Afficher les entrées partiellement lettrées au dernier lettrage"360msgstr "Afficher les entrées partiellement lettrées au dernier lettrage"
189361
190#. module: account_easy_reconcile362#. module: account_easy_reconcile
191#: view:easy.reconcile.history:0363#: field:account.easy.reconcile.method,sequence:0
192msgid "Partial Reconcilations"364msgid "Sequence"
193msgstr "Lettrages partiels"365msgstr "Séquence"
194366
195#. module: account_easy_reconcile367#. module: account_easy_reconcile
196#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple368#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple
@@ -203,11 +375,30 @@
203msgstr "Lettrages des 7 derniers jours"375msgstr "Lettrages des 7 derniers jours"
204376
205#. module: account_easy_reconcile377#. module: account_easy_reconcile
206#: code:addons/account_easy_reconcile/easy_reconcile_history.py:106378#: field:account.easy.reconcile.method,date_base_on:0
379#: field:easy.reconcile.base,date_base_on:0
380#: field:easy.reconcile.options,date_base_on:0
381#: field:easy.reconcile.simple,date_base_on:0
382#: field:easy.reconcile.simple.name,date_base_on:0
383#: field:easy.reconcile.simple.partner,date_base_on:0
384#: field:easy.reconcile.simple.reference,date_base_on:0
385msgid "Date of reconciliation"
386msgstr "Date de lettrage"
387
388#. module: account_easy_reconcile
389#: code:addons/account_easy_reconcile/easy_reconcile_history.py:104
390#: view:easy.reconcile.history:0
391#: field:easy.reconcile.history,reconcile_partial_ids:0
392#, python-format
207msgid "Partial Reconciliations"393msgid "Partial Reconciliations"
208msgstr "Lettrages partiels"394msgstr "Lettrages partiels"
209395
210#. module: account_easy_reconcile396#. module: account_easy_reconcile
397#: model:ir.model,name:account_easy_reconcile.model_account_easy_reconcile_method
398msgid "reconcile method for account_easy_reconcile"
399msgstr "Méthode de lettrage"
400
401#. module: account_easy_reconcile
211#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_reference402#: model:ir.model,name:account_easy_reconcile.model_easy_reconcile_simple_reference
212msgid "easy.reconcile.simple.reference"403msgid "easy.reconcile.simple.reference"
213msgstr "easy.reconcile.simple.reference"404msgstr "easy.reconcile.simple.reference"
@@ -217,5 +408,18 @@
217msgid "account easy reconcile"408msgid "account easy reconcile"
218msgstr "Lettrage automatisé"409msgstr "Lettrage automatisé"
219410
411#~ msgid "Unreconciled Entries"
412#~ msgstr "Écritures non lettrées"
413
414#, fuzzy
415#~ msgid "Partially Reconciled Entries"
416#~ msgstr "Lettrages partiels"
417
418#~ msgid "Task Information"
419#~ msgstr "Information sur la tâche"
420
421#~ msgid "Reconcile Method"
422#~ msgstr "Méthode de lettrage"
423
220#~ msgid "Log"424#~ msgid "Log"
221#~ msgstr "Historique"425#~ msgstr "Historique"
222426
=== added file 'account_easy_reconcile/security/ir_rule.xml'
--- account_easy_reconcile/security/ir_rule.xml 1970-01-01 00:00:00 +0000
+++ account_easy_reconcile/security/ir_rule.xml 2013-06-10 07:05:32 +0000
@@ -0,0 +1,25 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data noupdate="1">
4 <record id="easy_reconcile_rule" model="ir.rule">
5 <field name="name">Easy reconcile multi-company</field>
6 <field name="model_id" ref="model_account_easy_reconcile"/>
7 <field name="global" eval="True"/>
8 <field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
9 </record>
10
11 <record id="easy_reconcile_history_rule" model="ir.rule">
12 <field name="name">Easy reconcile history multi-company</field>
13 <field name="model_id" ref="model_easy_reconcile_history"/>
14 <field name="global" eval="True"/>
15 <field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
16 </record>
17
18 <record id="easy_reconcile_method_rule" model="ir.rule">
19 <field name="name">Easy reconcile method multi-company</field>
20 <field name="model_id" ref="model_account_easy_reconcile_method"/>
21 <field name="global" eval="True"/>
22 <field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
23 </record>
24 </data>
25</openerp>
026
=== modified file 'account_easy_reconcile/simple_reconciliation.py'
--- account_easy_reconcile/simple_reconciliation.py 2012-11-01 16:14:03 +0000
+++ account_easy_reconcile/simple_reconciliation.py 2013-06-10 07:05:32 +0000
@@ -1,7 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright 2012 Camptocamp SA (Guewen Baconnier)4# Copyright 2012-2013 Camptocamp SA (Guewen Baconnier)
5# Copyright (C) 2010 Sébastien Beau5# Copyright (C) 2010 Sébastien Beau
6#6#
7# This program is free software: you can redistribute it and/or modify7# This program is free software: you can redistribute it and/or modify
@@ -41,7 +41,7 @@
41 count = 041 count = 0
42 res = []42 res = []
43 while (count < len(lines)):43 while (count < len(lines)):
44 for i in range(count+1, len(lines)):44 for i in xrange(count+1, len(lines)):
45 writeoff_account_id = False45 writeoff_account_id = False
46 if lines[count][self._key_field] != lines[i][self._key_field]:46 if lines[count][self._key_field] != lines[i][self._key_field]:
47 break47 break
@@ -94,7 +94,6 @@
9494
95 _name = 'easy.reconcile.simple.name'95 _name = 'easy.reconcile.simple.name'
96 _inherit = 'easy.reconcile.simple'96 _inherit = 'easy.reconcile.simple'
97 _auto = True # False when inherited from AbstractModel
9897
99 # has to be subclassed98 # has to be subclassed
100 # field name used as key for matching the move lines99 # field name used as key for matching the move lines
@@ -105,17 +104,16 @@
105104
106 _name = 'easy.reconcile.simple.partner'105 _name = 'easy.reconcile.simple.partner'
107 _inherit = 'easy.reconcile.simple'106 _inherit = 'easy.reconcile.simple'
108 _auto = True # False when inherited from AbstractModel
109107
110 # has to be subclassed108 # has to be subclassed
111 # field name used as key for matching the move lines109 # field name used as key for matching the move lines
112 _key_field = 'partner_id'110 _key_field = 'partner_id'
113111
112
114class easy_reconcile_simple_reference(TransientModel):113class easy_reconcile_simple_reference(TransientModel):
115114
116 _name = 'easy.reconcile.simple.reference'115 _name = 'easy.reconcile.simple.reference'
117 _inherit = 'easy.reconcile.simple'116 _inherit = 'easy.reconcile.simple'
118 _auto = True # False when inherited from AbstractModel
119117
120 # has to be subclassed118 # has to be subclassed
121 # field name used as key for matching the move lines119 # field name used as key for matching the move lines
122120
=== modified file 'account_statement_base_completion/__init__.py'
--- account_statement_base_completion/__init__.py 2012-06-22 15:45:50 +0000
+++ account_statement_base_completion/__init__.py 2013-06-10 07:05:32 +0000
@@ -20,4 +20,4 @@
20##############################################################################20##############################################################################
2121
22import statement22import statement
23import partner
24\ No newline at end of file23\ No newline at end of file
24import partner
2525
=== modified file 'account_statement_base_completion/__openerp__.py'
--- account_statement_base_completion/__openerp__.py 2012-07-31 14:29:55 +0000
+++ account_statement_base_completion/__openerp__.py 2013-06-10 07:05:32 +0000
@@ -24,7 +24,7 @@
24 'author': 'Camptocamp',24 'author': 'Camptocamp',
25 'maintainer': 'Camptocamp',25 'maintainer': 'Camptocamp',
26 'category': 'Finance',26 'category': 'Finance',
27 'complexity': 'normal', #easy, normal, expert27 'complexity': 'normal',
28 'depends': ['account_statement_ext'],28 'depends': ['account_statement_ext'],
29 'description': """29 'description': """
30 The goal of this module is to improve the basic bank statement, help dealing with huge volume of30 The goal of this module is to improve the basic bank statement, help dealing with huge volume of
@@ -52,6 +52,10 @@
5252
53 You can use it with our account_advanced_reconcile module to automatize the reconciliation process.53 You can use it with our account_advanced_reconcile module to automatize the reconciliation process.
5454
55
56 TODO: The rules that look for invoices to find out the partner should take back the payable / receivable
57 account from there directly instead of retrieving it from partner properties !
58
55 """,59 """,
56 'website': 'http://www.camptocamp.com',60 'website': 'http://www.camptocamp.com',
57 'init_xml': [],61 'init_xml': [],
@@ -67,5 +71,4 @@
67 'images': [],71 'images': [],
68 'auto_install': False,72 'auto_install': False,
69 'license': 'AGPL-3',73 'license': 'AGPL-3',
70 'active': False,
71}74}
7275
=== modified file 'account_statement_base_completion/data.xml'
--- account_statement_base_completion/data.xml 2012-06-26 09:21:35 +0000
+++ account_statement_base_completion/data.xml 2013-06-10 07:05:32 +0000
@@ -7,7 +7,7 @@
7 <field name="sequence">60</field>7 <field name="sequence">60</field>
8 <field name="function_to_call">get_from_label_and_partner_field</field>8 <field name="function_to_call">get_from_label_and_partner_field</field>
9 </record>9 </record>
10 10
11 <record id="bank_statement_completion_rule_3" model="account.statement.completion.rule">11 <record id="bank_statement_completion_rule_3" model="account.statement.completion.rule">
12 <field name="name">Match from line label (based on partner name)</field>12 <field name="name">Match from line label (based on partner name)</field>
13 <field name="sequence">70</field>13 <field name="sequence">70</field>
@@ -26,7 +26,12 @@
26 <field name="function_to_call">get_from_ref_and_invoice</field>26 <field name="function_to_call">get_from_ref_and_invoice</field>
27 </record>27 </record>
2828
2929 <record id="bank_statement_completion_rule_5" model="account.statement.completion.rule">
30 30 <field name="name">Match from line reference (based on Invoice Supplier number)</field>
31 <field name="sequence">45</field>
32 <field name="function_to_call">get_from_ref_and_supplier_invoice</field>
33 </record>
34
35
31</data>36</data>
32</openerp>37</openerp>
3338
=== modified file 'account_statement_base_completion/partner.py'
--- account_statement_base_completion/partner.py 2012-06-20 14:10:01 +0000
+++ account_statement_base_completion/partner.py 2013-06-10 07:05:32 +0000
@@ -1,4 +1,4 @@
1# -*- encoding: utf-8 -*-1# -*- coding: utf-8 -*-
2#################################################################################2#################################################################################
3# #3# #
4# Copyright (C) 2011 Akretion & Camptocamp4# Copyright (C) 2011 Akretion & Camptocamp
@@ -19,9 +19,11 @@
19# #19# #
20#################################################################################20#################################################################################
2121
22from osv import fields, osv22from openerp.osv.orm import Model
2323from openerp.osv import fields, osv
24class res_partner(osv.osv):24
25
26class res_partner(Model):
25 """27 """
26 Add a bank label on the partner so that we can use it to match28 Add a bank label on the partner so that we can use it to match
27 this partner when we found this in a statement line.29 this partner when we found this in a statement line.
@@ -29,10 +31,8 @@
29 _inherit = 'res.partner'31 _inherit = 'res.partner'
3032
31 _columns = {33 _columns = {
32 'bank_statement_label':fields.char('Bank Statement Label', size=100,34 'bank_statement_label': fields.char('Bank Statement Label', size=100,
33 help="Enter the various label found on your bank statement separated by a ; If \35 help="Enter the various label found on your bank statement separated by a ; If \
34 one of this label is include in the bank statement line, the partner will be automatically \36 one of this label is include in the bank statement line, the partner will be automatically \
35 filled (as long as you use this method/rules in your statement profile)."),37 filled (as long as you use this method/rules in your statement profile)."),
36 }38 }
37
38res_partner()
3939
=== modified file 'account_statement_base_completion/statement.py'
--- account_statement_base_completion/statement.py 2012-12-13 13:57:29 +0000
+++ account_statement_base_completion/statement.py 2013-06-10 07:05:32 +0000
@@ -18,14 +18,22 @@
18# along with this program. If not, see <http://www.gnu.org/licenses/>.18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#19#
20##############################################################################20##############################################################################
21# TODO replace customer supplier by package constant
22import traceback
23import sys
24import logging
25
26from collections import defaultdict
27import re
21from tools.translate import _28from tools.translate import _
22import netsvc29from openerp.osv import osv, orm, fields
23logger = netsvc.Logger()30from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
24from openerp.osv.orm import Model, fields31from operator import attrgetter
25from openerp.osv import fields, osv
26from operator import itemgetter, attrgetter
27import datetime32import datetime
2833
34_logger = logging.getLogger(__name__)
35
36
29class ErrorTooManyPartner(Exception):37class ErrorTooManyPartner(Exception):
30 """38 """
31 New Exception definition that is raised when more than one partner is matched by39 New Exception definition that is raised when more than one partner is matched by
@@ -33,233 +41,338 @@
33 """41 """
34 def __init__(self, value):42 def __init__(self, value):
35 self.value = value43 self.value = value
44
36 def __str__(self):45 def __str__(self):
37 return repr(self.value)46 return repr(self.value)
3847
3948 def __repr__(self):
40class AccountStatementProfil(Model):49 return repr(self.value)
50
51
52class AccountStatementProfil(orm.Model):
41 """53 """
42 Extend the class to add rules per profile that will match at least the partner,54 Extend the class to add rules per profile that will match at least the partner,
43 but it could also be used to match other values as well.55 but it could also be used to match other values as well.
44 """56 """
45 57
46 _inherit = "account.statement.profile"58 _inherit = "account.statement.profile"
47 59
48 _columns={60 _columns = {
49 # @Akretion : For now, we don't implement this features, but this would probably be there:61 # @Akretion: For now, we don't implement this features, but this would probably be there:
50 # 'auto_completion': fields.text('Auto Completion'),62 # 'auto_completion': fields.text('Auto Completion'),
51 # 'transferts_account_id':fields.many2one('account.account', 'Transferts Account'),63 # 'transferts_account_id':fields.many2one('account.account', 'Transferts Account'),
52 # => You can implement it in a module easily, we design it with your needs in mind 64 # => You can implement it in a module easily, we design it with your needs in mind
53 # as well !65 # as well!
54 66
55 'rule_ids':fields.many2many('account.statement.completion.rule', 67 'rule_ids': fields.many2many(
68 'account.statement.completion.rule',
56 string='Related statement profiles',69 string='Related statement profiles',
57 rel='as_rul_st_prof_rel', 70 rel='as_rul_st_prof_rel'),
58 ),
59 }71 }
60 72
61 def find_values_from_rules(self, cr, uid, id, line_id, context=None):73 def _get_callable(self, cr, uid, profile, context=None):
74 if isinstance(profile, (int, long)):
75 prof = self.browse(cr, uid, profile, context=context)
76 else:
77 prof = profile
78 # We need to respect the sequence order
79 sorted_array = sorted(prof.rule_ids, key=attrgetter('sequence'))
80 return tuple((x.function_to_call for x in sorted_array))
81
82 def _find_values_from_rules(self, cr, uid, calls, line, context=None):
62 """83 """
63 This method will execute all related rules, in their sequence order, 84 This method will execute all related rules, in their sequence order,
64 to retrieve all the values returned by the first rules that will match.85 to retrieve all the values returned by the first rules that will match.
65 86 :param calls: list of lookup function name available in rules
66 :param int/long line_id: id of the concerned account.bank.statement.line87 :param dict line: read of the concerned account.bank.statement.line
67 :return:88 :return:
68 A dict of value that can be passed directly to the write method of89 A dict of value that can be passed directly to the write method of
69 the statement line or {}90 the statement line or {}
70 {'partner_id': value,91 {'partner_id': value,
71 'account_id' : value,92 'account_id: value,
72 93
73 ...}94 ...}
74 """95 """
75 if not context:96 if context is None:
76 context={}97 context = {}
77 res = {}98 if not calls:
99 calls = self._get_callable(cr, uid, line['profile_id'], context=context)
78 rule_obj = self.pool.get('account.statement.completion.rule')100 rule_obj = self.pool.get('account.statement.completion.rule')
79 profile = self.browse(cr, uid, id, context=context)101
80 # We need to respect the sequence order102 for call in calls:
81 sorted_array = sorted(profile.rule_ids, key=attrgetter('sequence'))103 method_to_call = getattr(rule_obj, call)
82 for rule in sorted_array:104 result = method_to_call(cr, uid, line, context)
83 method_to_call = getattr(rule_obj, rule.function_to_call)
84 result = method_to_call(cr,uid,line_id,context)
85 if result:105 if result:
106 result['already_completed'] = True
86 return result107 return result
87 return res108 return None
88 109
89110
90class AccountStatementCompletionRule(Model):111class AccountStatementCompletionRule(orm.Model):
91 """112 """
92 This will represent all the completion method that we can have to113 This will represent all the completion method that we can have to
93 fullfill the bank statement lines. You'll be able to extend them in you own module114 fullfill the bank statement lines. You'll be able to extend them in you own module
94 and choose those to apply for every statement profile.115 and choose those to apply for every statement profile.
95 The goal of a rule is to fullfill at least the partner of the line, but116 The goal of a rule is to fullfill at least the partner of the line, but
96 if possible also the reference because we'll use it in the reconciliation 117 if possible also the reference because we'll use it in the reconciliation
97 process. The reference should contain the invoice number or the SO number118 process. The reference should contain the invoice number or the SO number
98 or any reference that will be matched by the invoice accounting move.119 or any reference that will be matched by the invoice accounting move.
99 """120 """
100 121
101 _name = "account.statement.completion.rule"122 _name = "account.statement.completion.rule"
102 _order = "sequence asc"123 _order = "sequence asc"
103 124
104 def _get_functions(self, cr, uid, context=None):125 def _get_functions(self, cr, uid, context=None):
105 """126 """
106 List of available methods for rules. Override this to add you own.127 List of available methods for rules. Override this to add you own.
107 """128 """
108 return [129 return [
109 ('get_from_ref_and_invoice', 'From line reference (based on invoice number)'),130 ('get_from_ref_and_invoice', 'From line reference (based on customer invoice number)'),
131 ('get_from_ref_and_supplier_invoice', 'From line reference (based on supplier invoice number)'),
110 ('get_from_ref_and_so', 'From line reference (based on SO number)'),132 ('get_from_ref_and_so', 'From line reference (based on SO number)'),
111 ('get_from_label_and_partner_field', 'From line label (based on partner field)'),133 ('get_from_label_and_partner_field', 'From line label (based on partner field)'),
112 ('get_from_label_and_partner_name', 'From line label (based on partner name)'),134 ('get_from_label_and_partner_name', 'From line label (based on partner name)')]
113 ]135
114 136 _columns = {
115 _columns={
116 'sequence': fields.integer('Sequence', help="Lower means parsed first."),137 'sequence': fields.integer('Sequence', help="Lower means parsed first."),
117 'name': fields.char('Name', size=128),138 'name': fields.char('Name', size=128),
118 'profile_ids': fields.many2many('account.statement.profile', 139 'profile_ids': fields.many2many(
119 rel='as_rul_st_prof_rel', 140 'account.statement.profile',
141 rel='as_rul_st_prof_rel',
120 string='Related statement profiles'),142 string='Related statement profiles'),
121 'function_to_call': fields.selection(_get_functions, 'Method'),143 'function_to_call': fields.selection(_get_functions, 'Method'),
122 }144 }
123 145
124 def get_from_ref_and_invoice(self, cursor, uid, line_id, context=None):146 def _find_invoice(self, cr, uid, st_line, inv_type, context=None):
125 """147 """Find invoice related to statement line"""
126 Match the partner based on the invoice number and the reference of the statement 148 inv_obj = self.pool.get('account.invoice')
127 line. Then, call the generic get_values_for_line method to complete other values.149 if inv_type == 'supplier':
128 If more than one partner matched, raise the ErrorTooManyPartner error.150 type_domain = ('in_invoice', 'in_refund')
129151 number_field = 'supplier_invoice_number'
130 :param int/long line_id: id of the concerned account.bank.statement.line152 elif inv_type == 'customer':
131 :return:153 type_domain = ('out_invoice', 'out_refund')
132 A dict of value that can be passed directly to the write method of154 number_field = 'number'
133 the statement line or {}155 else:
134 {'partner_id': value,156 raise osv.except_osv(_('System error'),
135 'account_id' : value,157 _('Invalid invoice type for completion: %') % inv_type)
136 158
137 ...}159 inv_id = inv_obj.search(cr, uid,
138 """160 [(number_field, '=', st_line['ref'].strip()),
139 st_obj = self.pool.get('account.bank.statement.line')161 ('type', 'in', type_domain)],
140 st_line = st_obj.browse(cursor,uid,line_id)162 context=context)
163 if inv_id:
164 if len(inv_id) == 1:
165 inv = inv_obj.browse(cr, uid, inv_id[0], context=context)
166 else:
167 raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more '
168 'than one partner while looking on %s invoices') %
169 (st_line['name'], st_line['ref'], inv_type))
170 return inv
171 return False
172
173 def _from_invoice(self, cr, uid, line, inv_type, context):
174 """Populate statement line values"""
175 if not inv_type in ('supplier', 'customer'):
176 raise osv.except_osv(_('System error'),
177 _('Invalid invoice type for completion: %') % inv_type)
141 res = {}178 res = {}
142 if st_line:179 inv = self._find_invoice(cr, uid, line, inv_type, context=context)
143 inv_obj = self.pool.get('account.invoice')180 if inv:
144 inv_id = inv_obj.search(cursor, uid, [('number', '=', st_line.ref)])181 res = {'partner_id': inv.partner_id.id,
145 if inv_id:182 'account_id': inv.account_id.id,
146 if inv_id and len(inv_id) == 1:183 'type': inv_type}
147 inv = inv_obj.browse(cursor, uid, inv_id[0])184 override_acc = line['master_account_id']
148 res['partner_id'] = inv.partner_id.id185 if override_acc:
149 elif inv_id and len(inv_id) > 1:186 res['account_id'] = override_acc
150 raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more than one partner.')%(st_line.name,st_line.ref))
151 st_vals = st_obj.get_values_for_line(cursor, uid, profile_id = st_line.statement_id.profile_id.id,
152 partner_id = res.get('partner_id',False), line_type = st_line.type, amount = st_line.amount, context = context)
153 res.update(st_vals)
154 return res187 return res
155188
156 def get_from_ref_and_so(self, cursor, uid, line_id, context=None):189 # Should be private but data are initialised with no update XML
157 """190 def get_from_ref_and_supplier_invoice(self, cr, uid, line, context=None):
158 Match the partner based on the SO number and the reference of the statement 191 """
159 line. Then, call the generic get_values_for_line method to complete other values. 192 Match the partner based on the invoice supplier invoice number and the reference of the statement
160 If more than one partner matched, raise the ErrorTooManyPartner error.193 line. Then, call the generic get_values_for_line method to complete other values.
161194 If more than one partner matched, raise the ErrorTooManyPartner error.
162 :param int/long line_id: id of the concerned account.bank.statement.line195
163 :return:196 :param dict line: read of the concerned account.bank.statement.line
164 A dict of value that can be passed directly to the write method of197 :return:
165 the statement line or {}198 A dict of value that can be passed directly to the write method of
166 {'partner_id': value,199 the statement line or {}
167 'account_id' : value,200 {'partner_id': value,
168 201 'account_id': value,
202
203 ...}
204 """
205 return self._from_invoice(cr, uid, line, 'supplier', context=context)
206
207 # Should be private but data are initialised with no update XML
208 def get_from_ref_and_invoice(self, cr, uid, line, context=None):
209 """
210 Match the partner based on the invoice number and the reference of the statement
211 line. Then, call the generic get_values_for_line method to complete other values.
212 If more than one partner matched, raise the ErrorTooManyPartner error.
213
214 :param dict line: read of the concerned account.bank.statement.line
215 :return:
216 A dict of value that can be passed directly to the write method of
217 the statement line or {}
218 {'partner_id': value,
219 'account_id': value,
220 ...}
221 """
222 return self._from_invoice(cr, uid, line, 'customer', context=context)
223
224 # Should be private but data are initialised with no update XML
225 def get_from_ref_and_so(self, cr, uid, st_line, context=None):
226 """
227 Match the partner based on the SO number and the reference of the statement
228 line. Then, call the generic get_values_for_line method to complete other values.
229 If more than one partner matched, raise the ErrorTooManyPartner error.
230
231 :param int/long st_line: read of the concerned account.bank.statement.line
232 :return:
233 A dict of value that can be passed directly to the write method of
234 the statement line or {}
235 {'partner_id': value,
236 'account_id': value,
237
169 ...}238 ...}
170 """239 """
171 st_obj = self.pool.get('account.bank.statement.line')240 st_obj = self.pool.get('account.bank.statement.line')
172 st_line = st_obj.browse(cursor,uid,line_id)
173 res = {}241 res = {}
174 if st_line:242 if st_line:
175 so_obj = self.pool.get('sale.order')243 so_obj = self.pool.get('sale.order')
176 so_id = so_obj.search(cursor, uid, [('name', '=', st_line.ref)])244 so_id = so_obj.search(cr,
245 uid,
246 [('name', '=', st_line['ref'])],
247 context=context)
177 if so_id:248 if so_id:
178 if so_id and len(so_id) == 1:249 if so_id and len(so_id) == 1:
179 so = so_obj.browse(cursor, uid, so_id[0])250 so = so_obj.browse(cr, uid, so_id[0], context=context)
180 res['partner_id'] = so.partner_id.id251 res['partner_id'] = so.partner_id.id
181 elif so_id and len(so_id) > 1:252 elif so_id and len(so_id) > 1:
182 raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more than one partner.')%(st_line.name,st_line.ref))253 raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more '
183 st_vals = st_obj.get_values_for_line(cursor, uid, profile_id = st_line.statement_id.profile_id.id,254 'than one partner while looking on SO by ref.') %
184 partner_id = res.get('partner_id',False), line_type = st_line.type, amount = st_line.amount, context = context)255 (st_line['name'], st_line['ref']))
256 st_vals = st_obj.get_values_for_line(cr,
257 uid,
258 profile_id=st_line['profile_id'],
259 master_account_id=st_line['master_account_id'],
260 partner_id=res.get('partner_id', False),
261 line_type='customer',
262 amount=st_line['amount'] if st_line['amount'] else 0.0,
263 context=context)
185 res.update(st_vals)264 res.update(st_vals)
186 return res265 return res
187
188266
189 def get_from_label_and_partner_field(self, cursor, uid, line_id, context=None):267 # Should be private but data are initialised with no update XML
268 def get_from_label_and_partner_field(self, cr, uid, st_line, context=None):
190 """269 """
191 Match the partner based on the label field of the statement line270 Match the partner based on the label field of the statement line
192 and the text defined in the 'bank_statement_label' field of the partner.271 and the text defined in the 'bank_statement_label' field of the partner.
193 Remember that we can have values separated with ; Then, call the generic 272 Remember that we can have values separated with ; Then, call the generic
194 get_values_for_line method to complete other values.273 get_values_for_line method to complete other values.
195 If more than one partner matched, raise the ErrorTooManyPartner error.274 If more than one partner matched, raise the ErrorTooManyPartner error.
196275
197 :param int/long line_id: id of the concerned account.bank.statement.line276 :param dict st_line: read of the concerned account.bank.statement.line
198 :return:277 :return:
199 A dict of value that can be passed directly to the write method of278 A dict of value that can be passed directly to the write method of
200 the statement line or {}279 the statement line or {}
201 {'partner_id': value,280 {'partner_id': value,
202 'account_id' : value,281 'account_id': value,
203 282
204 ...}283 ...}
205 """284 """
206 partner_obj = self.pool.get('res.partner')285 partner_obj = self.pool.get('res.partner')
207 st_obj = self.pool.get('account.bank.statement.line')286 st_obj = self.pool.get('account.bank.statement.line')
208 st_line = st_obj.browse(cursor,uid,line_id)
209 res = {}287 res = {}
210 compt = 0288 # As we have to iterate on each partner for each line,
211 if st_line:289 # we memoize the pair to avoid
212 ids = partner_obj.search(cursor, uid, [['bank_statement_label', '!=', False]], context=context)290 # to redo computation for each line.
213 for partner in partner_obj.browse(cursor, uid, ids, context=context):291 # Following code can be done by a single SQL query
214 for partner_label in partner.bank_statement_label.split(';'):292 # but this option is not really maintanable
215 if partner_label in st_line.label:293 if not context.get('label_memoizer'):
216 compt += 1294 context['label_memoizer'] = defaultdict(list)
217 res['partner_id'] = partner.id295 partner_ids = partner_obj.search(cr,
218 if compt > 1:296 uid,
219 raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more than one partner.')%(st_line.name,st_line.ref))297 [('bank_statement_label', '!=', False)])
220 if res:298 line_ids = context.get('line_ids', [])
221 st_vals = st_obj.get_values_for_line(cursor, uid, profile_id = st_line.statement_id.profile_id.id,299 for partner in partner_obj.browse(cr, uid, partner_ids, context=context):
222 partner_id = res.get('partner_id',False), line_type = st_line.type, amount = st_line.amount, context = context)300 vals = '|'.join(re.escape(x.strip()) for x in partner.bank_statement_label.split(';'))
223 res.update(st_vals)301 or_regex = ".*%s.*" % vals
302 sql = ("SELECT id from account_bank_statement_line"
303 " WHERE id in %s"
304 " AND name ~* %s")
305 cr.execute(sql, (line_ids, or_regex))
306 pairs = cr.fetchall()
307 for pair in pairs:
308 context['label_memoizer'][pair[0]].append(partner)
309
310 if st_line['id'] in context['label_memoizer']:
311 found_partner = context['label_memoizer'][st_line['id']]
312 if len(found_partner) > 1:
313 msg = (_('Line named "%s" (Ref:%s) was matched by '
314 'more than one partner while looking on partner label: %s') %
315 (st_line['name'], st_line['ref'], ','.join([x.name for x in found_partner])))
316 raise ErrorTooManyPartner(msg)
317 res['partner_id'] = found_partner[0].id
318 st_vals = st_obj.get_values_for_line(cr,
319 uid,
320 profile_id=st_line['profile_id'],
321 master_account_id=st_line['master_account_id'],
322 partner_id=found_partner[0].id,
323 line_type=False,
324 amount=st_line['amount'] if st_line['amount'] else 0.0,
325 context=context)
326 res.update(st_vals)
224 return res327 return res
225328
226 def get_from_label_and_partner_name(self, cursor, uid, line_id, context=None):329 def get_from_label_and_partner_name(self, cr, uid, st_line, context=None):
227 """330 """
228 Match the partner based on the label field of the statement line331 Match the partner based on the label field of the statement line
229 and the name of the partner.332 and the name of the partner.
230 Then, call the generic get_values_for_line method to complete other values.333 Then, call the generic get_values_for_line method to complete other values.
231 If more than one partner matched, raise the ErrorTooManyPartner error.334 If more than one partner matched, raise the ErrorTooManyPartner error.
232335
233 :param int/long line_id: id of the concerned account.bank.statement.line336 :param dict st_line: read of the concerned account.bank.statement.line
234 :return:337 :return:
235 A dict of value that can be passed directly to the write method of338 A dict of value that can be passed directly to the write method of
236 the statement line or {}339 the statement line or {}
237 {'partner_id': value,340 {'partner_id': value,
238 'account_id' : value,341 'account_id': value,
239 342
240 ...}343 ...}
241 """344 """
242 # This Method has not been tested yet !
243 res = {}345 res = {}
346 # We memoize allowed partner
347 if not context.get('partner_memoizer'):
348 context['partner_memoizer'] = tuple(self.pool['res.partner'].search(cr, uid, []))
349 if not context['partner_memoizer']:
350 return res
244 st_obj = self.pool.get('account.bank.statement.line')351 st_obj = self.pool.get('account.bank.statement.line')
245 st_line = st_obj.browse(cursor,uid,line_id)352 sql = "SELECT id FROM res_partner WHERE name ~* %s and id in %s"
246 if st_line:353 pattern = ".*%s.*" % re.escape(st_line['name'])
247 sql = "SELECT id FROM res_partner WHERE name ~* %s"354 cr.execute(sql, (pattern, context['partner_memoizer']))
248 pattern = ".*%s.*" % st_line.label355 result = cr.fetchall()
249 cursor.execute(sql, (pattern,))356 if not result:
250 result = cursor.fetchall()357 return res
251 if len(result) > 1:358 if len(result) > 1:
252 raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more than one partner.')%(st_line.name,st_line.ref))359 raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more '
253 for id in result[0]:360 'than one partner while looking on partner by name') %
254 res['partner_id'] = id361 (st_line['name'], st_line['ref']))
255 if res:362 res['partner_id'] = result[0][0]
256 st_vals = st_obj.get_values_for_line(cursor, uid, profile_id = st_line.statement_id.profile_id.id,363 st_vals = st_obj.get_values_for_line(cr,
257 partner_id = res.get('partner_id',False), line_type = st_line.type, amount = st_line.amount, context = context)364 uid,
258 res.update(st_vals)365 profile_id=st_line['porfile_id'],
366 master_account_id=st_line['master_account_id'],
367 partner_id=res['partner_id'],
368 line_type=False,
369 amount=st_line['amount'] if st_line['amount'] else 0.0,
370 context=context)
371 res.update(st_vals)
259 return res372 return res
260 373
261 374
262class AccountStatementLine(Model):375class AccountStatementLine(orm.Model):
263 """376 """
264 Add sparse field on the statement line to allow to store all the377 Add sparse field on the statement line to allow to store all the
265 bank infos that are given by a bank/office. You can then add you own in your378 bank infos that are given by a bank/office. You can then add you own in your
@@ -270,23 +383,29 @@
270 """383 """
271 _inherit = "account.bank.statement.line"384 _inherit = "account.bank.statement.line"
272385
273 _columns={386 _columns = {
274 'additionnal_bank_fields' : fields.serialized('Additionnal infos from bank', 387 'additionnal_bank_fields': fields.serialized(
275 help="Used by completion and import system. Adds every field that is present in your bank/office \388 'Additionnal infos from bank',
276 statement file"),389 help="Used by completion and import system. Adds every field that "
277 'label': fields.sparse(type='char', string='Label', 390 "is present in your bank/office statement file"),
278 serialization_field='additionnal_bank_fields', 391 'label': fields.sparse(
279 help="Generiy field to store a label given from the bank/office on which we can \392 type='char',
280 base the default/standard providen rule."),393 string='Label',
281 'already_completed': fields.boolean("Auto-Completed",394 serialization_field='additionnal_bank_fields',
282 help="When this checkbox is ticked, the auto-completion process/button will ignore this line."),395 help="Generic field to store a label given from the "
396 "bank/office on which we can base the default/standard "
397 "providen rule."),
398 'already_completed': fields.boolean(
399 "Auto-Completed",
400 help="When this checkbox is ticked, the auto-completion "
401 "process/button will ignore this line."),
283 }402 }
403
284 _defaults = {404 _defaults = {
285 'already_completed': False,405 'already_completed': False,
286 }406 }
287 407
288 408 def _get_line_values_from_rules(self, cr, uid, line, rules, context=None):
289 def get_line_values_from_rules(self, cr, uid, ids, context=None):
290 """409 """
291 We'll try to find out the values related to the line based on rules setted on410 We'll try to find out the values related to the line based on rules setted on
292 the profile.. We will ignore line for which already_completed is ticked.411 the profile.. We will ignore line for which already_completed is ticked.
@@ -294,31 +413,20 @@
294 :return:413 :return:
295 A dict of dict value that can be passed directly to the write method of414 A dict of dict value that can be passed directly to the write method of
296 the statement line or {}. The first dict has statement line ID as a key:415 the statement line or {}. The first dict has statement line ID as a key:
297 {117009: {'partner_id': 100997, 'account_id': 489L}} 416 {117009: {'partner_id': 100997, 'account_id': 489L}}
298 """417 """
299 profile_obj = self.pool.get('account.statement.profile')418 profile_obj = self.pool.get('account.statement.profile')
300 st_obj = self.pool.get('account.bank.statement.line')419 if line.get('already_completed'):
301 res={}420 return {}
302 errors_stack = []421 # Ask the rule
303 for line in self.browse(cr,uid, ids, context):422 vals = profile_obj._find_values_from_rules(cr, uid, rules, line, context)
304 if not line.already_completed:423 if vals:
305 try:424 vals['id'] = line['id']
306 # Take the default values425 return vals
307 res[line.id] = st_obj.get_values_for_line(cr, uid, profile_id = line.statement_id.profile_id.id,426 return {}
308 line_type = line.type, amount = line.amount, context = context)427
309 # Ask the rule428
310 vals = profile_obj.find_values_from_rules(cr, uid, line.statement_id.profile_id.id, line.id, context)429class AccountBankSatement(orm.Model):
311 # Merge the result
312 res[line.id].update(vals)
313 except ErrorTooManyPartner, exc:
314 msg = "Line ID %s had following error: %s" % (line.id, exc.value)
315 errors_stack.append(msg)
316 if errors_stack:
317 msg = u"\n".join(errors_stack)
318 raise ErrorTooManyPartner(msg)
319 return res
320
321class AccountBankSatement(Model):
322 """430 """
323 We add a basic button and stuff to support the auto-completion431 We add a basic button and stuff to support the auto-completion
324 of the bank statement once line have been imported or manually fullfill.432 of the bank statement once line have been imported or manually fullfill.
@@ -328,61 +436,92 @@
328 _columns = {436 _columns = {
329 'completion_logs': fields.text('Completion Log', readonly=True),437 'completion_logs': fields.text('Completion Log', readonly=True),
330 }438 }
331 439
332 def write_completion_log(self, cr, uid, stat_id, error_msg, number_imported, context=None):440 def write_completion_log(self, cr, uid, stat_id, error_msg, number_imported, context=None):
333 """441 """
334 Write the log in the completion_logs field of the bank statement to let the user442 Write the log in the completion_logs field of the bank statement to let the user
335 know what have been done. This is an append mode, so we don't overwrite what443 know what have been done. This is an append mode, so we don't overwrite what
336 already recoded.444 already recoded.
337 445
338 :param int/long stat_id: ID of the account.bank.statement446 :param int/long stat_id: ID of the account.bank.statement
339 :param char error_msg: Message to add447 :param char error_msg: Message to add
340 :number_imported int/long: Number of lines that have been completed448 :number_imported int/long: Number of lines that have been completed
341 :return : True449 :return True
342
343 """450 """
344 error_log = ""451 user_name = self.pool.get('res.users').read(cr, uid, uid,
345 user_name = self.pool.get('res.users').read(cr, uid, uid, ['name'])['name']452 ['name'], context=context)['name']
346 log = self.read(cr, uid, stat_id, ['completion_logs'], context=context)['completion_logs']453
347 log_line = log and log.split("\n") or []454 log = self.read(cr, uid, stat_id, ['completion_logs'],
348 completion_date = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')455 context=context)['completion_logs']
349 if error_msg:456 log = log if log else ""
350 error_log = error_msg457
351 log_line[0:0] = [completion_date + ' : '458 completion_date = datetime.datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
352 + _("Bank Statement ID %s has %s lines completed by %s") %(stat_id, number_imported, user_name)459 message = (_("%s Bank Statement ID %s has %s lines completed by %s \n%s\n%s\n") %
353 + "\n" + error_log + "-------------" + "\n"]460 (completion_date, stat_id, number_imported, user_name, error_msg, log))
354 log = "\n".join(log_line)461 self.write(cr, uid, [stat_id], {'completion_logs': message}, context=context)
355 self.write(cr, uid, [stat_id], {'completion_logs' : log}, context=context)462
356 logger.notifyChannel('Bank Statement Completion', netsvc.LOG_INFO, 463 body = (_('Statement ID %s auto-completed for %s lines completed') %
357 "Bank Statement ID %s has %s lines completed"%(stat_id, number_imported))464 (stat_id, number_imported)),
465 self.message_post(cr, uid,
466 [stat_id],
467 body=body,
468 context=context)
358 return True469 return True
359 470
360 def button_auto_completion(self, cr, uid, ids, context=None):471 def button_auto_completion(self, cr, uid, ids, context=None):
361 """472 """
362 Complete line with values given by rules and tic the already_completed473 Complete line with values given by rules and tic the already_completed
363 checkbox so we won't compute them again unless the user untick them !474 checkbox so we won't compute them again unless the user untick them!
364 """475 """
365 if not context:476 if context is None:
366 context={}477 context = {}
367 stat_line_obj = self.pool.get('account.bank.statement.line')478 stat_line_obj = self.pool['account.bank.statement.line']
368 msg = ""479 profile_obj = self.pool.get('account.statement.profile')
369 compl_lines = 0480 compl_lines = 0
481 stat_line_obj.check_access_rule(cr, uid, [], 'create')
482 stat_line_obj.check_access_rights(cr, uid, 'create', raise_exception=True)
370 for stat in self.browse(cr, uid, ids, context=context):483 for stat in self.browse(cr, uid, ids, context=context):
484 msg_lines = []
371 ctx = context.copy()485 ctx = context.copy()
372 for line in stat.line_ids:486 ctx['line_ids'] = tuple((x.id for x in stat.line_ids))
373 res = {}487 b_profile = stat.profile_id
488 rules = profile_obj._get_callable(cr, uid, b_profile, context=context)
489 profile_id = b_profile.id # Only for perfo even it gains almost nothing
490 master_account_id = b_profile.receivable_account_id
491 master_account_id = master_account_id.id if master_account_id else False
492 res = False
493 for line in stat_line_obj.read(cr, uid, ctx['line_ids']):
374 try:494 try:
375 res = stat_line_obj.get_line_values_from_rules(cr, uid, [line.id], context=ctx)495 # performance trick
496 line['master_account_id'] = master_account_id
497 line['profile_id'] = profile_id
498 res = stat_line_obj._get_line_values_from_rules(cr, uid, line,
499 rules, context=ctx)
376 if res:500 if res:
377 compl_lines += 1501 compl_lines += 1
378 except ErrorTooManyPartner, exc:502 except ErrorTooManyPartner, exc:
379 msg += exc.value + "\n"503 msg_lines.append(repr(exc))
380 except Exception, exc:504 except Exception, exc:
381 msg += exc.value + "\n"505 msg_lines.append(repr(exc))
382 # vals = res and res.keys() or False506 error_type, error_value, trbk = sys.exc_info()
507 st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value)
508 st += ''.join(traceback.format_tb(trbk, 30))
509 _logger.error(st)
383 if res:510 if res:
384 vals = res[line.id]511 #stat_line_obj.write(cr, uid, [line.id], vals, context=ctx)
385 vals['already_completed'] = True512 try:
386 stat_line_obj.write(cr, uid, line.id, vals, context=ctx)513 stat_line_obj._update_line(cr, uid, res, context=context)
387 self.write_completion_log(cr, uid, stat.id, msg, compl_lines, context=context)514 except Exception as exc:
515 msg_lines.append(repr(exc))
516 error_type, error_value, trbk = sys.exc_info()
517 st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value)
518 st += ''.join(traceback.format_tb(trbk, 30))
519 _logger.error(st)
520 # we can commit as it is not needed to be atomic
521 # commiting here adds a nice perfo boost
522 if not compl_lines % 500:
523 cr.commit()
524 msg = u'\n'.join(msg_lines)
525 self.write_completion_log(cr, uid, stat.id,
526 msg, compl_lines, context=context)
388 return True527 return True
389528
=== modified file 'account_statement_base_completion/statement_view.xml'
--- account_statement_base_completion/statement_view.xml 2012-08-02 12:46:12 +0000
+++ account_statement_base_completion/statement_view.xml 2013-06-10 07:05:32 +0000
@@ -10,21 +10,21 @@
10 <field name="type">form</field>10 <field name="type">form</field>
11 <field name="arch" type="xml">11 <field name="arch" type="xml">
12 <data>12 <data>
13 <xpath expr="/form/notebook/page/field[@name='line_ids']/form/field[@name='sequence']" position="after">13 <xpath expr="/form/sheet/notebook/page/field[@name='line_ids']/form/group/field[@name='sequence']" position="after">
14 <separator colspan="4" string="Importation related infos"/>14 <separator colspan="4" string="Importation related infos"/>
15 <field name="label" />15 <field name="label" />
16 <field name="already_completed" />16 <field name="already_completed" />
17 </xpath>17 </xpath>
1818
19 <xpath expr="/form/group[2]" position="attributes">19 <!-- <xpath expr="/form/group[2]" position="attributes">
20 <attribute name="col">10</attribute>20 <attribute name="col">10</attribute>
21 </xpath>21 </xpath> -->
2222
23 <xpath expr="/form/group/field[@name='balance_end']" position="after">23 <xpath expr="/form/sheet/div[@name='import_buttons']" position="after">
24 <button name="button_auto_completion" string="Auto Completion" states='draft,open' type="object" colspan="1"/>24 <button name="button_auto_completion" string="Auto Completion" states='draft,open' type="object" colspan="1"/>
25 </xpath>25 </xpath>
26 26
27 <xpath expr="/form/notebook/page[@string='Journal Entries']" position="after">27 <xpath expr="/form/sheet/notebook/page[@string='Transactions']" position="after">
28 <page string="Completion Logs" attrs="{'invisible':[('completion_logs','=',False)]}">28 <page string="Completion Logs" attrs="{'invisible':[('completion_logs','=',False)]}">
29 <field name="completion_logs" colspan="4" nolabel="1" attrs="{'invisible':[('completion_logs','=',False)]}"/>29 <field name="completion_logs" colspan="4" nolabel="1" attrs="{'invisible':[('completion_logs','=',False)]}"/>
30 </page>30 </page>
@@ -40,7 +40,7 @@
40 <field name="type">form</field>40 <field name="type">form</field>
41 <field name="arch" type="xml">41 <field name="arch" type="xml">
42 <data>42 <data>
43 <xpath expr="/form/notebook/page/field[@name='line_ids']/tree/field[@name='amount']" position="after">43 <xpath expr="/form/sheet/notebook/page/field[@name='line_ids']/tree/field[@name='amount']" position="after">
44 <field name="already_completed" />44 <field name="already_completed" />
45 </xpath>45 </xpath>
46 </data>46 </data>
4747
=== modified file 'account_statement_base_import/__init__.py'
--- account_statement_base_import/__init__.py 2012-06-20 14:10:01 +0000
+++ account_statement_base_import/__init__.py 2013-06-10 07:05:32 +0000
@@ -20,4 +20,4 @@
20##############################################################################20##############################################################################
21import parser21import parser
22import wizard22import wizard
23import statement
24\ No newline at end of file23\ No newline at end of file
24import statement
2525
=== modified file 'account_statement_base_import/__openerp__.py'
--- account_statement_base_import/__openerp__.py 2012-08-02 12:46:12 +0000
+++ account_statement_base_import/__openerp__.py 2013-06-10 07:05:32 +0000
@@ -24,8 +24,11 @@
24 'author': 'Camptocamp',24 'author': 'Camptocamp',
25 'maintainer': 'Camptocamp',25 'maintainer': 'Camptocamp',
26 'category': 'Finance',26 'category': 'Finance',
27 'complexity': 'normal', #easy, normal, expert27 'complexity': 'normal',
28 'depends': ['account_statement_ext','account_statement_base_completion'],28 'depends': [
29 'account_statement_ext',
30 'account_statement_base_completion'
31 ],
29 'description': """32 'description': """
30 This module brings basic methods and fields on bank statement to deal with33 This module brings basic methods and fields on bank statement to deal with
31 the importation of different bank and offices. A generic abstract method is defined and an34 the importation of different bank and offices. A generic abstract method is defined and an
@@ -35,6 +38,8 @@
35 a standard .csv or .xls file (you'll find it in the 'data' folder). It respects the profile38 a standard .csv or .xls file (you'll find it in the 'data' folder). It respects the profile
36 (provided by the accouhnt_statement_ext module) to pass the entries. That means,39 (provided by the accouhnt_statement_ext module) to pass the entries. That means,
37 you'll have to choose a file format for each profile.40 you'll have to choose a file format for each profile.
41 In order to achieve this it uses the `xlrd` Python module which you will need to install
42 separately in your environment.
3843
39 This module can handle a commission taken by the payment office and has the following format:44 This module can handle a commission taken by the payment office and has the following format:
4045
@@ -53,16 +58,13 @@
5358
54 """,59 """,
55 'website': 'http://www.camptocamp.com',60 'website': 'http://www.camptocamp.com',
56 'init_xml': [],61 'data': [
57 'update_xml': [
58 "wizard/import_statement_view.xml",62 "wizard/import_statement_view.xml",
59 "statement_view.xml",63 "statement_view.xml",
60 ],64 ],
61 'demo_xml': [],
62 'test': [],65 'test': [],
63 'installable': True,66 'installable': True,
64 'images': [],67 'images': [],
65 'auto_install': False,68 'auto_install': False,
66 'license': 'AGPL-3',69 'license': 'AGPL-3',
67 'active': False,
68}70}
6971
=== modified file 'account_statement_base_import/parser/__init__.py'
--- account_statement_base_import/parser/__init__.py 2012-06-20 14:10:01 +0000
+++ account_statement_base_import/parser/__init__.py 2013-06-10 07:05:32 +0000
@@ -22,4 +22,4 @@
22from parser import new_bank_statement_parser22from parser import new_bank_statement_parser
23from parser import BankStatementImportParser23from parser import BankStatementImportParser
24import file_parser24import file_parser
25import generic_file_parser
26\ No newline at end of file25\ No newline at end of file
26import generic_file_parser
2727
=== modified file 'account_statement_base_import/parser/file_parser.py'
--- account_statement_base_import/parser/file_parser.py 2012-11-26 10:23:58 +0000
+++ account_statement_base_import/parser/file_parser.py 2013-06-10 07:05:32 +0000
@@ -17,8 +17,8 @@
17# along with this program. If not, see <http://www.gnu.org/licenses/>.17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#18#
19##############################################################################19##############################################################################
20
21from openerp.tools.translate import _20from openerp.tools.translate import _
21from openerp.osv.osv import except_osv
22import tempfile22import tempfile
23import datetime23import datetime
24from parser import BankStatementImportParser24from parser import BankStatementImportParser
@@ -28,17 +28,19 @@
28except:28except:
29 raise Exception(_('Please install python lib xlrd'))29 raise Exception(_('Please install python lib xlrd'))
3030
31
31class FileParser(BankStatementImportParser):32class FileParser(BankStatementImportParser):
32 """33 """
33 Generic abstract class for defining parser for .csv or .xls file format.34 Generic abstract class for defining parser for .csv or .xls file format.
34 """35 """
35 36
36 def __init__(self, parse_name, keys_to_validate=[], ftype='csv', convertion_dict=None, header=None, *args, **kwargs):37 def __init__(self, parse_name, keys_to_validate=None, ftype='csv', conversion_dict=None,
38 header=None, *args, **kwargs):
37 """39 """
38 :param char: parse_name : The name of the parser40 :param char: parse_name : The name of the parser
39 :param list: keys_to_validate : contain the key that need to be present in the file41 :param list: keys_to_validate : contain the key that need to be present in the file
40 :param char ftype: extension of the file (could be csv or xls)42 :param char ftype: extension of the file (could be csv or xls)
41 :param: convertion_dict : keys and type to convert of every column in the file like 43 :param: conversion_dict : keys and type to convert of every column in the file like
42 {44 {
43 'ref': unicode,45 'ref': unicode,
44 'label': unicode,46 'label': unicode,
@@ -48,18 +50,19 @@
48 }50 }
49 :param list: header : specify header fields if the csv file has no header51 :param list: header : specify header fields if the csv file has no header
50 """52 """
51 53
52 super(FileParser, self).__init__(parse_name, *args, **kwargs)54 super(FileParser, self).__init__(parse_name, *args, **kwargs)
53 if ftype in ('csv', 'xls'):55 if ftype in ('csv', 'xls'):
54 self.ftype = ftype56 self.ftype = ftype
55 else:57 else:
56 raise Exception(_('Invalide file type %s. please use csv or xls') % (ftype))58 raise except_osv(_('User Error'),
57 self.keys_to_validate = keys_to_validate59 _('Invalid file type %s. Please use csv or xls') % ftype)
58 self.convertion_dict = convertion_dict60 self.keys_to_validate = keys_to_validate if keys_to_validate is not None else []
61 self.conversion_dict = conversion_dict
59 self.fieldnames = header62 self.fieldnames = header
60 self._datemode = 0 # used only for xls documents,63 self._datemode = 0 # used only for xls documents,
61 # 0 means Windows mode (1900 based dates).64 # 0 means Windows mode (1900 based dates).
62 # Set in _parse_xls, from the contents of the file65 # Set in _parse_xls, from the contents of the file
6366
64 def _custom_format(self, *args, **kwargs):67 def _custom_format(self, *args, **kwargs):
65 """68 """
@@ -78,7 +81,7 @@
78 Launch the parsing through .csv or .xls depending on the81 Launch the parsing through .csv or .xls depending on the
79 given ftype82 given ftype
80 """83 """
81 84
82 res = None85 res = None
83 if self.ftype == 'csv':86 if self.ftype == 'csv':
84 res = self._parse_csv()87 res = self._parse_csv()
@@ -98,7 +101,8 @@
98 parsed_cols = self.result_row_list[0].keys()101 parsed_cols = self.result_row_list[0].keys()
99 for col in self.keys_to_validate:102 for col in self.keys_to_validate:
100 if col not in parsed_cols:103 if col not in parsed_cols:
101 raise Exception(_('Column %s not present in file') % (col))104 raise except_osv(_('Invalid data'),
105 _('Column %s not present in file') % col)
102 return True106 return True
103107
104 def _post(self, *args, **kwargs):108 def _post(self, *args, **kwargs):
@@ -108,7 +112,6 @@
108 self.result_row_list = self._cast_rows(*args, **kwargs)112 self.result_row_list = self._cast_rows(*args, **kwargs)
109 return True113 return True
110114
111
112 def _parse_csv(self):115 def _parse_csv(self):
113 """116 """
114 :return: list of dict from csv file (line/rows)117 :return: list of dict from csv file (line/rows)
@@ -116,7 +119,7 @@
116 csv_file = tempfile.NamedTemporaryFile()119 csv_file = tempfile.NamedTemporaryFile()
117 csv_file.write(self.filebuffer)120 csv_file.write(self.filebuffer)
118 csv_file.flush()121 csv_file.flush()
119 with open(csv_file.name, 'rU') as fobj: 122 with open(csv_file.name, 'rU') as fobj:
120 reader = UnicodeDictReader(fobj, fieldnames=self.fieldnames)123 reader = UnicodeDictReader(fobj, fieldnames=self.fieldnames)
121 return list(reader)124 return list(reader)
122125
@@ -128,17 +131,13 @@
128 wb_file.write(self.filebuffer)131 wb_file.write(self.filebuffer)
129 # We ensure that cursor is at beginig of file132 # We ensure that cursor is at beginig of file
130 wb_file.seek(0)133 wb_file.seek(0)
131 wb = xlrd.open_workbook(wb_file.name)134 with xlrd.open_workbook(wb_file.name) as wb:
132 self._datemode = wb.datemode135 self._datemode = wb.datemode
133 sheet = wb.sheet_by_index(0)136 sheet = wb.sheet_by_index(0)
134 header = sheet.row_values(0)137 header = sheet.row_values(0)
135 res = []138 res = []
136 for rownum in range(1, sheet.nrows):139 for rownum in range(1, sheet.nrows):
137 res.append(dict(zip(header, sheet.row_values(rownum))))140 res.append(dict(zip(header, sheet.row_values(rownum))))
138 try:
139 wb_file.close()
140 except Exception, e:
141 pass #file is allready closed
142 return res141 return res
143142
144 def _from_csv(self, result_set, conversion_rules):143 def _from_csv(self, result_set, conversion_rules):
@@ -149,11 +148,30 @@
149 for line in result_set:148 for line in result_set:
150 for rule in conversion_rules:149 for rule in conversion_rules:
151 if conversion_rules[rule] == datetime.datetime:150 if conversion_rules[rule] == datetime.datetime:
152 date_string = line[rule].split(' ')[0]151 try:
153 line[rule] = datetime.datetime.strptime(date_string,152 date_string = line[rule].split(' ')[0]
154 '%Y-%m-%d')153 line[rule] = datetime.datetime.strptime(date_string,
154 '%Y-%m-%d')
155 except ValueError as err:
156 raise except_osv(_("Date format is not valid."),
157 _(" It should be YYYY-MM-DD for column: %s"
158 " value: %s \n \n"
159 " \n Please check the line with ref: %s"
160 " \n \n Detail: %s") % (rule,
161 line.get(rule, _('Missing')),
162 line.get('ref', line),
163 repr(err)))
155 else:164 else:
156 line[rule] = conversion_rules[rule](line[rule])165 try:
166 line[rule] = conversion_rules[rule](line[rule])
167 except Exception as err:
168 raise except_osv(_('Invalid data'),
169 _("Value %s of column %s is not valid."
170 "\n Please check the line with ref %s:"
171 "\n \n Detail: %s") % (line.get(rule, _('Missing')),
172 rule,
173 line.get('ref', line),
174 repr(err)))
157 return result_set175 return result_set
158176
159 def _from_xls(self, result_set, conversion_rules):177 def _from_xls(self, result_set, conversion_rules):
@@ -164,17 +182,37 @@
164 for line in result_set:182 for line in result_set:
165 for rule in conversion_rules:183 for rule in conversion_rules:
166 if conversion_rules[rule] == datetime.datetime:184 if conversion_rules[rule] == datetime.datetime:
167 t_tuple = xlrd.xldate_as_tuple(line[rule], self._datemode)185 try:
168 line[rule] = datetime.datetime(*t_tuple)186 t_tuple = xlrd.xldate_as_tuple(line[rule], self._datemode)
187 line[rule] = datetime.datetime(*t_tuple)
188 except Exception as err:
189 raise except_osv(_("Date format is not valid"),
190 _("Please modify the cell formatting to date format"
191 " for column: %s"
192 " value: %s"
193 "\n Please check the line with ref: %s"
194 "\n \n Detail: %s") % (rule,
195 line.get(rule, _('Missing')),
196 line.get('ref', line),
197 repr(err)))
169 else:198 else:
170 line[rule] = conversion_rules[rule](line[rule])199 try:
200 line[rule] = conversion_rules[rule](line[rule])
201 except Exception as err:
202 raise except_osv(_('Invalid data'),
203 _("Value %s of column %s is not valid."
204 "\n Please check the line with ref %s:"
205 "\n \n Detail: %s") % (line.get(rule, _('Missing')),
206 rule,
207 line.get('ref', line),
208 repr(err)))
171 return result_set209 return result_set
172210
173 def _cast_rows(self, *args, **kwargs):211 def _cast_rows(self, *args, **kwargs):
174 """212 """
175 Convert the self.result_row_list using the self.convertion_dict providen.213 Convert the self.result_row_list using the self.conversion_dict providen.
176 We call here _from_xls or _from_csv depending on the self.ftype variable.214 We call here _from_xls or _from_csv depending on the self.ftype variable.
177 """215 """
178 func = getattr(self, '_from_%s'%(self.ftype))216 func = getattr(self, '_from_%s' % self.ftype)
179 res = func(self.result_row_list, self.convertion_dict)217 res = func(self.result_row_list, self.conversion_dict)
180 return res218 return res
181219
=== modified file 'account_statement_base_import/parser/generic_file_parser.py'
--- account_statement_base_import/parser/generic_file_parser.py 2012-06-20 14:10:01 +0000
+++ account_statement_base_import/parser/generic_file_parser.py 2013-06-10 07:05:32 +0000
@@ -23,31 +23,37 @@
23import csv23import csv
24import tempfile24import tempfile
25import datetime25import datetime
26# from . import file_parser
27from file_parser import FileParser26from file_parser import FileParser
28try:27try:
29 import xlrd28 import xlrd
30except:29except:
31 raise Exception(_('Please install python lib xlrd'))30 raise Exception(_('Please install python lib xlrd'))
3231
32
33def float_or_zero(val):
34 """ Conversion function used to manage
35 empty string into float usecase"""
36 return float(val) if val else 0.0
37
38
33class GenericFileParser(FileParser):39class GenericFileParser(FileParser):
34 """40 """
35 Standard parser that use a define format in csv or xls to import into a41 Standard parser that use a define format in csv or xls to import into a
36 bank statement. This is mostely an example of how to proceed to create a new 42 bank statement. This is mostely an example of how to proceed to create a new
37 parser, but will also be useful as it allow to import a basic flat file.43 parser, but will also be useful as it allow to import a basic flat file.
38 """44 """
39 45
40 def __init__(self, parse_name, ftype='csv'):46 def __init__(self, parse_name, ftype='csv'):
41 convertion_dict = {47 conversion_dict = {
42 'ref': unicode,48 'ref': unicode,
43 'label': unicode,49 'label': unicode,
44 'date': datetime.datetime,50 'date': datetime.datetime,
45 'amount': float,51 'amount': float_or_zero,
46 'commission_amount': float52 'commission_amount': float_or_zero
47 }53 }
48 # Order of cols does not matter but first row of the file has to be header54 # Order of cols does not matter but first row of the file has to be header
49 keys_to_validate = ['ref', 'label', 'date', 'amount', 'commission_amount']55 keys_to_validate = ['ref', 'label', 'date', 'amount', 'commission_amount']
50 super(GenericFileParser,self).__init__(parse_name, keys_to_validate=keys_to_validate, ftype=ftype, convertion_dict=convertion_dict)56 super(GenericFileParser, self).__init__(parse_name, keys_to_validate=keys_to_validate, ftype=ftype, conversion_dict=conversion_dict)
5157
52 @classmethod58 @classmethod
53 def parser_for(cls, parser_name):59 def parser_for(cls, parser_name):
@@ -60,7 +66,7 @@
60 def get_st_line_vals(self, line, *args, **kwargs):66 def get_st_line_vals(self, line, *args, **kwargs):
61 """67 """
62 This method must return a dict of vals that can be passed to create68 This method must return a dict of vals that can be passed to create
63 method of statement line in order to record it. It is the responsibility 69 method of statement line in order to record it. It is the responsibility
64 of every parser to give this dict of vals, so each one can implement his70 of every parser to give this dict of vals, so each one can implement his
65 own way of recording the lines.71 own way of recording the lines.
66 :param: line: a dict of vals that represent a line of result_row_list72 :param: line: a dict of vals that represent a line of result_row_list
@@ -77,14 +83,12 @@
77 In this generic parser, the commission is given for every line, so we store it83 In this generic parser, the commission is given for every line, so we store it
78 for each one.84 for each one.
79 """85 """
80 return {86 return {'name': line.get('label', line.get('ref', '/')),
81 'name': line.get('label', line.get('ref','/')),87 'date': line.get('date', datetime.datetime.now().date()),
82 'date': line.get('date', datetime.datetime.now().date()),88 'amount': line.get('amount', 0.0),
83 'amount': line.get('amount', 0.0),89 'ref': line.get('ref', '/'),
84 'ref': line.get('ref','/'),90 'label': line.get('label', ''),
85 'label': line.get('label',''),91 'commission_amount': line.get('commission_amount', 0.0)}
86 'commission_amount': line.get('commission_amount', 0.0),
87 }
8892
89 def _post(self, *args, **kwargs):93 def _post(self, *args, **kwargs):
90 """94 """
@@ -93,10 +97,6 @@
93 res = super(GenericFileParser, self)._post(*args, **kwargs)97 res = super(GenericFileParser, self)._post(*args, **kwargs)
94 val = 0.098 val = 0.0
95 for row in self.result_row_list:99 for row in self.result_row_list:
96 val += row.get('commission_amount',0.0)100 val += row.get('commission_amount', 0.0)
97 self.commission_global_amount = val101 self.commission_global_amount = val
98 return res102 return res
99
100
101
102
103103
=== modified file 'account_statement_base_import/parser/parser.py'
--- account_statement_base_import/parser/parser.py 2012-11-23 16:27:24 +0000
+++ account_statement_base_import/parser/parser.py 2013-06-10 07:05:32 +0000
@@ -21,6 +21,7 @@
21import base6421import base64
22import csv22import csv
2323
24
24def UnicodeDictReader(utf8_data, **kwargs):25def UnicodeDictReader(utf8_data, **kwargs):
25 sniffer = csv.Sniffer()26 sniffer = csv.Sniffer()
26 pos = utf8_data.tell()27 pos = utf8_data.tell()
@@ -31,6 +32,7 @@
31 for row in csv_reader:32 for row in csv_reader:
32 yield dict([(key, unicode(value, 'utf-8')) for key, value in row.iteritems()])33 yield dict([(key, unicode(value, 'utf-8')) for key, value in row.iteritems()])
3334
35
34class BankStatementImportParser(object):36class BankStatementImportParser(object):
35 """37 """
36 Generic abstract class for defining parser for different files and38 Generic abstract class for defining parser for different files and
@@ -38,7 +40,7 @@
38 own. If your file is a .csv or .xls format, you should consider inheirt40 own. If your file is a .csv or .xls format, you should consider inheirt
39 from the FileParser instead.41 from the FileParser instead.
40 """42 """
41 43
42 def __init__(self, parser_name, *args, **kwargs):44 def __init__(self, parser_name, *args, **kwargs):
43 # The name of the parser as it will be called45 # The name of the parser as it will be called
44 self.parser_name = parser_name46 self.parser_name = parser_name
@@ -50,7 +52,7 @@
50 # Concatenate here the global commission taken by the bank/office52 # Concatenate here the global commission taken by the bank/office
51 # for this statement.53 # for this statement.
52 self.commission_global_amount = None54 self.commission_global_amount = None
53 55
54 @classmethod56 @classmethod
55 def parser_for(cls, parser_name):57 def parser_for(cls, parser_name):
56 """58 """
@@ -58,17 +60,17 @@
58 return the good class from his name.60 return the good class from his name.
59 """61 """
60 return False62 return False
61 63
62 def _decode_64b_stream(self):64 def _decode_64b_stream(self):
63 """65 """
64 Decode self.filebuffer in base 64 and override it66 Decode self.filebuffer in base 64 and override it
65 """67 """
66 self.filebuffer = base64.b64decode(self.filebuffer)68 self.filebuffer = base64.b64decode(self.filebuffer)
67 return True69 return True
68 70
69 def _format(self, decode_base_64=True, **kwargs):71 def _format(self, decode_base_64=True, **kwargs):
70 """72 """
71 Decode into base 64 if asked and Format the given filebuffer by calling 73 Decode into base 64 if asked and Format the given filebuffer by calling
72 _custom_format method.74 _custom_format method.
73 """75 """
74 if decode_base_64:76 if decode_base_64:
@@ -83,43 +85,40 @@
83 """85 """
84 return NotImplementedError86 return NotImplementedError
8587
86
87 def _pre(self, *args, **kwargs):88 def _pre(self, *args, **kwargs):
88 """89 """
89 Implement a method in your parser to make a pre-treatment on datas before parsing 90 Implement a method in your parser to make a pre-treatment on datas before parsing
90 them, like concatenate stuff, and so... Work on self.filebuffer91 them, like concatenate stuff, and so... Work on self.filebuffer
91 """92 """
92 return NotImplementedError93 return NotImplementedError
9394
94 def _parse(self, *args, **kwargs):95 def _parse(self, *args, **kwargs):
95 """96 """
96 Implement a method in your parser to save the result of parsing self.filebuffer 97 Implement a method in your parser to save the result of parsing self.filebuffer
97 in self.result_row_list instance property.98 in self.result_row_list instance property.
98 """99 """
99 return NotImplementedError100 return NotImplementedError
100 101
101 def _validate(self, *args, **kwargs):102 def _validate(self, *args, **kwargs):
102 """103 """
103 Implement a method in your parser to validate the self.result_row_list instance104 Implement a method in your parser to validate the self.result_row_list instance
104 property and raise an error if not valid.105 property and raise an error if not valid.
105 """106 """
106 return NotImplementedError107 return NotImplementedError
107 108
108 def _post(self, *args, **kwargs):109 def _post(self, *args, **kwargs):
109 """110 """
110 Implement a method in your parser to make some last changes on the result of parsing111 Implement a method in your parser to make some last changes on the result of parsing
111 the datas, like converting dates, computing commission, ... 112 the datas, like converting dates, computing commission, ...
112 Work on self.result_row_list and put the commission global amount if any113 Work on self.result_row_list and put the commission global amount if any
113 in the self.commission_global_amount one.114 in the self.commission_global_amount one.
114 """115 """
115 return NotImplementedError116 return NotImplementedError
116
117117
118
119 def get_st_line_vals(self, line, *args, **kwargs):118 def get_st_line_vals(self, line, *args, **kwargs):
120 """119 """
121 Implement a method in your parser that must return a dict of vals that can be 120 Implement a method in your parser that must return a dict of vals that can be
122 passed to create method of statement line in order to record it. It is the responsibility 121 passed to create method of statement line in order to record it. It is the responsibility
123 of every parser to give this dict of vals, so each one can implement his122 of every parser to give this dict of vals, so each one can implement his
124 own way of recording the lines.123 own way of recording the lines.
125 :param: line: a dict of vals that represent a line of result_row_list124 :param: line: a dict of vals that represent a line of result_row_list
@@ -133,17 +132,17 @@
133 }132 }
134 """133 """
135 return NotImplementedError134 return NotImplementedError
136 135
137 def get_st_line_commision(self, *args, **kwargs):136 def get_st_line_commision(self, *args, **kwargs):
138 """137 """
139 This is called by the importation method to create the commission line in138 This is called by the importation method to create the commission line in
140 the bank statement. We will always create one line for the commission in the139 the bank statement. We will always create one line for the commission in the
141 bank statement, but it could be computated from a value of each line, or given 140 bank statement, but it could be computated from a value of each line, or given
142 in a single line for the whole file.141 in a single line for the whole file.
143 return: float of the whole commission (self.commission_global_amount)142 return: float of the whole commission (self.commission_global_amount)
144 """143 """
145 return self.commission_global_amount144 return self.commission_global_amount
146 145
147 def parse(self, filebuffer, *args, **kwargs):146 def parse(self, filebuffer, *args, **kwargs):
148 """147 """
149 This will be the method that will be called by wizard, button and so148 This will be the method that will be called by wizard, button and so
@@ -151,7 +150,7 @@
151 that need to be define for each parser.150 that need to be define for each parser.
152 Return:151 Return:
153 [] of rows as {'key':value}152 [] of rows as {'key':value}
154 153
155 Note: The row_list must contain only value that are present in the account.154 Note: The row_list must contain only value that are present in the account.
156 bank.statement.line object !!!155 bank.statement.line object !!!
157 """156 """
@@ -165,7 +164,8 @@
165 self._validate(*args, **kwargs)164 self._validate(*args, **kwargs)
166 self._post(*args, **kwargs)165 self._post(*args, **kwargs)
167 return self.result_row_list166 return self.result_row_list
168 167
168
169def itersubclasses(cls, _seen=None):169def itersubclasses(cls, _seen=None):
170 """170 """
171 itersubclasses(cls)171 itersubclasses(cls)
@@ -179,7 +179,7 @@
179 >>> class C(A): pass179 >>> class C(A): pass
180 >>> class D(B,C): pass180 >>> class D(B,C): pass
181 >>> class E(D): pass181 >>> class E(D): pass
182 >>> 182 >>>
183 >>> for cls in itersubclasses(A):183 >>> for cls in itersubclasses(A):
184 ... print(cls.__name__)184 ... print(cls.__name__)
185 B185 B
@@ -193,10 +193,11 @@
193 if not isinstance(cls, type):193 if not isinstance(cls, type):
194 raise TypeError('itersubclasses must be called with '194 raise TypeError('itersubclasses must be called with '
195 'new-style classes, not %.100r' % cls)195 'new-style classes, not %.100r' % cls)
196 if _seen is None: _seen = set()196 if _seen is None:
197 _seen = set()
197 try:198 try:
198 subs = cls.__subclasses__()199 subs = cls.__subclasses__()
199 except TypeError: # fails only when cls is type200 except TypeError: # fails only when cls is type
200 subs = cls.__subclasses__(cls)201 subs = cls.__subclasses__(cls)
201 for sub in subs:202 for sub in subs:
202 if sub not in _seen:203 if sub not in _seen:
@@ -204,7 +205,8 @@
204 yield sub205 yield sub
205 for sub in itersubclasses(sub, _seen):206 for sub in itersubclasses(sub, _seen):
206 yield sub207 yield sub
207 208
209
208def new_bank_statement_parser(parser_name, *args, **kwargs):210def new_bank_statement_parser(parser_name, *args, **kwargs):
209 """211 """
210 Return an instance of the good parser class base on the providen name212 Return an instance of the good parser class base on the providen name
@@ -215,4 +217,3 @@
215 if cls.parser_for(parser_name):217 if cls.parser_for(parser_name):
216 return cls(parser_name, *args, **kwargs)218 return cls(parser_name, *args, **kwargs)
217 raise ValueError219 raise ValueError
218
219220
=== modified file 'account_statement_base_import/statement.py'
--- account_statement_base_import/statement.py 2012-09-25 08:05:34 +0000
+++ account_statement_base_import/statement.py 2013-06-10 07:05:32 +0000
@@ -18,65 +18,62 @@
18# along with this program. If not, see <http://www.gnu.org/licenses/>.18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#19#
20##############################################################################20##############################################################################
2121import sys
22from tools.translate import _22import traceback
23
24import psycopg2
25
26from openerp.tools.translate import _
23import datetime27import datetime
24import netsvc28from openerp.osv.orm import Model
25logger = netsvc.Logger()
26from openerp.osv.orm import Model, fields
27from openerp.osv import fields, osv29from openerp.osv import fields, osv
28# from account_statement_base_import.parser.file_parser import FileParser
29from parser import new_bank_statement_parser30from parser import new_bank_statement_parser
30import sys31
31import traceback
3232
33class AccountStatementProfil(Model):33class AccountStatementProfil(Model):
34 _inherit = "account.statement.profile"34 _inherit = "account.statement.profile"
35 35
36
37 def get_import_type_selection(self, cr, uid, context=None):36 def get_import_type_selection(self, cr, uid, context=None):
38 """37 """
39 Has to be inherited to add parser38 Has to be inherited to add parser
40 """39 """
41 return [('generic_csvxls_so', 'Generic .csv/.xls based on SO Name')]40 return [('generic_csvxls_so', 'Generic .csv/.xls based on SO Name')]
42 41
43
44 _columns = {42 _columns = {
45 'launch_import_completion': fields.boolean("Launch completion after import", 43 'launch_import_completion': fields.boolean(
46 help="Tic that box to automatically launch the completion on each imported\44 "Launch completion after import",
47 file using this profile."),45 help="Tic that box to automatically launch the completion "
46 "on each imported file using this profile."),
48 'last_import_date': fields.datetime("Last Import Date"),47 'last_import_date': fields.datetime("Last Import Date"),
49 'rec_log': fields.text('log', readonly=True),48 # we remove deprecated as it floods logs in standard/warning level sob...
50 'import_type': fields.selection(get_import_type_selection, 'Type of import', required=True, 49 'rec_log': fields.text('log', readonly=True), # Deprecated
51 help = "Choose here the method by which you want to import bank statement for this profile."),50 'import_type': fields.selection(
52 51 get_import_type_selection,
52 'Type of import',
53 required=True,
54 help="Choose here the method by which you want to import bank"
55 "statement for this profile."),
56
53 }57 }
54 58
55 def write_logs_after_import(self, cr, uid, ids, statement_id, num_lines, context):59 def write_logs_after_import(self, cr, uid, ids, statement_id, num_lines, context):
56 """60 """
57 Write the log in the logger + in the log field of the profile to report the user about 61 Write the log in the logger
58 what has been done.62
59
60 :param int/long statement_id: ID of the concerned account.bank.statement63 :param int/long statement_id: ID of the concerned account.bank.statement
61 :param int/long num_lines: Number of line that have been parsed64 :param int/long num_lines: Number of line that have been parsed
62 :return: True65 :return: True
63 """66 """
64 if type(ids) is int:67 self.message_post(cr,
65 ids = [ids]68 uid,
66 for id in ids:69 ids,
67 log = self.read(cr, uid, id, ['rec_log'], context=context)['rec_log']70 body=_('Statement ID %s have been imported with %s lines.') %
68 log_line = log and log.split("\n") or []71 (statement_id, num_lines),
69 import_date = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')72 context=context)
70 log_line[0:0] = [import_date + ' : '
71 + _("Bank Statement ID %s have been imported with %s lines ") %(statement_id, num_lines)]
72 log = "\n".join(log_line)
73 self.write(cr, uid, id, {'rec_log' : log, 'last_import_date':import_date}, context=context)
74 logger.notifyChannel('Bank Statement Import', netsvc.LOG_INFO,
75 "Bank Statement ID %s have been imported with %s lines "%(statement_id, num_lines))
76 return True73 return True
77 74
78 def prepare_global_commission_line_vals(self, cr, uid, parser, 75 def prepare_global_commission_line_vals(
79 result_row_list, profile, statement_id, context):76 self, cr, uid, parser, result_row_list, profile, statement_id, context):
80 """77 """
81 Prepare the global commission line if there is one. The global78 Prepare the global commission line if there is one. The global
82 commission is computed by by calling the get_st_line_commision79 commission is computed by by calling the get_st_line_commision
@@ -86,7 +83,7 @@
86 :param: browse_record of the current parser83 :param: browse_record of the current parser
87 :param: result_row_list: [{'key':value}]84 :param: result_row_list: [{'key':value}]
88 :param: profile: browserecord of account.statement.profile85 :param: profile: browserecord of account.statement.profile
89 :param: statement_id : int/long of the current importing statement ID86 :param: statement_id: int/long of the current importing statement ID
90 :param: context: global context87 :param: context: global context
91 return: dict of vals that will be passed to create method of statement line.88 return: dict of vals that will be passed to create method of statement line.
92 """89 """
@@ -95,9 +92,8 @@
95 partner_id = profile.partner_id and profile.partner_id.id or False92 partner_id = profile.partner_id and profile.partner_id.id or False
96 commission_account_id = profile.commission_account_id and profile.commission_account_id.id or False93 commission_account_id = profile.commission_account_id and profile.commission_account_id.id or False
97 commission_analytic_id = profile.commission_analytic_id and profile.commission_analytic_id.id or False94 commission_analytic_id = profile.commission_analytic_id and profile.commission_analytic_id.id or False
98 statement_line_obj = self.pool.get('account.bank.statement.line')
99 comm_values = {95 comm_values = {
100 'name': 'IN '+ _('Commission line'),96 'name': 'IN ' + _('Commission line'),
101 'date': datetime.datetime.now().date(),97 'date': datetime.datetime.now().date(),
102 'amount': parser.get_st_line_commision(),98 'amount': parser.get_st_line_commision(),
103 'partner_id': partner_id,99 'partner_id': partner_id,
@@ -106,118 +102,150 @@
106 'account_id': commission_account_id,102 'account_id': commission_account_id,
107 'ref': 'commission',103 'ref': 'commission',
108 'analytic_account_id': commission_analytic_id,104 'analytic_account_id': commission_analytic_id,
109 # !! We set the already_completed so auto-completion will not update those values !105 # !! We set the already_completed so auto-completion will not update those values!
110 'already_completed': True,106 'already_completed': True,
111 }107 }
112 return comm_values108 return comm_values
113 109
114 def prepare_statetement_lines_vals(self, cursor, uid, parser_vals, 110 def prepare_statetement_lines_vals(
115 account_payable, account_receivable, statement_id, context):111 self, cr, uid, parser_vals, account_payable, account_receivable,
112 statement_id, context):
116 """113 """
117 Hook to build the values of a line from the parser returned values. At114 Hook to build the values of a line from the parser returned values. At
118 least it fullfill the statement_id and account_id. Overide it to add your115 least it fullfill the statement_id and account_id. Overide it to add your
119 own completion if needed. 116 own completion if needed.
120 117
121 :param dict of vals from parser for account.bank.statement.line (called by 118 :param dict of vals from parser for account.bank.statement.line (called by
122 parser.get_st_line_vals)119 parser.get_st_line_vals)
123 :param int/long account_payable: ID of the receivable account to use120 :param int/long account_payable: ID of the receivable account to use
124 :param int/long account_receivable: ID of the payable account to use121 :param int/long account_receivable: ID of the payable account to use
125 :param int/long statement_id: ID of the concerned account.bank.statement122 :param int/long statement_id: ID of the concerned account.bank.statement
126 :return : dict of vals that will be passed to create method of statement line.123 :return: dict of vals that will be passed to create method of statement line.
127 """124 """
128 statement_obj = self.pool.get('account.bank.statement')125 statement_obj = self.pool.get('account.bank.statement')
129 values = parser_vals126 values = parser_vals
130 values['statement_id']= statement_id127 values['statement_id'] = statement_id
131 values['account_id'] = statement_obj.get_account_for_counterpart(128 values['account_id'] = statement_obj.get_account_for_counterpart(cr,
132 cursor,129 uid,
133 uid,130 parser_vals['amount'],
134 parser_vals['amount'],131 account_receivable,
135 account_receivable,132 account_payable)
136 account_payable133
137 )134 date = values.get('date')
135 period_memoizer = context.get('period_memoizer')
136 if not period_memoizer:
137 period_memoizer = {}
138 context['period_memoizer'] = period_memoizer
139 if period_memoizer.get(date):
140 values['period_id'] = period_memoizer[date]
141 else:
142 # This is awfully slow...
143 periods = self.pool.get('account.period').find(cr, uid,
144 dt=values.get('date'),
145 context=context)
146 values['period_id'] = periods[0]
147 period_memoizer[date] = periods[0]
148 values['type'] = 'general'
138 return values149 return values
139 150
140 def statement_import(self, cursor, uid, ids, profile_id, file_stream, ftype="csv", context=None):151 def statement_import(self, cr, uid, ids, profile_id, file_stream, ftype="csv", context=None):
141 """152 """
142 Create a bank statement with the given profile and parser. It will fullfill the bank statement153 Create a bank statement with the given profile and parser. It will fullfill the bank statement
143 with the values of the file providen, but will not complete data (like finding the partner, or154 with the values of the file providen, but will not complete data (like finding the partner, or
144 the right account). This will be done in a second step with the completion rules.155 the right account). This will be done in a second step with the completion rules.
145 It will also create the commission line if it apply and record the providen file as156 It will also create the commission line if it apply and record the providen file as
146 an attachement of the bank statement.157 an attachement of the bank statement.
147 158
148 :param int/long profile_id: ID of the profile used to import the file159 :param int/long profile_id: ID of the profile used to import the file
149 :param filebuffer file_stream: binary of the providen file160 :param filebuffer file_stream: binary of the providen file
150 :param char: ftype represent the file exstension (csv by default)161 :param char: ftype represent the file exstension (csv by default)
151 :return: ID of the created account.bank.statemênt162 :return: ID of the created account.bank.statemênt
152 """163 """
153 context = context or {}
154 statement_obj = self.pool.get('account.bank.statement')164 statement_obj = self.pool.get('account.bank.statement')
155 statement_line_obj = self.pool.get('account.bank.statement.line')165 statement_line_obj = self.pool.get('account.bank.statement.line')
156 attachment_obj = self.pool.get('ir.attachment')166 attachment_obj = self.pool.get('ir.attachment')
157 prof_obj = self.pool.get("account.statement.profile")167 prof_obj = self.pool.get("account.statement.profile")
158 if not profile_id:168 if not profile_id:
159 raise osv.except_osv(169 raise osv.except_osv(_("No Profile!"),
160 _("No Profile !"),170 _("You must provide a valid profile to import a bank statement!"))
161 _("You must provide a valid profile to import a bank statement !"))171 prof = prof_obj.browse(cr, uid, profile_id, context=context)
162 prof = prof_obj.browse(cursor,uid,profile_id,context)172
163
164 parser = new_bank_statement_parser(prof.import_type, ftype=ftype)173 parser = new_bank_statement_parser(prof.import_type, ftype=ftype)
165 result_row_list = parser.parse(file_stream)174 result_row_list = parser.parse(file_stream)
166 # Check all key are present in account.bank.statement.line !!175 # Check all key are present in account.bank.statement.line!!
176 if not result_row_list:
177 raise osv.except_osv(_("Nothing to import"),
178 _("The file is empty"))
167 parsed_cols = parser.get_st_line_vals(result_row_list[0]).keys()179 parsed_cols = parser.get_st_line_vals(result_row_list[0]).keys()
168 for col in parsed_cols:180 for col in parsed_cols:
169 if col not in statement_line_obj._columns:181 if col not in statement_line_obj._columns:
170 raise osv.except_osv(182 raise osv.except_osv(_("Missing column!"),
171 _("Missing column !"),183 _("Column %s you try to import is not "
172 _("Column %s you try to import is not present in the bank statement line !") %(col))184 "present in the bank statement line!") % col)
173 185
174 statement_id = statement_obj.create(cursor,uid,{'profile_id':prof.id,},context) 186 statement_id = statement_obj.create(cr, uid,
175 account_receivable, account_payable = statement_obj.get_default_pay_receiv_accounts(cursor, uid, context)187 {'profile_id': prof.id},
188 context=context)
189 if prof.receivable_account_id:
190 account_receivable = account_payable = prof.receivable_account_id.id
191 else:
192 account_receivable, account_payable = statement_obj.get_default_pay_receiv_accounts(
193 cr, uid, context)
176 try:194 try:
177 # Record every line in the bank statement and compute the global commission195 # Record every line in the bank statement and compute the global commission
178 # based on the commission_amount column196 # based on the commission_amount column
197 statement_store = []
179 for line in result_row_list:198 for line in result_row_list:
180 parser_vals = parser.get_st_line_vals(line)199 parser_vals = parser.get_st_line_vals(line)
181 values = self.prepare_statetement_lines_vals(cursor, uid, parser_vals, account_payable, 200 values = self.prepare_statetement_lines_vals(cr, uid, parser_vals, account_payable,
182 account_receivable, statement_id, context)201 account_receivable, statement_id, context)
183 # we finally create the line in system202 statement_store.append(values)
184 statement_line_obj.create(cursor, uid, values, context=context)203 # Hack to bypass ORM poor perfomance. Sob...
204 statement_line_obj._insert_lines(cr, uid, statement_store, context=context)
205
185 # Build and create the global commission line for the whole statement206 # Build and create the global commission line for the whole statement
186 comm_vals = self.prepare_global_commission_line_vals(cursor, uid, parser, result_row_list, prof, statement_id, context)207 comm_vals = self.prepare_global_commission_line_vals(cr, uid, parser, result_row_list,
208 prof, statement_id, context)
187 if comm_vals:209 if comm_vals:
188 res = statement_line_obj.create(cursor, uid, comm_vals,context=context)210 statement_line_obj.create(cr, uid, comm_vals, context=context)
189 211 else:
190 attachment_obj.create(212 # Trigger store field computation if someone has better idea
191 cursor,213 start_bal = statement_obj.read(cr, uid, statement_id,
192 uid,214 ['balance_start'],
193 {215 context=context)
194 'name': 'statement file',216 start_bal = start_bal['balance_start']
195 'datas': file_stream,217 statement_obj.write(cr, uid, [statement_id],
196 'datas_fname': "%s.%s"%(datetime.datetime.now().date(),218 {'balance_start': start_bal})
197 ftype),219
198 'res_model': 'account.bank.statement',220 attachment_obj.create(cr,
199 'res_id': statement_id,221 uid,
200 },222 {'name': 'statement file',
201 context=context223 'datas': file_stream,
202 ) 224 'datas_fname': "%s.%s" % (
203 # If user ask to launch completion at end of import, do it !225 datetime.datetime.now().date(),
226 ftype),
227 'res_model': 'account.bank.statement',
228 'res_id': statement_id},
229 context=context)
230
231 # If user ask to launch completion at end of import, do it!
204 if prof.launch_import_completion:232 if prof.launch_import_completion:
205 statement_obj.button_auto_completion(cursor, uid, [statement_id], context)233 statement_obj.button_auto_completion(cr, uid, [statement_id], context)
206 234
207 # Write the needed log infos on profile235 # Write the needed log infos on profile
208 self.write_logs_after_import(cursor, uid, prof.id, statement_id,236 self.write_logs_after_import(cr, uid, prof.id,
209 len(result_row_list), context)237 statement_id,
210 238 len(result_row_list),
211 except Exception, exc:239 context)
212 statement_obj.unlink(cursor, uid, [statement_id])240
241 except Exception:
242 statement_obj.unlink(cr, uid, [statement_id], context=context)
213 error_type, error_value, trbk = sys.exc_info()243 error_type, error_value, trbk = sys.exc_info()
214 st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value)244 st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value)
215 st += ''.join(traceback.format_tb(trbk, 30))245 st += ''.join(traceback.format_tb(trbk, 30))
216 raise osv.except_osv(246 raise osv.except_osv(_("Statement import error"),
217 _("Statement import error"),247 _("The statement cannot be created: %s") % st)
218 _("The statement cannot be created : %s") %(st))
219 return statement_id248 return statement_id
220
221249
222250
223class AccountStatementLine(Model):251class AccountStatementLine(Model):
@@ -228,8 +256,48 @@
228 """256 """
229 _inherit = "account.bank.statement.line"257 _inherit = "account.bank.statement.line"
230258
231 _columns={259 def _get_available_columns(self, statement_store):
232 'commission_amount': fields.sparse(type='float', string='Line Commission Amount', 260 """Return writeable by SQL columns"""
261 statement_line_obj = self.pool['account.bank.statement.line']
262 model_cols = statement_line_obj._columns
263 avail = [k for k, col in model_cols.iteritems() if not hasattr(col, '_fnct')]
264 keys = [k for k in statement_store[0].keys() if k in avail]
265 keys.sort()
266 return keys
267
268 def _insert_lines(self, cr, uid, statement_store, context=None):
269 """ Do raw insert into database because ORM is awfully slow
270 when doing batch write. It is a shame that batch function
271 does not exist"""
272 statement_line_obj = self.pool['account.bank.statement.line']
273 statement_line_obj.check_access_rule(cr, uid, [], 'create')
274 statement_line_obj.check_access_rights(cr, uid, 'create', raise_exception=True)
275 cols = self._get_available_columns(statement_store)
276 tmp_vals = (', '.join(cols), ', '.join(['%%(%s)s' % i for i in cols]))
277 sql = "INSERT INTO account_bank_statement_line (%s) VALUES (%s);" % tmp_vals
278 try:
279 cr.executemany(sql, tuple(statement_store))
280 except psycopg2.Error as sql_err:
281 cr.rollback()
282 raise osv.except_osv(_("ORM bypass error"),
283 sql_err.pgerror)
284
285 def _update_line(self, cr, uid, vals, context=None):
286 """ Do raw update into database because ORM is awfully slow
287 when cheking security."""
288 cols = self._get_available_columns([vals])
289 tmp_vals = (', '.join(['%s = %%(%s)s' % (i, i) for i in cols]))
290 sql = "UPDATE account_bank_statement_line SET %s where id = %%(id)s;" % tmp_vals
291 try:
292 cr.execute(sql, vals)
293 except psycopg2.Error as sql_err:
294 cr.rollback()
295 raise osv.except_osv(_("ORM bypass error"),
296 sql_err.pgerror)
297
298 _columns = {
299 'commission_amount': fields.sparse(
300 type='float',
301 string='Line Commission Amount',
233 serialization_field='additionnal_bank_fields'),302 serialization_field='additionnal_bank_fields'),
234
235 }303 }
236304
=== modified file 'account_statement_base_import/statement_view.xml'
--- account_statement_base_import/statement_view.xml 2012-08-02 12:46:12 +0000
+++ account_statement_base_import/statement_view.xml 2013-06-10 07:05:32 +0000
@@ -16,10 +16,12 @@
16 <field name="import_type"/>16 <field name="import_type"/>
17 <button name="%(account_statement_base_import.statement_importer_action)d"17 <button name="%(account_statement_base_import.statement_importer_action)d"
18 string="Import Bank Statement"18 string="Import Bank Statement"
19 type="action" icon="gtk-ok" 19 type="action" icon="gtk-ok"
20 colspan = "2"/>20 colspan = "2"/>
21 <separator colspan="4" string="Import Logs"/>21 <group attrs="{'invisible': [('rec_log', '=', False)]}">
22 <field name="rec_log" colspan="4" nolabel="1"/>22 <separator colspan="4" string="Historical Import Logs"/>
23 <field name="rec_log" colspan="4" nolabel="1" />
24 </group>
23 </field>25 </field>
24 </field>26 </field>
25 </record>27 </record>
@@ -32,13 +34,13 @@
32 <field eval="20" name="priority"/>34 <field eval="20" name="priority"/>
33 <field name="arch" type="xml">35 <field name="arch" type="xml">
34 <data>36 <data>
35 <xpath expr="/form/notebook/page/field[@name='line_ids']/form/field[@name='label']" position="after">37 <xpath expr="/form/sheet/notebook/page/field[@name='line_ids']/form/group/field[@name='label']" position="after">
36 <field name="commission_amount" />38 <field name="commission_amount" />
37 </xpath>39 </xpath>
38 </data>40 </data>
39 </field>41 </field>
40 </record>42 </record>
4143
42 44
43</data>45</data>
44</openerp>46</openerp>
4547
=== modified file 'account_statement_base_import/wizard/import_statement.py'
--- account_statement_base_import/wizard/import_statement.py 2012-11-27 10:51:47 +0000
+++ account_statement_base_import/wizard/import_statement.py 2013-06-10 07:05:32 +0000
@@ -23,15 +23,18 @@
23Wizard to import financial institute date in bank statement23Wizard to import financial institute date in bank statement
24"""24"""
2525
26from osv import fields, osv26from openerp.osv import orm, fields
27from tools.translate import _27
28from openerp.tools.translate import _
28import os29import os
2930
30class CreditPartnerStatementImporter(osv.osv_memory):31
32class CreditPartnerStatementImporter(orm.TransientModel):
31 _name = "credit.statement.import"33 _name = "credit.statement.import"
32 34
33 def default_get(self, cr, uid, fields, context=None):35 def default_get(self, cr, uid, fields, context=None):
34 if context is None: context = {}36 if context is None:
37 context = {}
35 res = {}38 res = {}
36 if (context.get('active_model', False) == 'account.statement.profile' and39 if (context.get('active_model', False) == 'account.statement.profile' and
37 context.get('active_ids', False)):40 context.get('active_ids', False)):
@@ -39,80 +42,81 @@
39 assert len(ids) == 1, 'You cannot use this on more than one profile !'42 assert len(ids) == 1, 'You cannot use this on more than one profile !'
40 res['profile_id'] = ids[0]43 res['profile_id'] = ids[0]
41 other_vals = self.onchange_profile_id(cr, uid, [], res['profile_id'], context=context)44 other_vals = self.onchange_profile_id(cr, uid, [], res['profile_id'], context=context)
42 res.update(other_vals.get('value',{}))45 res.update(other_vals.get('value', {}))
43 return res46 return res
44 47
45 _columns = {48 _columns = {
46 'profile_id': fields.many2one('account.statement.profile',49 'profile_id': fields.many2one('account.statement.profile',
47 'Import configuration parameter',50 'Import configuration parameter',
48 required=True),51 required=True),
49 'input_statement': fields.binary('Statement file', required=True),52 'input_statement': fields.binary('Statement file', required=True),
50 'partner_id': fields.many2one('res.partner',53 'partner_id': fields.many2one('res.partner',
51 'Credit insitute partner',54 'Credit insitute partner'),
52 ),
53 'journal_id': fields.many2one('account.journal',55 'journal_id': fields.many2one('account.journal',
54 'Financial journal to use transaction',56 'Financial journal to use transaction'),
55 ),
56 'input_statement': fields.binary('Statement file', required=True),
57 'file_name': fields.char('File Name', size=128),57 'file_name': fields.char('File Name', size=128),
58 'commission_account_id': fields.many2one('account.account',58 'commission_account_id': fields.many2one('account.account',
59 'Commission account',59 'Commission account'),
60 ),
61 'commission_analytic_id': fields.many2one('account.analytic.account',60 'commission_analytic_id': fields.many2one('account.analytic.account',
62 'Commission analytic account',61 'Commission analytic account'),
63 ),
64 'receivable_account_id': fields.many2one('account.account',62 'receivable_account_id': fields.many2one('account.account',
65 'Force Receivable/Payable Account'),63 'Force Receivable/Payable Account'),
66 'force_partner_on_bank': fields.boolean('Force partner on bank move', 64 'force_partner_on_bank': fields.boolean(
67 help="Tic that box if you want to use the credit insitute partner\65 'Force partner on bank move',
68 in the counterpart of the treasury/banking move."66 help="Tic that box if you want to use the credit insitute partner "
69 ),67 "in the counterpart of the treasury/banking move."),
70 'balance_check': fields.boolean('Balance check', 68 'balance_check': fields.boolean(
71 help="Tic that box if you want OpenERP to control the start/end balance\69 'Balance check',
72 before confirming a bank statement. If don't ticked, no balance control will be done."70 help="Tic that box if you want OpenERP to control the "
73 ),71 "start/end balance before confirming a bank statement. "
74 } 72 "If don't ticked, no balance control will be done."),
75 73 }
74
76 def onchange_profile_id(self, cr, uid, ids, profile_id, context=None):75 def onchange_profile_id(self, cr, uid, ids, profile_id, context=None):
77 res={}76 res = {}
78 if profile_id:77 if profile_id:
79 c = self.pool.get("account.statement.profile").browse(cr,uid,profile_id)78 c = self.pool.get("account.statement.profile").browse(
80 res = {'value': {'partner_id': c.partner_id and c.partner_id.id or False,79 cr, uid, profile_id, context=context)
81 'journal_id': c.journal_id and c.journal_id.id or False, 'commission_account_id': \80 res = {'value':
82 c.commission_account_id and c.commission_account_id.id or False, 81 {'partner_id': c.partner_id and c.partner_id.id or False,
83 'receivable_account_id': c.receivable_account_id and c.receivable_account_id.id or False,82 'journal_id': c.journal_id and c.journal_id.id or False,
84 'commission_a':c.commission_analytic_id and c.commission_analytic_id.id or False,83 'commission_account_id':
85 'force_partner_on_bank':c.force_partner_on_bank,84 c.commission_account_id and c.commission_account_id.id or False,
86 'balance_check':c.balance_check,}}85 'receivable_account_id': c.receivable_account_id and c.receivable_account_id.id or False,
86 'commission_a': c.commission_analytic_id and c.commission_analytic_id.id or False,
87 'force_partner_on_bank': c.force_partner_on_bank,
88 'balance_check': c.balance_check,
89 }
90 }
87 return res91 return res
8892
89 def _check_extension(self, filename):93 def _check_extension(self, filename):
90 (shortname, ftype) = os.path.splitext(filename)94 (__, ftype) = os.path.splitext(filename)
91 if not ftype:95 if not ftype:
92 #We do not use osv exception we do not want to have it logged96 #We do not use osv exception we do not want to have it logged
93 raise Exception(_('Please use a file with an extention'))97 raise Exception(_('Please use a file with an extention'))
94 return ftype98 return ftype
9599
96 def import_statement(self, cursor, uid, req_id, context=None):100 def import_statement(self, cr, uid, req_id, context=None):
97 """This Function import credit card agency statement"""101 """This Function import credit card agency statement"""
98 context = context or {}102 context = context or {}
99 if isinstance(req_id, list):103 if isinstance(req_id, list):
100 req_id = req_id[0]104 req_id = req_id[0]
101 importer = self.browse(cursor, uid, req_id, context)105 importer = self.browse(cr, uid, req_id, context)
102 ftype = self._check_extension(importer.file_name)106 ftype = self._check_extension(importer.file_name)
103 sid = self.pool.get(107 sid = self.pool.get(
104 'account.statement.profile').statement_import(108 'account.statement.profile').statement_import(
105 cursor,109 cr,
106 uid,110 uid,
107 False,111 False,
108 importer.profile_id.id,112 importer.profile_id.id,
109 importer.input_statement,113 importer.input_statement,
110 ftype.replace('.',''),114 ftype.replace('.', ''),
111 context=context115 context=context
112 )116 )
113 model_obj = self.pool.get('ir.model.data')117 model_obj = self.pool.get('ir.model.data')
114 action_obj = self.pool.get('ir.actions.act_window')118 action_obj = self.pool.get('ir.actions.act_window')
115 action_id = model_obj.get_object_reference(cursor, uid, 'account', 'action_bank_statement_tree')[1]119 action_id = model_obj.get_object_reference(cr, uid, 'account', 'action_bank_statement_tree')[1]
116 res = action_obj.read(cursor, uid, action_id)120 res = action_obj.read(cr, uid, action_id)
117 res['domain'] = res['domain'][:-1] + ",('id', '=', %d)]" % sid121 res['domain'] = res['domain'][:-1] + ",('id', '=', %d)]" % sid
118 return res122 return res
119123
=== modified file 'account_statement_completion_voucher/__init__.py'
--- account_statement_completion_voucher/__init__.py 2012-06-27 07:58:32 +0000
+++ account_statement_completion_voucher/__init__.py 2013-06-10 07:05:32 +0000
@@ -18,4 +18,3 @@
18# along with this program. If not, see <http://www.gnu.org/licenses/>.18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#19#
20##############################################################################20##############################################################################
21
2221
=== modified file 'account_statement_completion_voucher/__openerp__.py'
--- account_statement_completion_voucher/__openerp__.py 2012-06-27 07:58:32 +0000
+++ account_statement_completion_voucher/__openerp__.py 2013-06-10 07:05:32 +0000
@@ -24,8 +24,11 @@
24 'author': 'Camptocamp',24 'author': 'Camptocamp',
25 'maintainer': 'Camptocamp',25 'maintainer': 'Camptocamp',
26 'category': 'Finance',26 'category': 'Finance',
27 'complexity': 'normal', #easy, normal, expert27 'complexity': 'normal',
28 'depends': ['account_statement_base_completion','account_voucher'],28 'depends': [
29 'account_statement_base_completion',
30 'account_voucher'
31 ],
29 'description': """32 'description': """
30 This module is only needed when using account_statement_base_completion with voucher in order adapt the view correctly.33 This module is only needed when using account_statement_base_completion with voucher in order adapt the view correctly.
31 """,34 """,
@@ -36,9 +39,8 @@
36 ],39 ],
37 'demo_xml': [],40 'demo_xml': [],
38 'test': [],41 'test': [],
39 'installable': True,42 'installable': False,
40 'images': [],43 'images': [],
41 'auto_install': True,44 'auto_install': False,
42 'license': 'AGPL-3',45 'license': 'AGPL-3',
43 'active': False,
44}46}
4547
=== modified file 'account_statement_completion_voucher/statement_view.xml'
--- account_statement_completion_voucher/statement_view.xml 2012-06-27 07:58:32 +0000
+++ account_statement_completion_voucher/statement_view.xml 2013-06-10 07:05:32 +0000
@@ -3,19 +3,19 @@
3<data>3<data>
44
5 <!-- Override what we have in account_statement_base_completion to replace the one in the voucher -->5 <!-- Override what we have in account_statement_base_completion to replace the one in the voucher -->
6 <record id="account_statement_base_completion.bank_statement_view_form2" model="ir.ui.view">6 <!-- <record id="account_statement_base_completion.bank_statement_view_form2" model="ir.ui.view">
7 <field name="name">account_bank_statement_import_base.bank_statement.auto_cmpl</field>7 <field name="name">account_bank_statement_import_base.bank_statement.auto_cmpl</field>
8 <field name="model">account.bank.statement</field>8 <field name="model">account.bank.statement</field>
9 <field name="inherit_id" ref="account_voucher.view_bank_statement_tree_voucher" />9 <field name="inherit_id" ref="account_voucher.view_bank_statement_tree_voucher" />
10 <field name="type">form</field>10 <field name="type">form</field>
11 <field name="arch" type="xml">11 <field name="arch" type="xml">
12 <data>12 <data>
13 <xpath expr="/form/notebook/page/field[@name='line_ids']/tree/field[@name='voucher_id']" position="after">13 <xpath expr="/form/sheet/notebook/page/field[@name='line_ids']/tree/field[@name='voucher_id']" position="after">
14 <field name="already_completed" />14 <field name="already_completed" />
15 </xpath>15 </xpath>
16 </data>16 </data>
17 </field>17 </field>
18 </record>18 </record> -->
19 19
20</data>20</data>
21</openerp>21</openerp>
2222
=== modified file 'account_statement_ext/__init__.py'
--- account_statement_ext/__init__.py 2012-06-20 14:10:01 +0000
+++ account_statement_ext/__init__.py 2013-06-10 07:05:32 +0000
@@ -21,4 +21,5 @@
2121
22import statement22import statement
23import report23import report
24import account
25\ No newline at end of file24\ No newline at end of file
25import account
26import voucher
26\ No newline at end of file27\ No newline at end of file
2728
=== modified file 'account_statement_ext/__openerp__.py'
--- account_statement_ext/__openerp__.py 2013-05-15 21:12:19 +0000
+++ account_statement_ext/__openerp__.py 2013-06-10 07:05:32 +0000
@@ -2,7 +2,7 @@
2##############################################################################2##############################################################################
3#3#
4# Author: Nicolas Bessi, Joel Grand-Guillaume4# Author: Nicolas Bessi, Joel Grand-Guillaume
5# Copyright 2011-2012 Camptocamp SA5# Copyright 2011-2013 Camptocamp SA
6#6#
7# This program is free software: you can redistribute it and/or modify7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as8# it under the terms of the GNU Affero General Public License as
@@ -20,12 +20,19 @@
20##############################################################################20##############################################################################
2121
22{'name': "Bank statement extension and profiles",22{'name': "Bank statement extension and profiles",
23 'version': '1.1',23 'version': '1.3.0',
24 'author': 'Camptocamp',24 'author': 'Camptocamp',
25 'maintainer': 'Camptocamp',25 'maintainer': 'Camptocamp',
26 'category': 'Finance',26 'category': 'Finance',
27<<<<<<< TREE
27 'complexity': 'normal', #easy, normal, expert28 'complexity': 'normal', #easy, normal, expert
28 'depends': ['account','report_webkit'],29 'depends': ['account','report_webkit'],
30=======
31 'complexity': 'normal',
32 'depends': ['account',
33 'report_webkit',
34 'account_voucher'],
35>>>>>>> MERGE-SOURCE
29 'description': """36 'description': """
30 Improve the basic bank statement, by adding various new features,37 Improve the basic bank statement, by adding various new features,
31 and help dealing with huge volume of reconciliation through payment offices such as Paypal, Lazer,38 and help dealing with huge volume of reconciliation through payment offices such as Paypal, Lazer,
@@ -59,6 +66,7 @@
59 all the erronous line in a same popup instead of raising and crashing on every step.66 all the erronous line in a same popup instead of raising and crashing on every step.
6067
61 4) Remove the period on the bank statement, and compute it for each line based on their date instead.68 4) Remove the period on the bank statement, and compute it for each line based on their date instead.
69 It also adds this feature in the voucher in order to compute the period correctly.
6270
63 5) Cancelling a bank statement is much more easy and will cancel all related entries, unreconcile them,71 5) Cancelling a bank statement is much more easy and will cancel all related entries, unreconcile them,
64 and finally delete them.72 and finally delete them.
@@ -70,13 +78,11 @@
7078
71 """,79 """,
72 'website': 'http://www.camptocamp.com',80 'website': 'http://www.camptocamp.com',
73 'init_xml': [],81 'data': ['statement_view.xml',
74 'update_xml': [82 'report/bank_statement_webkit_header.xml',
75 'statement_view.xml',83 'report.xml',
76 'report/bank_statement_webkit_header.xml',84 'security/ir.model.access.csv',
77 'report.xml',85 'security/ir_rule.xml'],
78 'security/ir.model.access.csv',
79 ],
80 'demo_xml': [],86 'demo_xml': [],
81 'test': [],87 'test': [],
82 'installable': True,88 'installable': True,
@@ -84,4 +90,4 @@
84 'auto_install': False,90 'auto_install': False,
85 'license': 'AGPL-3',91 'license': 'AGPL-3',
86 'active': False,92 'active': False,
87}93 }
8894
=== modified file 'account_statement_ext/account.py'
--- account_statement_ext/account.py 2012-06-20 14:10:01 +0000
+++ account_statement_ext/account.py 2013-06-10 07:05:32 +0000
@@ -18,13 +18,14 @@
18# along with this program. If not, see <http://www.gnu.org/licenses/>.18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#19#
20##############################################################################20##############################################################################
21import netsvc21
22logger = netsvc.Logger()22from openerp.osv.orm import Model
23from openerp.osv.orm import Model, fields23from openerp.osv import fields
24
2425
25class account_move(Model):26class account_move(Model):
26 _inherit='account.move'27 _inherit = 'account.move'
27 28
28 def unlink(self, cr, uid, ids, context=None):29 def unlink(self, cr, uid, ids, context=None):
29 """30 """
30 Delete the reconciliation when we delete the moves. This31 Delete the reconciliation when we delete the moves. This
@@ -35,6 +36,3 @@
35 if move_line.reconcile_id:36 if move_line.reconcile_id:
36 move_line.reconcile_id.unlink(context=context)37 move_line.reconcile_id.unlink(context=context)
37 return super(account_move, self).unlink(cr, uid, ids, context=context)38 return super(account_move, self).unlink(cr, uid, ids, context=context)
38
39
40
4139
=== modified file 'account_statement_ext/i18n/fr.po'
--- account_statement_ext/i18n/fr.po 2012-12-13 13:57:29 +0000
+++ account_statement_ext/i18n/fr.po 2013-06-10 07:05:32 +0000
@@ -41,7 +41,7 @@
41#. module: account_statement_ext41#. module: account_statement_ext
42#: code:addons/account_statement_ext/statement.py:36142#: code:addons/account_statement_ext/statement.py:361
43#, python-format43#, python-format
44msgid "Configuration Error !"44msgid "Configuration Error!"
45msgstr "Erreur de configuration !"45msgstr "Erreur de configuration !"
4646
47#. module: account_statement_ext47#. module: account_statement_ext
@@ -64,7 +64,7 @@
64#: code:addons/account_statement_ext/statement.py:30764#: code:addons/account_statement_ext/statement.py:307
65#: code:addons/account_statement_ext/statement.py:37265#: code:addons/account_statement_ext/statement.py:372
66#, python-format66#, python-format
67msgid "Error !"67msgid "Error!"
68msgstr "Erreur !"68msgstr "Erreur !"
6969
70#. module: account_statement_ext70#. module: account_statement_ext
7171
=== modified file 'account_statement_ext/report/__init__.py'
--- account_statement_ext/report/__init__.py 2012-06-20 14:10:01 +0000
+++ account_statement_ext/report/__init__.py 2013-06-10 07:05:32 +0000
@@ -1,9 +1,4 @@
1# -*- encoding: utf-8 -*-1# -*- coding: utf-8 -*-
2#
3# __init__.py
4#
5# Copyright (c) 2009 CamptoCamp. All rights reserved.
6##############################################################################
7#2#
8# WARNING: This program as such is intended to be used by professional3# WARNING: This program as such is intended to be used by professional
9# programmers who take the whole responsability of assessing all potential4# programmers who take the whole responsability of assessing all potential
@@ -28,4 +23,4 @@
28#23#
29##############################################################################24##############################################################################
3025
31import bank_statement_report
32\ No newline at end of file26\ No newline at end of file
27import bank_statement_report
3328
=== modified file 'account_statement_ext/report/bank_statement_report.py'
--- account_statement_ext/report/bank_statement_report.py 2012-06-20 14:10:01 +0000
+++ account_statement_ext/report/bank_statement_report.py 2013-06-10 07:05:32 +0000
@@ -1,4 +1,4 @@
1# -*- encoding: utf-8 -*-1# -*- coding utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Author: Nicolas Bessi. Copyright Camptocamp SA4# Author: Nicolas Bessi. Copyright Camptocamp SA
@@ -18,33 +18,29 @@
18#18#
19##############################################################################19##############################################################################
2020
21import time21from openerp.report import report_sxw
2222from openerp.tools.translate import _
23from report import report_sxw23from openerp import pooler
24from osv import osv
25from tools.translate import _
26import pooler
27from operator import add, itemgetter
28from itertools import groupby
29from datetime import datetime24from datetime import datetime
30from report_webkit import webkit_report25from report_webkit import webkit_report
3126
27
32class BankStatementWebkit(report_sxw.rml_parse):28class BankStatementWebkit(report_sxw.rml_parse):
3329
34 def __init__(self, cursor, uid, name, context):30 def __init__(self, cr, uid, name, context):
35 super(BankStatementWebkit, self).__init__(cursor, uid, name, context=context)31 super(BankStatementWebkit, self).__init__(cr, uid, name, context=context)
36 self.pool = pooler.get_pool(self.cr.dbname)32 self.pool = pooler.get_pool(self.cr.dbname)
37 self.cursor = self.cr33 self.cursor = self.cr
3834
39 company = self.pool.get('res.users').browse(self.cr, uid, uid, context=context).company_id35 company = self.pool.get('res.users').browse(
36 self.cr, uid, uid, context=context).company_id
40 header_report_name = ' - '.join((_('BORDEREAU DE REMISE DE CHEQUES'),37 header_report_name = ' - '.join((_('BORDEREAU DE REMISE DE CHEQUES'),
41 company.name, company.currency_id.name))38 company.name, company.currency_id.name))
42 statement = self.pool.get('account.bank.statement').browse(cursor,uid,context['active_id']);
43 footer_date_time = self.formatLang(str(datetime.today())[:19], date_time=True)39 footer_date_time = self.formatLang(str(datetime.today())[:19], date_time=True)
44 self.localcontext.update({40 self.localcontext.update({
45 'cr': cursor,41 'cr': cr,
46 'uid': uid,42 'uid': uid,
47 'get_bank_statement' : self._get_bank_statement_data,43 'get_bank_statement': self._get_bank_statement_data,
48 'report_name': _('BORDEREAU DE REMISE DE CHEQUES'),44 'report_name': _('BORDEREAU DE REMISE DE CHEQUES'),
49 'additional_args': [45 'additional_args': [
50 ('--header-font-name', 'Helvetica'),46 ('--header-font-name', 'Helvetica'),
@@ -58,10 +54,15 @@
58 ('--footer-line',),54 ('--footer-line',),
59 ],55 ],
60 })56 })
61 def _get_bank_statement_data(self,statement):57
58 def _get_bank_statement_data(self, statement):
62 statement_obj = self.pool.get('account.bank.statement.line')59 statement_obj = self.pool.get('account.bank.statement.line')
63 statement_line_ids = statement_obj.search(self.cr,self.uid,[['statement_id','=',statement.id]])60 statement_line_ids = statement_obj.search(
64 statement_lines = statement_obj.browse(self.cr,self.uid,statement_line_ids)61 self.cr,
62 self.uid,
63 [('statement_id', '=', statement.id)])
64 statement_lines = statement_obj.browse(
65 self.cr, self.uid, statement_line_ids)
65 return statement_lines66 return statement_lines
6667
67webkit_report.WebKitParser('report.bank_statement_webkit',68webkit_report.WebKitParser('report.bank_statement_webkit',
6869
=== modified file 'account_statement_ext/security/ir.model.access.csv'
--- account_statement_ext/security/ir.model.access.csv 2012-09-20 08:37:42 +0000
+++ account_statement_ext/security/ir.model.access.csv 2013-06-10 07:05:32 +0000
@@ -1,3 +1,3 @@
1id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink1id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2access_account_bank_st_profile_user,account.statement.profile,model_account_statement_profile,account.group_account_user,1,0,0,02access_account_bank_st_profile_user,account.statement.profile,model_account_statement_profile,account.group_account_user,1,1,0,0
3access_account_bank_st_profile_manager,account.statement.profile,model_account_statement_profile,account.group_account_manager,1,1,1,13access_account_bank_st_profile_manager,account.statement.profile,model_account_statement_profile,account.group_account_manager,1,1,1,1
44
=== added file 'account_statement_ext/security/ir_rule.xml'
--- account_statement_ext/security/ir_rule.xml 1970-01-01 00:00:00 +0000
+++ account_statement_ext/security/ir_rule.xml 2013-06-10 07:05:32 +0000
@@ -0,0 +1,10 @@
1<openerp>
2 <data noupdate="1">
3 <record id="account_bank_statement_profile_rule" model="ir.rule">
4 <field name="name">Bank statement profile multi-company</field>
5 <field name="model_id" ref="model_account_statement_profile"/>
6 <field name="global" eval="True"/>
7 <field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
8 </record>
9 </data>
10</openerp>
0\ No newline at end of file11\ No newline at end of file
112
=== modified file 'account_statement_ext/statement.py'
--- account_statement_ext/statement.py 2013-04-04 11:14:03 +0000
+++ account_statement_ext/statement.py 2013-06-10 07:05:32 +0000
@@ -18,13 +18,25 @@
18# along with this program. If not, see <http://www.gnu.org/licenses/>.18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#19#
20##############################################################################20##############################################################################
2121import openerp.addons.account.account_bank_statement as stat_mod
22from tools.translate import _22from openerp.osv.orm import Model
23import datetime
24import netsvc
25logger = netsvc.Logger()
26from openerp.osv.orm import Model, fields
27from openerp.osv import fields, osv23from openerp.osv import fields, osv
24from openerp.tools.translate import _
25
26
27# Monkey patch to fix bad write implementation...
28def fixed_write(self, cr, uid, ids, vals, context=None):
29 """ Fix performance desing of original function
30 Ideally we should use a real PostgreSQL sequence or serial fields.
31 I will do it when I have time."""
32 res = super(stat_mod.account_bank_statement, self).write(cr, uid, ids,
33 vals, context=context)
34 cr.execute("UPDATE account_bank_statement_line"
35 " SET sequence = account_bank_statement_line.id + 1"
36 " where statement_id in %s", (tuple(ids),))
37 return res
38stat_mod.account_bank_statement.write = fixed_write
39
2840
29class AccountStatementProfil(Model):41class AccountStatementProfil(Model):
30 """42 """
@@ -33,38 +45,55 @@
33 journal to use, the partner and commision account and so on.45 journal to use, the partner and commision account and so on.
34 """46 """
35 _name = "account.statement.profile"47 _name = "account.statement.profile"
48 _inherit = ['mail.thread']
49
36 _description = "Statement Profil"50 _description = "Statement Profil"
37 51
38 _columns = {52 _columns = {
39 'name': fields.char('Name', size=128, required=True),53 'name': fields.char('Name', required=True),
40 'partner_id': fields.many2one('res.partner',54 'partner_id': fields.many2one(
41 'Bank/Payment Office partner',55 'res.partner',
42 help="Put a partner if you want to have it on the commission move \56 'Bank/Payment Office partner',
43 (and optionaly on the counterpart of the intermediate/banking move \57 help="Put a partner if you want to have it on the "
44 if you tic the corresponding checkbox)."),58 "commission move (and optionaly on the counterpart "
45 'journal_id': fields.many2one('account.journal',59 "of the intermediate/banking move if you tick the "
46 'Financial journal to use for transaction',60 "corresponding checkbox)."),
47 required=True),61
48 'commission_account_id': fields.many2one('account.account',62 'journal_id': fields.many2one(
49 'Commission account',63 'account.journal',
50 required=True),64 'Financial journal to use for transaction',
51 'commission_analytic_id': fields.many2one('account.analytic.account',65 required=True),
52 'Commission analytic account'),66
53 'receivable_account_id': fields.many2one('account.account',67 'commission_account_id': fields.many2one(
54 'Force Receivable/Payable Account',68 'account.account',
55 help="Choose a receivable account to force the default\69 'Commission account',
56 debit/credit account (eg. an intermediat bank account instead of\70 required=True),
57 default debitors)."),71
58 'force_partner_on_bank': fields.boolean('Force partner on bank move', 72 'commission_analytic_id': fields.many2one(
59 help="Tic that box if you want to use the credit insitute partner\73 'account.analytic.account',
60 in the counterpart of the intermediat/banking move."74 'Commission analytic account'),
61 ),75
62 'balance_check': fields.boolean('Balance check', 76 'receivable_account_id': fields.many2one(
63 help="Tic that box if you want OpenERP to control the start/end \77 'account.account',
64 balance before confirming a bank statement. If don't ticked, no \78 'Force Receivable/Payable Account',
65 balance control will be done."79 help="Choose a receivable account to force the default "
66 ),80 "debit/credit account (eg. an intermediat bank account "
81 "instead of default debitors)."),
82
83 'force_partner_on_bank': fields.boolean(
84 'Force partner on bank move',
85 help="Tick that box if you want to use the credit "
86 "institute partner in the counterpart of the "
87 "intermediate/banking move."),
88
89 'balance_check': fields.boolean(
90 'Balance check',
91 help="Tick that box if you want OpenERP to control "
92 "the start/end balance before confirming a bank statement. "
93 "If don't ticked, no balance control will be done."),
94
67 'bank_statement_prefix': fields.char('Bank Statement Prefix', size=32),95 'bank_statement_prefix': fields.char('Bank Statement Prefix', size=32),
96<<<<<<< TREE
68 'bank_statement_ids': fields.one2many('account.bank.statement', 'profile_id', 'Bank Statement Imported'),97 'bank_statement_ids': fields.one2many('account.bank.statement', 'profile_id', 'Bank Statement Imported'),
69 'internal_account_transfer_id': fields.many2one('account.account',98 'internal_account_transfer_id': fields.many2one('account.account',
70 'Internal Account Transfer',99 'Internal Account Transfer',
@@ -72,80 +101,115 @@
72 bank transfer")101 bank transfer")
73 102
74 103
104=======
105
106 'bank_statement_ids': fields.one2many('account.bank.statement',
107 'profile_id',
108 'Bank Statement Imported'),
109 'company_id': fields.many2one('res.company', 'Company'),
110>>>>>>> MERGE-SOURCE
75 }111 }
76 112
77 def _check_partner(self, cr, uid, ids, context=None):113 def _check_partner(self, cr, uid, ids, context=None):
78 obj = self.browse(cr, uid, ids[0], context=context)114 obj = self.browse(cr, uid, ids[0], context=context)
79 if obj.partner_id == False and obj.force_partner_on_bank:115 if obj.partner_id is False and obj.force_partner_on_bank:
80 return False116 return False
81 return True117 return True
82118
83 _constraints = [119 _constraints = [
84 (_check_partner, "You need to put a partner if you tic the 'Force partner on bank move' !", []),120 (_check_partner, "You need to put a partner if you tic the 'Force partner on bank move'!", []),
85 ]121 ]
86122
87123
88class AccountBankStatement(Model):124class AccountBankStatement(Model):
89 """125 """
90 We improve the bank statement class mostly for : 126 We improve the bank statement class mostly for :
91 - Removing the period and compute it from the date of each line. 127 - Removing the period and compute it from the date of each line.
92 - Allow to remove the balance check depending on the chosen profile128 - Allow to remove the balance check depending on the chosen profile
93 - Report errors on confirmation all at once instead of crashing onr by one129 - Report errors on confirmation all at once instead of crashing onr by one
94 - Add a profile notion that can change the generated entries on statement 130 - Add a profile notion that can change the generated entries on statement
95 confirmation.131 confirmation.
96 For this, we had to override quite some long method and we'll need to maintain132 For this, we had to override quite some long method and we'll need to maintain
97 them up to date. Changes are point up by '#Chg' comment.133 them up to date. Changes are point up by '#Chg' comment.
98 """134 """
99135
100 _inherit = "account.bank.statement"136 _inherit = "account.bank.statement"
101 137
138 def _default_period(self, cr, uid, context=None):
139 """
140 Statement default period
141 """
142 if context is None:
143 context = {}
144 period_obj = self.pool.get('account.period')
145 periods = period_obj.find(cr, uid, dt=context.get('date'), context=context)
146 return periods and periods[0] or False
147
102 _columns = {148 _columns = {
103 'profile_id': fields.many2one('account.statement.profile',149 'profile_id': fields.many2one(
104 'Profil', required=True, readonly=True, states={'draft': [('readonly', False)]}),150 'account.statement.profile',
151 'Profil',
152 required=True,
153 readonly=True,
154 states={'draft': [('readonly', False)]}),
105 'credit_partner_id': fields.related(155 'credit_partner_id': fields.related(
106 'profile_id', 156 'profile_id',
107 'partner_id', 157 'partner_id',
108 type='many2one', 158 type='many2one',
109 relation='res.partner', 159 relation='res.partner',
110 string='Financial Partner', 160 string='Financial Partner',
111 store=True, readonly=True),161 store=True,
162 readonly=True),
112 'balance_check': fields.related(163 'balance_check': fields.related(
113 'profile_id', 164 'profile_id',
114 'balance_check', 165 'balance_check',
115 type='boolean', 166 type='boolean',
116 string='Balance check', 167 string='Balance check',
117 store=True, readonly=True),168 store=True,
118 'journal_id': fields.related(169 readonly=True),
119 'profile_id', 170 'journal_id': fields.related(
120 'journal_id', 171 'profile_id',
172 'journal_id',
121 type='many2one',173 type='many2one',
122 relation='account.journal', 174 relation='account.journal',
123 string='Journal', 175 string='Journal',
124 store=True, readonly=True),176 store=True,
125 'period_id': fields.many2one('account.period', 'Period', required=False, readonly=True),177 readonly=True),
178 'period_id': fields.many2one(
179 'account.period',
180 'Period',
181 required=False,
182 readonly=False,
183 invisible=True),
126 }184 }
127185
128 _defaults = {186 _defaults = {
129 'period_id': lambda *a: False,187 'period_id': _default_period,
130 }188 }
131 189
132 def create(self, cr, uid, vals, context=None):190 def create(self, cr, uid, vals, context=None):
133 """Need to pass the journal_id in vals anytime because of account.cash.statement191 """Need to pass the journal_id in vals anytime because of account.cash.statement
134 need it."""192 need it."""
135 if 'profile_id' in vals:193 if 'profile_id' in vals:
136 profile_obj = self.pool.get('account.statement.profile')194 profile_obj = self.pool.get('account.statement.profile')
137 profile = profile_obj.browse(cr,uid,vals['profile_id'],context)195 profile = profile_obj.browse(cr, uid, vals['profile_id'], context=context)
138 vals['journal_id'] = profile.journal_id.id196 vals['journal_id'] = profile.journal_id.id
197<<<<<<< TREE
139 return super(AccountBankStatement, self).create(cr, uid, vals, context=context)198 return super(AccountBankStatement, self).create(cr, uid, vals, context=context)
140 199
141 def _get_period(self, cursor, uid, date, context=None):200 def _get_period(self, cursor, uid, date, context=None):
201=======
202 return super(AccountBankSatement, self).create(cr, uid, vals, context=context)
203
204 def _get_period(self, cr, uid, date, context=None):
205>>>>>>> MERGE-SOURCE
142 """206 """
143 Find matching period for date, used in the statement line creation.207 Find matching period for date, used in the statement line creation.
144 """208 """
145 period_obj = self.pool.get('account.period')209 period_obj = self.pool.get('account.period')
146 periods = period_obj.find(cursor, uid, dt=date, context=context)210 periods = period_obj.find(cr, uid, dt=date, context=context)
147 return periods and periods[0] or False211 return periods and periods[0] or False
148 212
149 def _check_company_id(self, cr, uid, ids, context=None):213 def _check_company_id(self, cr, uid, ids, context=None):
150 """214 """
151 Adapt this constraint method from the account module to reflect the215 Adapt this constraint method from the account module to reflect the
@@ -153,184 +217,111 @@
153 """217 """
154 for statement in self.browse(cr, uid, ids, context=context):218 for statement in self.browse(cr, uid, ids, context=context):
155 if (statement.period_id and219 if (statement.period_id and
156 statement.company_id.id != statement.period_id.company_id.id):220 statement.company_id.id != statement.period_id.company_id.id):
157 return False221 return False
158 for line in statement.line_ids:222 for line in statement.line_ids:
159 if (line.period_id and223 if (line.period_id and
160 statement.company_id.id != line.period_id.company_id.id):224 statement.company_id.id != line.period_id.company_id.id):
161 return False225 return False
162 return True226 return True
163227
164 _constraints = [228 _constraints = [
165 (_check_company_id, 'The journal and period chosen have to belong to the same company.', ['journal_id','period_id']),229 (_check_company_id,
230 'The journal and period chosen have to belong to the same company.',
231 ['journal_id', 'period_id']),
166 ]232 ]
167233
168 def button_cancel(self, cr, uid, ids, context={}):234 def _prepare_move(self, cr, uid, st_line, st_line_number, context=None):
169 """235 """Add the period_id from the statement line date to the move preparation.
170 We cancel the related move, delete them and finally put the236 Originaly, it was taken from the statement period_id
171 statement in draft state. So no need to unreconcile all entries,237 :param browse_record st_line: account.bank.statement.line record to
172 then unpost them, then finaly cancel the bank statement.238 create the move from.
173 """239 :param char st_line_number: will be used as the name of the generated account move
174 done = []240 :return: dict of value to create() the account.move
175 for st in self.browse(cr, uid, ids, context=context):241 """
176 if st.state=='draft':242 if context is None:
177 continue243 context = {}
178 ids = []244 res = super(AccountBankSatement, self)._prepare_move(
179 for line in st.line_ids:245 cr, uid, st_line, st_line_number, context=context)
180 for move in line.move_ids:246 ctx = context.copy()
181 if move.state <> 'draft':247 ctx['company_id'] = st_line.company_id.id
182 move.button_cancel(context=context)248 period_id = self._get_period(cr, uid, st_line.date, context=ctx)
183 move.unlink(context=context)249 res.update({'period_id': period_id})
184 done.append(st.id)250 return res
185 self.write(cr, uid, done, {'state':'draft'}, context=context)251
186 return True252 def _prepare_move_line_vals(
187253 self, cr, uid, st_line, move_id, debit, credit, currency_id=False,
188 def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, st_line_number, context=None):254 amount_currency=False, account_id=False, analytic_id=False,
189 """255 partner_id=False, context=None):
190 Override a large portion of the code to compute the periode for each line instead of256 """Add the period_id from the statement line date to the move preparation.
191 taking the period of the whole statement.257 Originaly, it was taken from the statement period_id
192 Remove the entry posting on generated account moves.258
259 :param browse_record st_line: account.bank.statement.line record to
260 create the move from.
261 :param int/long move_id: ID of the account.move to link the move line
262 :param float debit: debit amount of the move line
263 :param float credit: credit amount of the move line
264 :param int/long currency_id: ID of currency of the move line to create
265 :param float amount_currency: amount of the debit/credit expressed in the currency_id
266 :param int/long account_id: ID of the account to use in the move line if different
267 from the statement line account ID
268 :param int/long analytic_id: ID of analytic account to put on the move line
269 :param int/long partner_id: ID of the partner to put on the move line
270 :return: dict of value to create() the account.move.line
271 """
272 if context is None:
273 context = {}
274 res = super(AccountBankSatement, self)._prepare_move_line_vals(
275 cr, uid, st_line, move_id, debit, credit,
276 currency_id=currency_id,
277 amount_currency=amount_currency,
278 account_id=account_id,
279 analytic_id=analytic_id,
280 partner_id=partner_id, context=context)
281 ctx = context.copy()
282 ctx['company_id'] = st_line.company_id.id
283 period_id = self._get_period(cr, uid, st_line.date, context=ctx)
284 res.update({'period_id': period_id})
285 return res
286
287 def _get_counter_part_partner(self, cr, uid, st_line, context=None):
288 """
193 We change the move line generated from the lines depending on the profile:289 We change the move line generated from the lines depending on the profile:
194 - If receivable_account_id is set, we'll use it instead of the "partner" one
195 - If partner_id is set, we'll us it for the commission (when imported throufh the wizard)
196 - If partner_id is set and force_partner_on_bank is ticked, we'll let the partner of each line290 - If partner_id is set and force_partner_on_bank is ticked, we'll let the partner of each line
197 for the debit line, but we'll change it on the credit move line for the choosen partner_id291 for the debit line, but we'll change it on the credit move line for the choosen partner_id
198 => This will ease the reconciliation process with the bank as the partner will match the bank292 => This will ease the reconciliation process with the bank as the partner will match the bank
199 statement line293 statement line
200 294 :param browse_record st_line: account.bank.statement.line record to
201 :param int/long: st_line_id: account.bank.statement.line ID295 create the move from.
202 :param int/long: company_currency_id: res.currency ID296 :return: int/long of the res.partner to use as counterpart
203 :param char: st_line_number: that will be used as the name of the generated account move
204 :return: int/long: ID of the created account.move
205 """297 """
206 if context is None:298 bank_partner_id = super(AccountBankSatement, self)._get_counter_part_partner(cr,
207 context = {}299 uid,
208 res_currency_obj = self.pool.get('res.currency')300 st_line,
209 account_move_obj = self.pool.get('account.move')301 context=context)
210 account_move_line_obj = self.pool.get('account.move.line')302 # get the right partner according to the chosen profil
211 account_bank_statement_line_obj = self.pool.get('account.bank.statement.line') # Chg303 if st_line.statement_id.profile_id.force_partner_on_bank:
212 st_line = account_bank_statement_line_obj.browse(cr, uid, st_line_id, context=context) # Chg304 bank_partner_id = st_line.statement_id.profile_id.partner_id.id
213 st = st_line.statement_id305 return bank_partner_id
214
215 context.update({'date': st_line.date})
216 ctx = context.copy() # Chg
217 ctx['company_id'] = st_line.company_id.id # Chg
218 period_id = self._get_period( # Chg
219 cr, uid, st_line.date, context=ctx)
220
221 move_id = account_move_obj.create(cr, uid, {
222 'journal_id': st.journal_id.id,
223 'period_id': period_id, # Chg
224 'date': st_line.date,
225 'name': st_line_number,
226 'ref': st_line.ref,
227 }, context=context)
228 account_bank_statement_line_obj.write(cr, uid, [st_line.id], { # Chg
229 'move_ids': [(4, move_id, False)]
230 })
231
232 torec = []
233 if st_line.amount >= 0:
234 account_id = st.journal_id.default_credit_account_id.id
235 else:
236 account_id = st.journal_id.default_debit_account_id.id
237
238 acc_cur = ((st_line.amount<=0) and st.journal_id.default_debit_account_id) or st_line.account_id
239 context.update({
240 'res.currency.compute.account': acc_cur,
241 })
242 amount = res_currency_obj.compute(cr, uid, st.currency.id,
243 company_currency_id, st_line.amount, context=context)
244
245 val = {
246 'name': st_line.name,
247 'date': st_line.date,
248 'ref': st_line.ref,
249 'move_id': move_id,
250 'partner_id': ((st_line.partner_id) and st_line.partner_id.id) or False,
251 'account_id': (st_line.account_id) and st_line.account_id.id,
252 'credit': ((amount>0) and amount) or 0.0,
253 'debit': ((amount<0) and -amount) or 0.0,
254 # Replace with the treasury one instead of bank #Chg
255 'statement_id': st.id,
256 'journal_id': st.journal_id.id,
257 'period_id': period_id, #Chg
258 'currency_id': st.currency.id,
259 'analytic_account_id': st_line.analytic_account_id and st_line.analytic_account_id.id or False
260 }
261
262 if st.currency.id <> company_currency_id:
263 amount_cur = res_currency_obj.compute(cr, uid, company_currency_id,
264 st.currency.id, amount, context=context)
265 val['amount_currency'] = -amount_cur
266
267 if st_line.account_id and st_line.account_id.currency_id and st_line.account_id.currency_id.id <> company_currency_id:
268 val['currency_id'] = st_line.account_id.currency_id.id
269 amount_cur = res_currency_obj.compute(cr, uid, company_currency_id,
270 st_line.account_id.currency_id.id, amount, context=context)
271 val['amount_currency'] = -amount_cur
272
273 move_line_id = account_move_line_obj.create(cr, uid, val, context=context)
274 torec.append(move_line_id)
275
276 # Fill the secondary amount/currency
277 # if currency is not the same than the company
278 amount_currency = False
279 currency_id = False
280 if st.currency.id <> company_currency_id:
281 amount_currency = st_line.amount
282 currency_id = st.currency.id
283 # GET THE RIGHT PARTNER ACCORDING TO THE CHOSEN PROFIL # Chg
284 if st.profile_id.force_partner_on_bank: # Chg
285 bank_parrtner_id = st.profile_id.partner_id.id # Chg
286 else: # Chg
287 bank_parrtner_id = ((st_line.partner_id) and st_line.partner_id.id) or False # Chg
288
289 account_move_line_obj.create(cr, uid, {
290 'name': st_line.name,
291 'date': st_line.date,
292 'ref': st_line.ref,
293 'move_id': move_id,
294 'partner_id': bank_parrtner_id, # Chg
295 'account_id': account_id,
296 'credit': ((amount < 0) and -amount) or 0.0,
297 'debit': ((amount > 0) and amount) or 0.0,
298 # Replace with the treasury one instead of bank #Chg
299 'statement_id': st.id,
300 'journal_id': st.journal_id.id,
301 'period_id': period_id, #Chg
302 'amount_currency': amount_currency,
303 'currency_id': currency_id,
304 }, context=context)
305
306 for line in account_move_line_obj.browse(cr, uid, [x.id for x in
307 account_move_obj.browse(cr, uid, move_id,
308 context=context).line_id],
309 context=context):
310 if line.state <> 'valid':
311 raise osv.except_osv(_('Error !'),
312 _('Journal item "%s" is not valid.') % line.name)
313
314 # Bank statements will not consider boolean on journal entry_posted
315 account_move_obj.post(cr, uid, [move_id], context=context)
316 return move_id
317306
318 def _get_st_number_period_profile(self, cr, uid, date, profile_id):307 def _get_st_number_period_profile(self, cr, uid, date, profile_id):
319 """308 """
320 Retrieve the name of bank statement from sequence, according to the period 309 Retrieve the name of bank statement from sequence, according to the period
321 corresponding to the date passed in args. Add a prefix if set in the profile.310 corresponding to the date passed in args. Add a prefix if set in the profile.
322 311
323 :param: date: date of the statement used to compute the right period312 :param: date: date of the statement used to compute the right period
324 :param: int/long: profile_id: the account.statement.profile ID from which to take the313 :param: int/long: profile_id: the account.statement.profile ID from which to take the
325 bank_statement_prefix for the name314 bank_statement_prefix for the name
326 :return: char: name of the bank statement (st_number)315 :return: char: name of the bank statement (st_number)
327 316
328 """317 """
329 year = self.pool.get('account.period').browse(cr, uid, self._get_period(cr, uid, date)).fiscalyear_id.id318 year = self.pool.get('account.period').browse(
330 profile = self.pool.get('account.statement.profile').browse(cr,uid, profile_id)319 cr, uid, self._get_period(cr, uid, date)).fiscalyear_id.id
320 profile = self.pool.get('account.statement.profile').browse(cr, uid, profile_id)
331 c = {'fiscalyear_id': year}321 c = {'fiscalyear_id': year}
332 obj_seq = self.pool.get('ir.sequence')322 obj_seq = self.pool.get('ir.sequence')
333 journal_sequence_id = profile.journal_id.sequence_id and profile.journal_id.sequence_id.id or False323 journal_sequence_id = (profile.journal_id.sequence_id and
324 profile.journal_id.sequence_id.id or False)
334 if journal_sequence_id:325 if journal_sequence_id:
335 st_number = obj_seq.next_by_id(cr, uid, journal_sequence_id, context=c)326 st_number = obj_seq.next_by_id(cr, uid, journal_sequence_id, context=c)
336 else:327 else:
@@ -346,47 +337,48 @@
346 instead of having them pop one by one.337 instead of having them pop one by one.
347 We have to copy paste a big block of code, changing the error338 We have to copy paste a big block of code, changing the error
348 stack + managing period from date.339 stack + managing period from date.
349 340
350 TODO: Log the error in a bank statement field instead of using a popup !341 TODO: Log the error in a bank statement field instead of using a popup!
351 """342 """
352 # obj_seq = self.pool.get('irerrors_stack.sequence')
353 if context is None:
354 context = {}
355 for st in self.browse(cr, uid, ids, context=context):343 for st in self.browse(cr, uid, ids, context=context):
356344
357 j_type = st.journal_id.type345 j_type = st.journal_id.type
358 company_currency_id = st.journal_id.company_id.currency_id.id346 company_currency_id = st.journal_id.company_id.currency_id.id
359 if not self.check_status_condition(cr, uid, st.state, journal_type=j_type):347 if not self.check_status_condition(cr, uid, st.state, journal_type=j_type):
360 continue348 continue
361 349
362 self.balance_check(cr, uid, st.id, journal_type=j_type, context=context)350 self.balance_check(cr, uid, st.id, journal_type=j_type, context=context)
363 if (not st.journal_id.default_credit_account_id) \351 if (not st.journal_id.default_credit_account_id) \
364 or (not st.journal_id.default_debit_account_id):352 or (not st.journal_id.default_debit_account_id):
365 raise osv.except_osv(_('Configuration Error !'),353 raise osv.except_osv(_('Configuration Error!'),
366 _('Please verify that an account is defined in the journal.'))354 _('Please verify that an account is defined in the journal.'))
367355
368 if not st.name == '/':356 if not st.name == '/':
369 st_number = st.name357 st_number = st.name
370 else:358 else:
371# Begin Changes 359# Begin Changes
372 st_number = self._get_st_number_period_profile(cr, uid, st.date, st.profile_id.id)360 st_number = self._get_st_number_period_profile(cr, uid, st.date, st.profile_id.id)
373# End Changes 361# End Changes
374 for line in st.move_line_ids:362 for line in st.move_line_ids:
375 if line.state <> 'valid':363 if line.state != 'valid':
376 raise osv.except_osv(_('Error !'),364 raise osv.except_osv(_('Error!'),
377 _('The account entries lines are not in valid state.'))365 _('The account entries lines are not in valid state.'))
378# begin changes366# begin changes
379 errors_stack = []367 errors_stack = []
380 for st_line in st.line_ids:368 for st_line in st.line_ids:
381 try:369 try:
382 if st_line.analytic_account_id:370 if st_line.analytic_account_id:
383 if not st.journal_id.analytic_journal_id:371 if not st.journal_id.analytic_journal_id:
384 raise osv.except_osv(_('No Analytic Journal !'),372 raise osv.except_osv(_('No Analytic Journal!'),
385 _("You have to assign an analytic journal on the '%s' journal!") % (st.journal_id.name,))373 _("You have to assign an analytic"
374 " journal on the '%s' journal!") % st.journal_id.name)
386 if not st_line.amount:375 if not st_line.amount:
387 continue376 continue
388 st_line_number = self.get_next_st_line_number(cr, uid, st_number, st_line, context)377 st_line_number = self.get_next_st_line_number(cr, uid, st_number, st_line, context)
389 self.create_move_from_st_line(cr, uid, st_line.id, company_currency_id, st_line_number, context)378 self.create_move_from_st_line(cr, uid, st_line.id,
379 company_currency_id,
380 st_line_number,
381 context)
390 except osv.except_osv, exc:382 except osv.except_osv, exc:
391 msg = "Line ID %s with ref %s had following error: %s" % (st_line.id, st_line.ref, exc.value)383 msg = "Line ID %s with ref %s had following error: %s" % (st_line.id, st_line.ref, exc.value)
392 errors_stack.append(msg)384 errors_stack.append(msg)
@@ -397,72 +389,128 @@
397 msg = u"\n".join(errors_stack)389 msg = u"\n".join(errors_stack)
398 raise osv.except_osv(_('Error'), msg)390 raise osv.except_osv(_('Error'), msg)
399#end changes391#end changes
400 self.write(cr, uid, [st.id], {392 self.write(cr, uid, [st.id],
401 'name': st_number,393 {'name': st_number,
402 'balance_end_real': st.balance_end394 'balance_end_real': st.balance_end},
403 }, context=context)395 context=context)
404 self.log(cr, uid, st.id, _('Statement %s is confirmed, journal items are created.') % (st_number,))396 body = _('Statement %s confirmed, journal items were created.') % st_number
405 return self.write(cr, uid, ids, {'state':'confirm'}, context=context)397 self.message_post(cr, uid, [st.id],
406398 body,
407 def get_account_for_counterpart(self, cursor, uid,399 context=context)
408 amount, account_receivable, account_payable):400 return self.write(cr, uid, ids, {'state': 'confirm'}, context=context)
401
402 def get_account_for_counterpart(self, cr, uid, amount, account_receivable, account_payable):
403 """For backward compatibility."""
404 account_id, type = self.get_account_and_type_for_counterpart(cr, uid, amount,
405 account_receivable,
406 account_payable)
407 return account_id
408
409 def _compute_type_from_partner_profile(self, cr, uid, partner_id,
410 default_type, context=None):
411 """Compute the statement line type
412 from partner profile (customer, supplier)"""
413 obj_partner = self.pool.get('res.partner')
414 part = obj_partner.browse(cr, uid, partner_id, context=context)
415 if part.supplier == part.customer:
416 return default_type
417 if part.supplier:
418 return 'supplier'
419 else:
420 return 'customer'
421
422 def _compute_type_from_amount(self, cr, uid, amount):
423 """Compute the statement type based on amount"""
424 if amount in (None, False):
425 return 'general'
426 if amount < 0:
427 return 'supplier'
428 return 'customer'
429
430 def get_type_for_counterpart(self, cr, uid, amount, partner_id=False):
431 """Give the amount and receive the type to use for the line.
432 The rules are:
433 - If the customer checkbox is checked on the found partner, type customer
434 - If the supplier checkbox is checked on the found partner, typewill be supplier
435 - If both checkbox are checked or none of them, it'll be based on the amount :
436 If amount is positif the type customer,
437 If amount is negativ, the type supplier
438 :param float: amount of the line
439 :param int/long: partner_id the partner id
440 :return: type as string: the default type to use: 'customer' or 'supplier'.
441 """
442 s_line_type = self._compute_type_from_amount(cr, uid, amount)
443 if partner_id:
444 s_line_type = self._compute_type_from_partner_profile(cr, uid,
445 partner_id, s_line_type)
446 return s_line_type
447
448 def get_account_and_type_for_counterpart(self, cr, uid, amount, account_receivable,
449 account_payable, partner_id=False):
409 """450 """
410 Give the amount, payable and receivable account (that can be found using451 Give the amount, payable and receivable account (that can be found using
411 get_default_pay_receiv_accounts method) and receive the one to use. This method452 get_default_pay_receiv_accounts method) and receive the one to use. This method
412 should be use when there is no other way to know which one to take.453 should be use when there is no other way to know which one to take.
413 454 The rules are:
455 - If the customer checkbox is checked on the found partner, type and account will be customer and receivable
456 - If the supplier checkbox is checked on the found partner, type and account will be supplier and payable
457 - If both checkbox are checked or none of them, it'll be based on the amount :
458 If amount is positive, the type and account will be customer and receivable,
459 If amount is negative, the type and account will be supplier and payable
460 Note that we return the payable or receivable account from agrs and not from the optional partner_id
461 given!
462
414 :param float: amount of the line463 :param float: amount of the line
415 :param int/long: account_receivable the receivable account 464 :param int/long: account_receivable the receivable account
416 :param int/long: account_payable the payable account 465 :param int/long: account_payable the payable account
417 :return: int/long :the default account to be used by statement line as the counterpart466 :param int/long: partner_id the partner id
418 of the journal account depending on the amount.467 :return: dict with [account_id as int/long,type as string]: the default account to be used by
468 statement line as the counterpart of the journal account depending on the amount and the type
469 as 'customer' or 'supplier'.
419 """470 """
420 account_id = False471 account_id = False
421 if amount >= 0:472 ltype = self.get_type_for_counterpart(cr, uid, amount, partner_id=partner_id)
473 if ltype == 'supplier':
474 account_id = account_payable
475 else:
422 account_id = account_receivable476 account_id = account_receivable
423 else:
424 account_id = account_payable
425 if not account_id:477 if not account_id:
426 raise osv.except_osv(478 raise osv.except_osv(
427 _('Can not determine account'),479 _('Can not determine account'),
428 _('Please ensure that minimal properties are set')480 _('Please ensure that minimal properties are set')
429 )481 )
430 return account_id482 return [account_id, ltype]
431483
432 def get_default_pay_receiv_accounts(self, cursor, uid, context=None):484 def get_default_pay_receiv_accounts(self, cr, uid, context=None):
433 """485 """
434 We try to determine default payable/receivable accounts to be used as counterpart486 We try to determine default payable/receivable accounts to be used as counterpart
435 from the company default propoerty. This is to be used if there is no otherway to 487 from the company default propoerty. This is to be used if there is no otherway to
436 find the good one, or to find a default value that will be overriden by a completion 488 find the good one, or to find a default value that will be overriden by a completion
437 method (rules of account_statement_base_completion) afterwards.489 method (rules of account_statement_base_completion) afterwards.
438 490
439 :return: tuple of int/long ID that give account_receivable, account_payable based on491 :return: tuple of int/long ID that give account_receivable, account_payable based on
440 company default.492 company default.
441 """493 """
442 account_receivable = False494 account_receivable = False
443 account_payable = False495 account_payable = False
444 context = context or {}
445 property_obj = self.pool.get('ir.property')496 property_obj = self.pool.get('ir.property')
446 model_fields_obj = self.pool.get('ir.model.fields')497 model_fields_obj = self.pool.get('ir.model.fields')
447 model_fields_ids = model_fields_obj.search(498 model_fields_ids = model_fields_obj.search(
448 cursor,499 cr,
449 uid,500 uid,
450 [('name', 'in', ['property_account_receivable',501 [('name', 'in', ['property_account_receivable',
451 'property_account_payable']),502 'property_account_payable']),
452 ('model', '=', 'res.partner'),],503 ('model', '=', 'res.partner')],
453 context=context504 context=context
454 )505 )
455 property_ids = property_obj.search(506 property_ids = property_obj.search(cr,
456 cursor,507 uid,
457 uid, [508 [('fields_id', 'in', model_fields_ids),
458 ('fields_id', 'in', model_fields_ids),509 ('res_id', '=', False)],
459 ('res_id', '=', False),510 context=context)
460 ],511
461 context=context512 for erp_property in property_obj.browse(
462 )513 cr, uid, property_ids, context=context):
463
464 for erp_property in property_obj.browse(cursor, uid,
465 property_ids, context=context):
466 if erp_property.fields_id.name == 'property_account_receivable':514 if erp_property.fields_id.name == 'property_account_receivable':
467 account_receivable = erp_property.value_reference.id515 account_receivable = erp_property.value_reference.id
468 elif erp_property.fields_id.name == 'property_account_payable':516 elif erp_property.fields_id.name == 'property_account_payable':
@@ -473,56 +521,64 @@
473 """521 """
474 Balance check depends on the profile. If no check for this profile is required,522 Balance check depends on the profile. If no check for this profile is required,
475 return True and do nothing, otherwise call super.523 return True and do nothing, otherwise call super.
476 524
477 :param int/long st_id: ID of the concerned account.bank.statement 525 :param int/long st_id: ID of the concerned account.bank.statement
478 :param char: journal_type that concern the bank statement526 :param char: journal_type that concern the bank statement
479 :return: True527 :return: True
480 """528 """
481 st = self.browse(cr, uid, st_id, context=context)529 st = self.browse(cr, uid, st_id, context=context)
482 if st.balance_check:530 if st.balance_check:
531<<<<<<< TREE
483 return super(AccountBankStatement,self).balance_check(cr, uid, st_id, journal_type, context)532 return super(AccountBankStatement,self).balance_check(cr, uid, st_id, journal_type, context)
533=======
534 return super(AccountBankSatement, self).balance_check(
535 cr, uid, st_id, journal_type, context=context)
536>>>>>>> MERGE-SOURCE
484 else:537 else:
485 return True538 return True
486539
487 def onchange_imp_config_id(self, cr, uid, ids, profile_id, context=None):540 def onchange_imp_config_id(self, cr, uid, ids, profile_id, context=None):
488 """541 """
489 Compute values on the change of the profile.542 Compute values on the change of the profile.
490 543
491 :param: int/long: profile_id that changed544 :param: int/long: profile_id that changed
492 :return dict of dict with key = name of the field545 :return dict of dict with key = name of the field
493 """546 """
494 if not profile_id:547 if not profile_id:
495 return {}548 return {}
496 import_config = self.pool.get("account.statement.profile").browse(cr,uid,profile_id)549 import_config = self.pool.get("account.statement.profile").browse(
550 cr, uid, profile_id, context=context)
497 journal_id = import_config.journal_id.id551 journal_id = import_config.journal_id.id
498 account_id = import_config.journal_id.default_debit_account_id.id552 account_id = import_config.journal_id.default_debit_account_id.id
499 credit_partner_id = import_config.partner_id and import_config.partner_id.id or False553 credit_partner_id = import_config.partner_id and import_config.partner_id.id or False
500 return {'value': {'journal_id':journal_id, 'account_id': account_id,554 return {'value': {'journal_id': journal_id,
501 'balance_check':import_config.balance_check,555 'account_id': account_id,
502 'credit_partner_id':credit_partner_id,556 'balance_check': import_config.balance_check,
503 }}557 'credit_partner_id': credit_partner_id}}
504558
505559
506class AccountBankStatementLine(Model):560class AccountBankStatementLine(Model):
507 """561 """
508 Override to compute the period from the date of the line, add a method to retrieve562 Override to compute the period from the date of the line, add a method to retrieve
509 the values for a line from the profile. Override the on_change method to take care of 563 the values for a line from the profile. Override the on_change method to take care of
510 the profile when fullfilling the bank statement manually. Set the reference to 64 564 the profile when fullfilling the bank statement manually. Set the reference to 64
511 Char long instead 32.565 Char long instead 32.
512 """566 """
513 _inherit = "account.bank.statement.line"567 _inherit = "account.bank.statement.line"
514568
515 def _get_period(self, cursor, user, context=None):569 def _get_period(self, cr, uid, context=None):
516 """570 """
517 Return a period from a given date in the context.571 Return a period from a given date in the context.
518 """572 """
519 date = context.get('date', None)573 if context is None:
520 periods = self.pool.get('account.period').find(cursor, user, dt=date)574 context = {}
575 date = context.get('date')
576 periods = self.pool.get('account.period').find(cr, uid, dt=date)
521 return periods and periods[0] or False577 return periods and periods[0] or False
522578
523 def _get_default_account(self, cursor, user, context=None):579 def _get_default_account(self, cr, uid, context=None):
524 return self.get_values_for_line(cursor, user, context = context)['account_id']580 return self.get_values_for_line(cr, uid, context=context)['account_id']
525 581
526 _columns = {582 _columns = {
527 # Set them as required + 64 char instead of 32583 # Set them as required + 64 char instead of 32
528 'ref': fields.char('Reference', size=64, required=True),584 'ref': fields.char('Reference', size=64, required=True),
@@ -532,19 +588,25 @@
532 'period_id': _get_period,588 'period_id': _get_period,
533 'account_id': _get_default_account,589 'account_id': _get_default_account,
534 }590 }
535 591
536 def get_values_for_line(self, cr, uid, profile_id = False, partner_id = False, line_type = False, amount = False, context = None):592 def get_values_for_line(self, cr, uid, profile_id=False, partner_id=False, line_type=False, amount=False, master_account_id=None, context=None):
537 """593 """
538 Return the account_id to be used in the line of a bank statement. It'll base the result as follow:594 Return the account_id to be used in the line of a bank statement. It'll base the result as follow:
539 - If a receivable_account_id is set in the profile, return this value and type = general595 - If a receivable_account_id is set in the profile, return this value and type = general
540 - Elif line_type is given, take the partner receivable/payable property (payable if type= supplier, receivable596 # TODO
597 - Elif how_get_type_account is set to force_supplier or force_customer, will take respectively payable and type=supplier,
598 receivable and type=customer otherwise
599 # END TODO
600 - Elif line_type is given, take the partner receivable/payable property (payable if type=supplier, receivable
541 otherwise)601 otherwise)
542 - Elif amount is given, take the partner receivable/payable property (receivable if amount >= 0.0,602 - Elif amount is given:
543 payable otherwise). In that case, we also fullfill the type (receivable = customer, payable = supplier)603 - If the customer checkbox is checked on the found partner, type and account will be customer and receivable
544 so it is easier for the accountant to know why the receivable/payable has been chosen604 - If the supplier checkbox is checked on the found partner, type and account will be supplier and payable
605 - If both checkbox are checked or none of them, it'll be based on the amount :
606 If amount is positive, the type and account will be customer and receivable,
607 If amount is negative, the type and account will be supplier an payable
545 - Then, if no partner are given we look and take the property from the company so we always give a value608 - Then, if no partner are given we look and take the property from the company so we always give a value
546 for account_id. Note that in that case, we return the receivable one.609 for account_id. Note that in that case, we return the receivable one.
547
548 :param int/long profile_id of the related bank statement610 :param int/long profile_id of the related bank statement
549 :param int/long partner_id of the line611 :param int/long partner_id of the line
550 :param char line_type: a value from: 'general', 'supplier', 'customer'612 :param char line_type: a value from: 'general', 'supplier', 'customer'
@@ -557,20 +619,32 @@
557 ...619 ...
558 }620 }
559 """621 """
560 if context is None:
561 context = {}
562 res = {}622 res = {}
563 obj_partner = self.pool.get('res.partner')623 obj_partner = self.pool.get('res.partner')
564 obj_stat = self.pool.get('account.bank.statement')624 obj_stat = self.pool.get('account.bank.statement')
565 receiv_account = pay_account = account_id = False625 receiv_account = pay_account = account_id = False
566 # If profile has a receivable_account_id, we return it in any case626 # If profile has a receivable_account_id, we return it in any case
567 if profile_id:627 if master_account_id:
568 profile = self.pool.get("account.statement.profile").browse(cr,uid,profile_id)628 res['account_id'] = master_account_id
629 # We return general as default instead of get_type_for_counterpart
630 # for perfomance reasons as line_type is not a meaningfull value
631 # as account is forced
632 res['type'] = line_type if line_type else 'general'
633 return res
634 # To optimize we consider passing false means there is no account
635 # on profile
636 if profile_id and master_account_id is None:
637 profile = self.pool.get("account.statement.profile").browse(
638 cr, uid, profile_id, context=context)
569 if profile.receivable_account_id:639 if profile.receivable_account_id:
570 res['account_id'] = profile.receivable_account_id.id 640 res['account_id'] = profile.receivable_account_id.id
571 res['type'] = 'general'641 # We return general as default instead of get_type_for_counterpart
642 # for perfomance reasons as line_type is not a meaningfull value
643 # as account is forced
644 res['type'] = line_type if line_type else 'general'
572 return res645 return res
573 # If partner -> take from him646 # If no account is available on profile you have to do the lookup
647 # This can be quite a performance killer as we read ir.properity fields
574 if partner_id:648 if partner_id:
575 part = obj_partner.browse(cr, uid, partner_id, context=context)649 part = obj_partner.browse(cr, uid, partner_id, context=context)
576 pay_account = part.property_account_payable.id650 pay_account = part.property_account_payable.id
@@ -578,62 +652,53 @@
578 # If no value, look on the default company property652 # If no value, look on the default company property
579 if not pay_account or not receiv_account:653 if not pay_account or not receiv_account:
580 receiv_account, pay_account = obj_stat.get_default_pay_receiv_accounts(cr, uid, context=None)654 receiv_account, pay_account = obj_stat.get_default_pay_receiv_accounts(cr, uid, context=None)
581 # Now we have both pay and receive account, choose the one to use655 account_id, comp_line_type = obj_stat.get_account_and_type_for_counterpart(cr, uid, amount,
582 # based on line_type first, then amount, otherwise take receivable one.656 receiv_account, pay_account,
583 if line_type is not False:657 partner_id=partner_id)
584 if line_type == 'supplier':658 res['account_id'] = account_id if account_id else receiv_account
585 res['account_id'] = pay_account659 res['type'] = line_type if line_type else comp_line_type
586 else:
587 res['account_id'] = receiv_account
588 elif amount is not False:
589 if amount >= 0:
590 res['account_id'] = receiv_account
591 res['type'] = 'customer'
592 else:
593 res['account_id'] = pay_account
594 res['type'] = 'supplier'
595 if not account_id:
596 res['account_id'] = receiv_account
597 return res660 return res
598 661
599 662 def onchange_partner_id(self, cr, uid, ids, partner_id, profile_id=None, context=None):
600 def onchange_partner_id(self, cr, uid, ids, partner_id, profile_id, context=None):
601 """663 """
602 Override of the basic method as we need to pass the profile_id in the on_change_type664 Override of the basic method as we need to pass the profile_id in the on_change_type
603 call.665 call.
666 Moreover, we now call the get_account_and_type_for_counterpart method now to get the
667 type to use.
604 """668 """
605 obj_partner = self.pool.get('res.partner')669 obj_stat = self.pool.get('account.bank.statement')
606 if context is None:
607 context = {}
608 if not partner_id:670 if not partner_id:
609 return {}671 return {}
610 part = obj_partner.browse(cr, uid, partner_id, context=context)672 line_type = obj_stat.get_type_for_counterpart(cr, uid, 0.0, partner_id=partner_id)
611 if not part.supplier and not part.customer:673 res_type = self.onchange_type(cr, uid, ids, partner_id, line_type, profile_id, context=context)
612 type = 'general'
613 elif part.supplier and part.customer:
614 type = 'general'
615 else:
616 if part.supplier == True:
617 type = 'supplier'
618 if part.customer == True:
619 type = 'customer'
620 res_type = self.onchange_type(cr, uid, ids, partner_id, type, profile_id, context=context) # Chg
621 if res_type['value'] and res_type['value'].get('account_id', False):674 if res_type['value'] and res_type['value'].get('account_id', False):
622 return {'value': {'type': type, 'account_id': res_type['value']['account_id']}}675 return {'value': {'type': line_type,
623 return {'value': {'type': type}}676 'account_id': res_type['value']['account_id'],
624 677 'voucher_id': False}}
625 def onchange_type(self, cr, uid, line_id, partner_id, type, profile_id, context=None):678 return {'value': {'type': line_type}}
679
680 def onchange_type(self, cr, uid, line_id, partner_id, line_type, profile_id, context=None):
626 """681 """
627 Keep the same features as in standard and call super. If an account is returned,682 Keep the same features as in standard and call super. If an account is returned,
628 call the method to compute line values.683 call the method to compute line values.
629 """684 """
685<<<<<<< TREE
630 if context is None:686 if context is None:
631 context = {}687 context = {}
632 res = super(AccountBankStatementLine,self).onchange_type(cr, uid, line_id, partner_id, type, context)688 res = super(AccountBankStatementLine,self).onchange_type(cr, uid, line_id, partner_id, type, context)
689=======
690 res = super(AccountBankSatementLine, self).onchange_type(cr, uid,
691 line_id,
692 partner_id,
693 line_type,
694 context=context)
695>>>>>>> MERGE-SOURCE
633 if 'account_id' in res['value']:696 if 'account_id' in res['value']:
634 result = self.get_values_for_line(cr, uid, profile_id = profile_id, 697 result = self.get_values_for_line(cr, uid,
635 partner_id = partner_id, line_type = type, context = context)698 profile_id=profile_id,
699 partner_id=partner_id,
700 line_type=line_type,
701 context=context)
636 if result:702 if result:
637 res['value'].update({'account_id':result['account_id']})703 res['value'].update({'account_id': result['account_id']})
638 return res704 return res
639
640705
=== modified file 'account_statement_ext/statement_view.xml'
--- account_statement_ext/statement_view.xml 2013-04-04 11:14:03 +0000
+++ account_statement_ext/statement_view.xml 2013-06-10 07:05:32 +0000
@@ -2,6 +2,7 @@
2<openerp>2<openerp>
3<data>3<data>
44
5<<<<<<< TREE
5 <!-- Account Move Line : add statement_treasury_id -->6 <!-- Account Move Line : add statement_treasury_id -->
6 <record id="view_move_line_tree" model="ir.ui.view">7 <record id="view_move_line_tree" model="ir.ui.view">
7 <field name="name">account.move.line.tree</field>8 <field name="name">account.move.line.tree</field>
@@ -27,6 +28,8 @@
27 </field>28 </field>
28 </record>29 </record>
2930
31=======
32>>>>>>> MERGE-SOURCE
30 <record id="statement_importer_view_form" model="ir.ui.view">33 <record id="statement_importer_view_form" model="ir.ui.view">
31 <field name="name">account.statement.profile.view</field>34 <field name="name">account.statement.profile.view</field>
32 <field name="model">account.statement.profile</field>35 <field name="model">account.statement.profile</field>
@@ -36,6 +39,7 @@
36 <separator string="" colspan="4"/>39 <separator string="" colspan="4"/>
37 <field name="name" select="1" />40 <field name="name" select="1" />
38 <field name="partner_id" select="1"/>41 <field name="partner_id" select="1"/>
42 <field name="company_id" select="1" groups="base.group_multi_company"/>
39 <field name="journal_id" select="1"/>43 <field name="journal_id" select="1"/>
40 <field name="commission_account_id" />44 <field name="commission_account_id" />
41 <field name="commission_analytic_id" />45 <field name="commission_analytic_id" />
@@ -44,6 +48,7 @@
44 <field name="force_partner_on_bank"/>48 <field name="force_partner_on_bank"/>
45 <field name="balance_check"/>49 <field name="balance_check"/>
46 <field name="bank_statement_prefix"/>50 <field name="bank_statement_prefix"/>
51 <field name="message_ids" widget="mail_thread" placeholder="Share a note..." colspan="4"/>
47 </form>52 </form>
48 </field>53 </field>
49 </record>54 </record>
@@ -56,6 +61,7 @@
56 <tree string="Import statement">61 <tree string="Import statement">
57 <field name="name" />62 <field name="name" />
58 <field name="partner_id" />63 <field name="partner_id" />
64 <field name="company_id" groups="base.group_multi_company"/>
59 <field name="journal_id" />65 <field name="journal_id" />
60 <field name="commission_account_id" />66 <field name="commission_account_id" />
61 <field name="commission_analytic_id" />67 <field name="commission_analytic_id" />
@@ -72,6 +78,7 @@
72 <field name="view_mode">tree,form</field>78 <field name="view_mode">tree,form</field>
73 </record>79 </record>
7480
81<<<<<<< TREE
75 <menuitem string="Bank Statements Profile" action="action_treasury_statement_profile_tree" id="menu_treasury_statement_profile_tree" parent="account.menu_configuration_misc" sequence="30"/>82 <menuitem string="Bank Statements Profile" action="action_treasury_statement_profile_tree" id="menu_treasury_statement_profile_tree" parent="account.menu_configuration_misc" sequence="30"/>
7683
77 <record model="ir.ui.view" id="id_in_statement_line">84 <record model="ir.ui.view" id="id_in_statement_line">
@@ -87,19 +94,24 @@
87 </record>94 </record>
8895
8996
97=======
98 <menuitem string="Bank Statements Profile" action="action_treasury_statement_profile_tree" id="menu_treasury_statement_profile_tree" parent="account.menu_configuration_misc" sequence="30"/>
99
100
101
102>>>>>>> MERGE-SOURCE
90 <record id="view_treasury_statement_search" model="ir.ui.view">103 <record id="view_treasury_statement_search" model="ir.ui.view">
91 <field name="name">account.bank.statement.search</field>104 <field name="name">account.bank.statement.search</field>
92 <field name="model">account.bank.statement</field>105 <field name="model">account.bank.statement</field>
93 <field name="inherit_id" ref="account.view_bank_statement_search"/>106 <field name="inherit_id" ref="account.view_bank_statement_search"/>
94 <field name="type">search</field>
95 <field name="arch" type="xml">107 <field name="arch" type="xml">
96 <xpath expr="/search/group/field[@name='name']" position="before">108 <xpath expr="/search/field[@name='name']" position="before">
97 <field name="id"/>109 <field name="id"/>
98 <field name="profile_id"/>110 <field name="profile_id"/>
99 <field name="credit_partner_id"/>111 <field name="credit_partner_id"/>
100 <separator orientation="vertical"/>112 <separator orientation="vertical"/>
101 </xpath>113 </xpath>
102 <xpath expr="/search/group/field[@name='period_id']" position="replace">114 <xpath expr="/search/field[@name='period_id']" position="replace">
103 </xpath>115 </xpath>
104 <xpath expr="/search/group/filter[@string='Period']" position="replace">116 <xpath expr="/search/group/filter[@string='Period']" position="replace">
105 <filter string="Financial Partner" context="{'group_by': 'credit_partner_id'}" icon="terp-partner"/>117 <filter string="Financial Partner" context="{'group_by': 'credit_partner_id'}" icon="terp-partner"/>
@@ -125,6 +137,7 @@
125 </field>137 </field>
126 </record>138 </record>
127139
140<<<<<<< TREE
128141
129 <record id="view_treasury_statement_form" model="ir.ui.view">142 <record id="view_treasury_statement_form" model="ir.ui.view">
130 <field name="name">account.bank.statement.form</field>143 <field name="name">account.bank.statement.form</field>
@@ -197,6 +210,68 @@
197 </record>210 </record>
198211
199212
213=======
214
215 <record id="view_treasury_statement_form" model="ir.ui.view">
216 <field name="name">account.bank.statement.form</field>
217 <field name="model">account.bank.statement</field>
218 <field name="inherit_id" ref="account.view_bank_statement_form"/>
219 <field name="type">form</field>
220 <field name="arch" type="xml">
221
222 <!-- Add before the group : profile and related infos -->
223 <xpath expr="/form/sheet/group/group/field[@name='journal_id']" position="replace">
224 </xpath>
225
226 <xpath expr="/form/sheet/group" position="after">
227 <group>
228 <field name="profile_id" select="1" required="1" on_change="onchange_imp_config_id(profile_id)" widget="selection"/>
229 <separator string="Profile Details" colspan="4"/>
230 <field name="journal_id" domain="[('type', '=', 'bank')]" on_change="onchange_journal_id(journal_id)" widget="selection"/>
231 <field name="credit_partner_id"/>
232 <field name="account_id" invisible="1"/>
233 <field name="balance_check" invisible="1"/>
234 </group>
235 </xpath>
236
237 # Make balance visible or not depending on profile
238 <xpath expr="/form/sheet/group/group/field[@name='balance_start']" position="attributes">
239 <attribute name="attrs">{'invisible':[('balance_check','=',False)]}</attribute>
240 </xpath>
241 <xpath expr="/form/sheet/group/group/field[@name='balance_end_real']" position="attributes">
242 <attribute name="attrs">{'invisible':[('balance_check','=',False)]}</attribute>
243 </xpath>
244 <xpath expr="/form/sheet/group/group/field[@name='balance_end_real']" position="after">
245 <field name="balance_end" widget="monetary" options='{"currency_field" : "currency"}' attrs="{'invisible':[('balance_check','=',False)]}"/>
246 </xpath>
247
248 <xpath expr="/form/sheet/notebook/page/field/tree/field[@name='sequence']" position="after">
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches