resolve conflict

This commit is contained in:
Graeme Gellatly
2014-02-02 22:37:44 +13:00
29 changed files with 899 additions and 78 deletions

View File

@@ -35,7 +35,6 @@
1) Match from statement line label (based on partner field 'Bank Statement Label') 1) Match from statement line label (based on partner field 'Bank Statement Label')
2) Match from statement line label (based on partner name) 2) Match from statement line label (based on partner name)
3) Match from statement line reference (based on SO number)
3) Match from statement line reference (based on Invoice number) 3) Match from statement line reference (based on Invoice number)
You can easily override this module and add your own rules in your own one. The basic rules only You can easily override this module and add your own rules in your own one. The basic rules only
@@ -58,15 +57,19 @@
""", """,
'website': 'http://www.camptocamp.com', 'website': 'http://www.camptocamp.com',
'init_xml': [], 'data': [
'update_xml': [
'statement_view.xml', 'statement_view.xml',
'partner_view.xml', 'partner_view.xml',
'data.xml', 'data.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
], ],
'demo_xml': [], 'demo': [],
'test': [], 'test': [
'test/partner.yml',
'test/invoice.yml',
'test/supplier_invoice.yml',
'test/completion_test.yml'
],
'installable': True, 'installable': True,
'images': [], 'images': [],
'auto_install': False, 'auto_install': False,

View File

@@ -14,12 +14,6 @@
<field name="function_to_call">get_from_label_and_partner_name</field> <field name="function_to_call">get_from_label_and_partner_name</field>
</record> </record>
<record id="bank_statement_completion_rule_1" model="account.statement.completion.rule">
<field name="name">Match from line reference (based on SO number)</field>
<field name="sequence">50</field>
<field name="function_to_call">get_from_ref_and_so</field>
</record>
<record id="bank_statement_completion_rule_4" model="account.statement.completion.rule"> <record id="bank_statement_completion_rule_4" model="account.statement.completion.rule">
<field name="name">Match from line reference (based on Invoice number)</field> <field name="name">Match from line reference (based on Invoice number)</field>
<field name="sequence">40</field> <field name="sequence">40</field>

View File

@@ -23,15 +23,17 @@ import traceback
import sys import sys
import logging import logging
import simplejson import simplejson
import datetime import inspect
import psycopg2 import psycopg2
from collections import defaultdict from collections import defaultdict
import re import re
from openerp.tools.translate import _ from tools.translate import _
from openerp.osv import osv, orm, fields from openerp.osv import osv, orm, fields
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from operator import attrgetter from operator import attrgetter
import datetime
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -72,14 +74,13 @@ class AccountStatementProfil(orm.Model):
rel='as_rul_st_prof_rel'), rel='as_rul_st_prof_rel'),
} }
def _get_callable(self, cr, uid, profile, context=None): def _get_rules(self, cr, uid, profile, context=None):
if isinstance(profile, (int, long)): if isinstance(profile, (int, long)):
prof = self.browse(cr, uid, profile, context=context) prof = self.browse(cr, uid, profile, context=context)
else: else:
prof = profile prof = profile
# We need to respect the sequence order # We need to respect the sequence order
sorted_array = sorted(prof.rule_ids, key=attrgetter('sequence')) return sorted(prof.rule_ids, key=attrgetter('sequence'))
return tuple((x.function_to_call for x in sorted_array))
def _find_values_from_rules(self, cr, uid, calls, line, context=None): def _find_values_from_rules(self, cr, uid, calls, line, context=None):
""" """
@@ -98,12 +99,15 @@ class AccountStatementProfil(orm.Model):
if context is None: if context is None:
context = {} context = {}
if not calls: if not calls:
calls = self._get_callable(cr, uid, line['profile_id'], context=context) calls = self._get_rules(cr, uid, line['profile_id'], context=context)
rule_obj = self.pool.get('account.statement.completion.rule') rule_obj = self.pool.get('account.statement.completion.rule')
for call in calls: for call in calls:
method_to_call = getattr(rule_obj, call) method_to_call = getattr(rule_obj, call.function_to_call)
result = method_to_call(cr, uid, line, context) if len(inspect.getargspec(method_to_call).args) == 6:
result = method_to_call(cr, uid, call.id, line, context)
else:
result = method_to_call(cr, uid, line, context)
if result: if result:
result['already_completed'] = True result['already_completed'] = True
return result return result
@@ -131,7 +135,6 @@ class AccountStatementCompletionRule(orm.Model):
return [ return [
('get_from_ref_and_invoice', 'From line reference (based on customer invoice number)'), ('get_from_ref_and_invoice', 'From line reference (based on customer invoice number)'),
('get_from_ref_and_supplier_invoice', 'From line reference (based on supplier invoice number)'), ('get_from_ref_and_supplier_invoice', 'From line reference (based on supplier invoice number)'),
('get_from_ref_and_so', 'From line reference (based on SO number)'),
('get_from_label_and_partner_field', 'From line label (based on partner field)'), ('get_from_label_and_partner_field', 'From line label (based on partner field)'),
('get_from_label_and_partner_name', 'From line label (based on partner name)')] ('get_from_label_and_partner_name', 'From line label (based on partner name)')]
@@ -231,49 +234,6 @@ class AccountStatementCompletionRule(orm.Model):
""" """
return self._from_invoice(cr, uid, line, 'customer', context=context) return self._from_invoice(cr, uid, line, 'customer', context=context)
# Should be private but data are initialised with no update XML
def get_from_ref_and_so(self, cr, uid, st_line, context=None):
"""
Match the partner based on the SO number and the reference of the statement
line. Then, call the generic get_values_for_line method to complete other values.
If more than one partner matched, raise the ErrorTooManyPartner error.
:param int/long st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id': value,
...}
"""
st_obj = self.pool.get('account.bank.statement.line')
res = {}
if st_line:
so_obj = self.pool.get('sale.order')
so_id = so_obj.search(cr,
uid,
[('name', '=', st_line['ref'])],
context=context)
if so_id:
if so_id and len(so_id) == 1:
so = so_obj.browse(cr, uid, so_id[0], context=context)
res['partner_id'] = so.partner_id.id
elif so_id and len(so_id) > 1:
raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more '
'than one partner while looking on SO by ref.') %
(st_line['name'], st_line['ref']))
st_vals = st_obj.get_values_for_line(cr,
uid,
profile_id=st_line['profile_id'],
master_account_id=st_line['master_account_id'],
partner_id=res.get('partner_id', False),
line_type='customer',
amount=st_line['amount'] if st_line['amount'] else 0.0,
context=context)
res.update(st_vals)
return res
# Should be private but data are initialised with no update XML # Should be private but data are initialised with no update XML
def get_from_label_and_partner_field(self, cr, uid, st_line, context=None): def get_from_label_and_partner_field(self, cr, uid, st_line, context=None):
""" """
@@ -569,7 +529,7 @@ class AccountBankSatement(orm.Model):
ctx = context.copy() ctx = context.copy()
ctx['line_ids'] = tuple((x.id for x in stat.line_ids)) ctx['line_ids'] = tuple((x.id for x in stat.line_ids))
b_profile = stat.profile_id b_profile = stat.profile_id
rules = profile_obj._get_callable(cr, uid, b_profile, context=context) rules = profile_obj._get_rules(cr, uid, b_profile, context=context)
profile_id = b_profile.id # Only for perfo even it gains almost nothing profile_id = b_profile.id # Only for perfo even it gains almost nothing
master_account_id = b_profile.receivable_account_id master_account_id = b_profile.receivable_account_id
master_account_id = master_account_id.id if master_account_id else False master_account_id = master_account_id.id if master_account_id else False

View File

@@ -0,0 +1,87 @@
-
In order to test the banking framework, I first need to create a profile
-
!record {model: account.statement.profile, id: profile_test1}:
name: Bank EUR Profile
journal_id: account.bank_journal
commission_account_id: account.a_expense
company_id: base.main_company
balance_check: True
rule_ids:
- bank_statement_completion_rule_4
- bank_statement_completion_rule_5
- bank_statement_completion_rule_2
- bank_statement_completion_rule_3
-
Now I create a statement. I create statment lines separately because I need
to find each one by XML id
-
!record {model: account.bank.statement, id: statement_test1}:
name: Statement 2
profile_id: profile_test1
company_id: base.main_company
-
I create a statement line for a CI
-
!record {model: account.bank.statement.line, id: statement_line_ci}:
name: Test autocompletion based on Customer Invoice Number
statement_id: statement_test1
ref: CI0001
date: '2013-12-20'
amount: 210.0
-
I create a statement line for a SI
-
!record {model: account.bank.statement.line, id: statement_line_si}:
name: Test autocompletion based on Supplier Invoice Number
statement_id: statement_test1
ref: T2S12345
date: '2013-12-19'
amount: -65.0
-
I create a statement line for the Partner Name
-
!record {model: account.bank.statement.line, id: statement_line_partner_name}:
name: Test autocompletion based on Partner Name Vauxoo
statement_id: statement_test1
ref: /
date: '2013-12-17'
amount: 600.0
-
I create a statement line for the Partner Label
-
!record {model: account.bank.statement.line, id: statement_line_partner_label}:
name: test autocompletion based on text (XXX66Z) matching with partner form information (note that Ref does not exist)
statement_id: statement_test1
ref: ZU788
date: '2013-12-24'
amount: -932.4
-
I run the auto complete
-
!python {model: account.bank.statement}: |
result = self.button_auto_completion(cr, uid, [ref("statement_test1")])
-
Now I can check that all is nice and shiny, line 1. I expect the Customer
Invoice Number to be recognised.
I Use _ref, because ref conflicts with the field ref of the statement line
-
!assert {model: account.bank.statement.line, id: statement_line_ci, string: Check completion by CI number}:
- partner_id.id == _ref("base.res_partner_12")
-
Line 2. I expect the Supplier invoice number to be recognised. The supplier
invoice was created by the account module demo data, and we confirmed it
here.
-
!assert {model: account.bank.statement.line, id: statement_line_si, string: Check completion by SI number}:
- partner_id.id == _ref("base.res_partner_17")
-
Line 3. I check that the partner name has been recognised.
-
!assert {model: account.bank.statement.line, id: statement_line_partner_name, string: Check completion by partner name}:
- partner_id.name == 'Vauxoo'
-
Line 4. I check that the partner special label has been recognised.
-
!assert {model: account.bank.statement.line, id: statement_line_partner_label, string: Check completion by partner label}:
- partner_id.id == _ref("base.res_partner_6")

View File

@@ -0,0 +1,32 @@
-
I create a customer Invoice to be found by the completion.
-
!record {model: account.invoice, id: invoice_for_completion_1}:
account_id: account.a_recv
company_id: base.main_company
currency_id: base.EUR
internal_number: CI0001
invoice_line:
- account_id: account.a_sale
name: '[PCSC234] PC Assemble SC234'
price_unit: 210.0
quantity: 1.0
product_id: product.product_product_3
uos_id: product.product_uom_unit
journal_id: account.bank_journal
partner_id: base.res_partner_12
reference_type: none
-
I confirm the Invoice
-
!workflow {model: account.invoice, action: invoice_open, ref: invoice_for_completion_1}
-
I check that the invoice state is "Open"
-
!assert {model: account.invoice, id: invoice_for_completion_1}:
- state == 'open'
-
I check that it is given the number "CI0001"
-
!assert {model: account.invoice, id: invoice_for_completion_1, string: Check CI number}:
- number == 'CI0001'

View File

@@ -0,0 +1,5 @@
-
I fill in the field Bank Statement Label in a Partner
-
!record {model: res.partner, id: base.res_partner_6}:
bank_statement_label: XXX66Z

View File

@@ -0,0 +1,31 @@
-
I check that my invoice is a supplier invoice
-
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check invoice type}:
- type == 'in_invoice'
-
I add a reference to an existing supplier invoce
-
!python {model: account.invoice}: |
self.write(cr, uid, ref('account.demo_invoice_0'), {
'supplier_invoice_number': 'T2S12345'
})
-
I check a second time that my invoice is still a supplier invoice
-
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check invoice type 2}:
- type == 'in_invoice'
-
Now I confirm it
-
!workflow {model: account.invoice, action: invoice_open, ref: account.demo_invoice_0}
-
I check that the supplier number is there
-
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check supplier number}:
- supplier_invoice_number == 'T2S12345'
-
I check a third time that my invoice is still a supplier invoice
-
!assert {model: account.invoice, id: account.demo_invoice_0, string: Check invoice type 3}:
- type == 'in_invoice'

View File

@@ -20,6 +20,7 @@
############################################################################## ##############################################################################
import base64 import base64
import csv import csv
from datetime import datetime
def UnicodeDictReader(utf8_data, **kwargs): def UnicodeDictReader(utf8_data, **kwargs):
@@ -49,6 +50,10 @@ class BankStatementImportParser(object):
self.result_row_list = None self.result_row_list = None
# The file buffer on which to work on # The file buffer on which to work on
self.filebuffer = None self.filebuffer = None
self.balance_start = None
self.balance_end = None
self.statement_name = None
self.statement_date = None
@classmethod @classmethod
def parser_for(cls, parser_name): def parser_for(cls, parser_name):
@@ -150,6 +155,37 @@ class BankStatementImportParser(object):
self._post(*args, **kwargs) self._post(*args, **kwargs)
return self.result_row_list return self.result_row_list
def get_start_balance(self, *args, **kwargs):
"""
This is called by the importation method to set the balance start
amount in the bank statement.
return: float of the balance start (self.balance_start)
"""
return self.balance_start
def get_end_balance(self, *args, **kwargs):
"""
This is called by the importation method to set the balance end
amount in the bank statement.
return: float of the balance end (self.balance_end)
"""
return self.balance_end
def get_statement_name(self, *args, **kwargs):
"""
This is called by the importation method to set the statement
name in the bank statement.
return: string of the statement name (self.statement_name)
"""
return self.statement_name or '/'
def get_statement_date(self, *args, **kwargs):
"""
This is called by the importation method to set the statement
date in the bank statement.
return: datetime of the statement date (self.statement_date)
"""
return self.statement_date or datetime.now()
def itersubclasses(cls, _seen=None): def itersubclasses(cls, _seen=None):
""" """

View File

@@ -126,6 +126,15 @@ class AccountStatementProfil(Model):
values['type'] = 'general' values['type'] = 'general'
return values return values
def _prepare_statement_vals(self, cr, uid, prof, parser, context=None):
return {
'profile_id': prof.id,
'name': parser.get_statement_name(),
'balance_start': parser.get_start_balance(),
'balance_end_real': parser.get_end_balance(),
}
def statement_import(self, cr, uid, ids, profile_id, file_stream, ftype="csv", context=None): def statement_import(self, cr, uid, ids, profile_id, file_stream, ftype="csv", context=None):
""" """
Create a bank statement with the given profile and parser. It will fullfill the bank statement Create a bank statement with the given profile and parser. It will fullfill the bank statement
@@ -159,9 +168,8 @@ class AccountStatementProfil(Model):
_("Column %s you try to import is not " _("Column %s you try to import is not "
"present in the bank statement line!") % col) "present in the bank statement line!") % col)
statement_id = statement_obj.create(cr, uid, st_vals = self._prepare_statement_vals(cr, uid, prof, parser, context=context)
{'profile_id': prof.id}, statement_id = statement_obj.create(cr, uid, st_vals, context=context)
context=context)
if prof.receivable_account_id: if prof.receivable_account_id:
account_receivable = account_payable = prof.receivable_account_id.id account_receivable = account_payable = prof.receivable_account_id.id
else: else:

View File

@@ -97,6 +97,7 @@ class CreditPartnerStatementImporter(orm.TransientModel):
req_id = req_id[0] req_id = req_id[0]
importer = self.browse(cr, uid, req_id, context) importer = self.browse(cr, uid, req_id, context)
ftype = self._check_extension(importer.file_name) ftype = self._check_extension(importer.file_name)
context['file_name'] = importer.file_name
sid = self.pool.get( sid = self.pool.get(
'account.statement.profile').statement_import( 'account.statement.profile').statement_import(
cr, cr,

View File

@@ -22,7 +22,7 @@ class AccountStatementProfil(orm.Model):
commission_analytic_id = profile.commission_analytic_id and profile.commission_analytic_id.id or False commission_analytic_id = profile.commission_analytic_id and profile.commission_analytic_id.id or False
comm_values = { comm_values = {
'name': 'IN ' + _('Commission line'), 'name': 'IN ' + _('Commission line'),
'date': datetime.datetime.now().date(), 'date': parser.get_statement_date(),
'amount': global_commission_amount, 'amount': global_commission_amount,
'partner_id': partner_id, 'partner_id': partner_id,
'type': 'general', 'type': 'general',
@@ -65,4 +65,4 @@ class CreditPartnerStatementImporter(orm.TransientModel):
c.commission_account_id and c.commission_account_id.id or False c.commission_account_id and c.commission_account_id.id or False
res['value']['commission_a'] = \ res['value']['commission_a'] = \
c.commission_analytic_id and c.commission_analytic_id.id or False c.commission_analytic_id and c.commission_analytic_id.id or False
return res return res

View File

@@ -31,8 +31,11 @@ class account_move(Model):
Delete the reconciliation when we delete the moves. This Delete the reconciliation when we delete the moves. This
allow an easier way of cancelling the bank statement. allow an easier way of cancelling the bank statement.
""" """
reconcile_to_delete = []
reconcile_obj = self.pool.get('account.move.reconcile')
for move in self.browse(cr, uid, ids, context=context): for move in self.browse(cr, uid, ids, context=context):
for move_line in move.line_id: for move_line in move.line_id:
if move_line.reconcile_id: if move_line.reconcile_id:
move_line.reconcile_id.unlink(context=context) reconcile_to_delete.append(move_line.reconcile_id.id)
reconcile_obj.unlink(cr, uid, reconcile_to_delete, context=context)
return super(account_move, self).unlink(cr, uid, ids, context=context) return super(account_move, self).unlink(cr, uid, ids, context=context)

View File

@@ -223,12 +223,17 @@ class AccountBankSatement(Model):
move of period_id to the statement line move of period_id to the statement line
""" """
for statement in self.browse(cr, uid, ids, context=context): for statement in self.browse(cr, uid, ids, context=context):
# statement.company_id is a related store=True that for some
# reason doesn't work in YAML tests. As a workaround, I unwind it
# to statement.journal_id.company_id here.
if (statement.period_id and if (statement.period_id and
statement.company_id.id != statement.period_id.company_id.id): statement.journal_id.company_id.id !=
statement.period_id.company_id.id):
return False return False
for line in statement.line_ids: for line in statement.line_ids:
if (line.period_id and if (line.period_id and
statement.company_id.id != line.period_id.company_id.id): statement.journal_id.company_id.id
!= line.period_id.company_id.id):
return False return False
return True return True
@@ -536,12 +541,8 @@ class AccountBankSatement(Model):
import_config = self.pool.get("account.statement.profile").browse( import_config = self.pool.get("account.statement.profile").browse(
cr, uid, profile_id, context=context) cr, uid, profile_id, context=context)
journal_id = import_config.journal_id.id journal_id = import_config.journal_id.id
account_id = import_config.journal_id.default_debit_account_id.id
credit_partner_id = import_config.partner_id and import_config.partner_id.id or False
return {'value': {'journal_id': journal_id, return {'value': {'journal_id': journal_id,
'account_id': account_id, 'balance_check': import_config.balance_check}}
'balance_check': import_config.balance_check,
'credit_partner_id': credit_partner_id}}
class AccountBankSatementLine(Model): class AccountBankSatementLine(Model):

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Authors: Laetitia Gangloff
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import statement
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Authors: Laetitia Gangloff
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
"name": "Account Statement Regex Account Completion addon",
"version": "0.1",
"author": "ACSONE SA/NV",
"category": "Other",
"website": "http://www.acsone.eu",
"depends": ["account_statement_base_completion",
],
"description": """
Account Statement Regex Account Completion addon
=========================
- Add a completion method based on a specified regular expression
and update account to use in the bank statement line with the specified account.
""",
"data": ['statement_view.xml',
],
"demo": [],
"test": [],
"active": False,
"license": "AGPL-3",
"installable": True,
"auto_install": False,
"application": False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -0,0 +1,32 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * account_statement_regex_account_completion
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-01-22 10:59+0000\n"
"PO-Revision-Date: 2014-01-22 10:59+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_statement_regex_account_completion
#: field:account.statement.completion.rule,regex:0
msgid "Regular Expression"
msgstr ""
#. module: account_statement_regex_account_completion
#: field:account.statement.completion.rule,account_id:0
msgid "Account to set"
msgstr ""
#. module: account_statement_regex_account_completion
#: model:ir.model,name:account_statement_regex_account_completion.model_account_statement_completion_rule
msgid "account.statement.completion.rule"
msgstr ""

View File

@@ -0,0 +1,32 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * account_statement_regex_account_completion
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-01-22 10:59+0000\n"
"PO-Revision-Date: 2014-01-23 17:42+0000\n"
"Last-Translator: Laetitia Gangloff (Acsone) <laetitia.gangloff@acsone.eu>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-01-24 06:27+0000\n"
"X-Generator: Launchpad (build 16914)\n"
#. module: account_statement_regex_account_completion
#: field:account.statement.completion.rule,regex:0
msgid "Regular Expression"
msgstr "Expression Régulière"
#. module: account_statement_regex_account_completion
#: field:account.statement.completion.rule,account_id:0
msgid "Account to set"
msgstr "Compte à utiliser"
#. module: account_statement_regex_account_completion
#: model:ir.model,name:account_statement_regex_account_completion.model_account_statement_completion_rule
msgid "account.statement.completion.rule"
msgstr "account.statement.completion.rule"

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Authors: Laetitia Gangloff
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv.orm import Model
from openerp.osv import fields
import re
class AccountStatementCompletionRule(Model):
"""Add a rule to complete account based on a regular expression"""
_inherit = "account.statement.completion.rule"
def _get_functions(self, cr, uid, context=None):
res = super(AccountStatementCompletionRule, self)._get_functions(
cr, uid, context=context)
res.append(('set_account',
'Set account for line labels matching a regular expression'))
return res
_columns = {
'function_to_call': fields.selection(_get_functions, 'Method'),
'regex': fields.char('Regular Expression', size=128),
'account_id': fields.many2one('account.account', string="Account to set"),
}
def set_account(self, cr, uid, id, st_line, context=None):
"""
If line name match regex, update account_id
Then, call the generic st_line method to complete other values.
:param dict st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id' : value,
...}
"""
name = st_line['name']
res = {}
if name:
rule = self.browse(cr, uid, id, context=context)
if re.match(rule.regex, name):
res['account_id'] = rule.account_id.id
return res
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="statement_st_completion_rule_view_form" model="ir.ui.view">
<field name="name">account.statement.completion.rule.view (account_statement_regex_account_completion)</field>
<field name="model">account.statement.completion.rule</field>
<field name="inherit_id" ref="account_statement_base_completion.statement_st_completion_rule_view_form" />
<field name="type">form</field>
<field name="arch" type="xml">
<field name="function_to_call" position="after">
<group colspan="2">
<field name="regex" attrs="{'invisible':[('function_to_call','!=','set_account')],'required':[('function_to_call','=','set_account')]}"/>
<field name="account_id" attrs="{'invisible':[('function_to_call','!=','set_account')],'required':[('function_to_call','=','set_account')]}"/>
</group>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Authors: Laetitia Gangloff
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_regex_account_completion
checks = [
test_regex_account_completion
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Authors: Laetitia Gangloff
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.tests import common
import time
ACC_NUMBER = "BE38733040385372"
class test_regex_account_completion(common.TransactionCase):
def prepare(self):
self.account_bank_statement_obj = self.registry("account.bank.statement")
self.account_bank_statement_line_obj = self.registry("account.bank.statement.line")
self.account_id = self.ref('account.a_expense')
# create the completion rule
rule_vals = {'function_to_call': 'set_account',
'regex': '^My statement',
'account_id': self.account_id}
completion_rule_id = self.registry("account.statement.completion.rule").create(self.cr, self.uid, rule_vals)
# Create the profile
journal_id = self.ref("account.bank_journal")
profile_id = self.registry("account.statement.profile").create(self.cr, self.uid, {
"name": "TEST",
"commission_account_id": self.ref("account.a_recv"),
"journal_id": journal_id,
"rule_ids": [(6, 0, [completion_rule_id])]})
# Create a bank statement
self.statement_id = self.account_bank_statement_obj.create(self.cr, self.uid, {
"balance_end_real": 0.0,
"balance_start": 0.0,
"date": time.strftime('%Y-%m-%d'),
"journal_id": journal_id,
"profile_id": profile_id
})
# Create two bank statement lines
self.statement_line1_id = self.account_bank_statement_line_obj.create(self.cr, self.uid, {
'amount': 1000.0,
'name': 'My statement',
'ref': 'My ref',
'statement_id': self.statement_id,
'partner_acc_number': ACC_NUMBER
})
self.statement_line2_id = self.account_bank_statement_line_obj.create(self.cr, self.uid, {
'amount': 2000.0,
'name': 'My second statement',
'ref': 'My second ref',
'statement_id': self.statement_id,
'partner_acc_number': ACC_NUMBER
})
def test_00(self):
"""Test the automatic completion on account
"""
self.prepare()
statement_obj = self.account_bank_statement_obj.browse(self.cr, self.uid, self.statement_id)
statement_obj.button_auto_completion()
statement_line1 = self.account_bank_statement_line_obj.browse(self.cr, self.uid, self.statement_line1_id)
self.assertEquals(self.account_id, statement_line1.account_id.id, "The account should be the account of the completion")
statement_line2 = self.account_bank_statement_line_obj.browse(self.cr, self.uid, self.statement_line2_id)
self.assertNotEqual(self.account_id, statement_line2.account_id.id, "The account should be not the account of the completion")

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
###############################################################################
# #
# Author: Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
# #
# Author: Leonardo Pistone <leonardo.pistone@camptocamp.com> #
# Copyright 2013 Camptocamp SA #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU Affero General Public License as #
# published by the Free Software Foundation, either version 3 of the #
# License, or (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU Affero General Public License for more details. #
# #
# You should have received a copy of the GNU Affero General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
from . import statement

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
###############################################################################
# #
# Author: Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
# #
# Author: Leonardo Pistone <leonardo.pistone@camptocamp.com> #
# Copyright 2013 Camptocamp SA #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU Affero General Public License as #
# published by the Free Software Foundation, either version 3 of the #
# License, or (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU Affero General Public License for more details. #
# #
# You should have received a copy of the GNU Affero General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
{'name': "Bank statement Sale Order completion",
'version': '0.1',
'author': 'Camptocamp',
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'easy',
'depends': ['account_statement_base_completion', 'sale'],
'description': """
This module improve the module account_statement_base_completion to add
support for completion rules based on Sale Orders. This was initially part of
the module account_statement_base_completion, but is now separate to keep
dependencies separate.
This module provides the following rule:
1) Match from statement line reference (based on SO number)
""",
'website': 'http://www.camptocamp.com',
'data': [
'data.xml',
],
'test': [
'test/completion_so_test.yml'],
'installable': True,
'images': [],
'auto_install': True,
'license': 'AGPL-3',
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="bank_statement_completion_rule_1" model="account.statement.completion.rule">
<field name="name">Match from line reference (based on SO number)</field>
<field name="sequence">50</field>
<field name="function_to_call">get_from_ref_and_so</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
###############################################################################
# #
# Author: Joel Grand-Guillaume #
# Copyright 2011-2012 Camptocamp SA #
# #
# Author: Leonardo Pistone <leonardo.pistone@camptocamp.com> #
# Copyright 2013 Camptocamp SA #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU Affero General Public License as #
# published by the Free Software Foundation, either version 3 of the #
# License, or (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU Affero General Public License for more details. #
# #
# You should have received a copy of the GNU Affero General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
from openerp.osv import fields, orm
from tools.translate import _
from openerp.addons.account_statement_base_completion.statement import ErrorTooManyPartner
class account_statement_completion_rule(orm.Model):
_name = "account.statement.completion.rule"
_inherit = "account.statement.completion.rule"
def _get_functions(self, cr, uid, context=None):
res = super(account_statement_completion_rule, self)._get_functions(
cr, uid, context=context)
res.append(
('get_from_ref_and_so', 'From line reference (based on SO number)')
)
return res
# Should be private but data are initialised with no update XML
def get_from_ref_and_so(self, cr, uid, st_line, context=None):
"""
Match the partner based on the SO number and the reference of the
statement line. Then, call the generic get_values_for_line method to
complete other values. If more than one partner matched, raise the
ErrorTooManyPartner error.
:param int/long st_line: read of the concerned
account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id': value,
...}
"""
st_obj = self.pool.get('account.bank.statement.line')
res = {}
if st_line:
so_obj = self.pool.get('sale.order')
so_id = so_obj.search(cr,
uid,
[('name', '=', st_line['ref'])],
context=context)
if so_id:
if so_id and len(so_id) == 1:
so = so_obj.browse(cr, uid, so_id[0], context=context)
res['partner_id'] = so.partner_id.id
elif so_id and len(so_id) > 1:
raise ErrorTooManyPartner(
_('Line named "%s" (Ref:%s) was matched by more '
'than one partner while looking on SO by ref.') %
(st_line['name'], st_line['ref']))
st_vals = st_obj.get_values_for_line(
cr,
uid,
profile_id=st_line['profile_id'],
master_account_id=st_line['master_account_id'],
partner_id=res.get('partner_id', False),
line_type='customer',
amount=st_line['amount'] if st_line['amount'] else 0.0,
context=context)
res.update(st_vals)
return res
_columns = {
'function_to_call': fields.selection(_get_functions, 'Method'),
}

View File

@@ -0,0 +1,44 @@
-
In order to test the banking framework for Sale Orders, I first need to
create a profile
-
!record {model: account.statement.profile, id: profile_test_so}:
name: Bank EUR Profile for SO
journal_id: account.bank_journal
commission_account_id: account.a_expense
company_id: base.main_company
balance_check: True
rule_ids:
- account_statement_base_completion.bank_statement_completion_rule_4
- account_statement_base_completion.bank_statement_completion_rule_5
- account_statement_base_completion.bank_statement_completion_rule_2
- account_statement_base_completion.bank_statement_completion_rule_3
- bank_statement_completion_rule_1
-
Now I create a statement. I create statment lines separately because I need
to find each one by XML id
-
!record {model: account.bank.statement, id: statement_test_sale1}:
name: Statement for SO
profile_id: profile_test_so
company_id: base.main_company
-
I create a statement line for a SO
-
!record {model: account.bank.statement.line, id: statement_line_so}:
name: Test autocompletion based on Sale Order Number
statement_id: statement_test_sale1
ref: SO007
date: '2013-12-20'
amount: 14981.0
-
I run the auto complete
-
!python {model: account.bank.statement}: |
result = self.button_auto_completion(cr, uid, [ref("statement_test_sale1")])
-
Now I can check that all is nice and shiny, line 1. I expect the Sale Order
Number to be recognised.
-
!assert {model: account.bank.statement.line, id: statement_line_so, string: Check completion by SO number}:
- partner_id.name == u'Luminous Technologies'

View File

@@ -48,7 +48,10 @@
"data.xml", "data.xml",
], ],
'demo_xml': [], 'demo_xml': [],
'test': [], 'test': [
'test/sale.yml',
'test/completion_transactionid_test.yml',
],
'installable': True, 'installable': True,
'images': [], 'images': [],
'auto_install': True, 'auto_install': True,

View File

@@ -0,0 +1,44 @@
-
In order to test the banking framework, I first need to create a profile
-
!record {model: account.statement.profile, id: statement_profile_transactionid}:
name: Bank EUR Profile (transaction ID)
journal_id: account.bank_journal
commission_account_id: account.a_expense
company_id: base.main_company
balance_check: True
rule_ids:
- bank_statement_completion_rule_4
- account_statement_base_completion.bank_statement_completion_rule_4
- account_statement_base_completion.bank_statement_completion_rule_5
- account_statement_base_completion.bank_statement_completion_rule_2
- account_statement_base_completion.bank_statement_completion_rule_3
-
Now I create a statement. I create statment lines separately because I need
to find each one by XML id
-
!record {model: account.bank.statement, id: statement_transactionid_test1}:
name: Statement with transaction ID
profile_id: statement_profile_transactionid
company_id: base.main_company
-
I create a statement line for a SO with transaction ID
-
!record {model: account.bank.statement.line, id: statement_line_transactionid}:
name: Test autocompletion based on SO with transaction ID
statement_id: statement_transactionid_test1
transaction_id: XXX66Z
ref: 6
date: '2014-01-06'
amount: 118.4
-
I run the auto complete
-
!python {model: account.bank.statement}: |
result = self.button_auto_completion(cr, uid, [ref("statement_profile_transactionid")])
-
Now I can check that all is nice and shiny, line 1. I expect the SO has been
recognised from the transaction ID.
-
!assert {model: account.bank.statement.line, id: statement_line_transactionid, string: Check completion by SO transaction ID}:
- partner_id.name == u'Agrolait'

View File

@@ -0,0 +1,11 @@
-
I create a new Sale Order with transaction ID
-
!record {model: sale.order, id: so_with_transaction_id}:
partner_id: base.res_partner_2
note: Invoice after delivery
payment_term: account.account_payment_term
transaction_id: XXX66Z
order_line:
- product_id: product.product_product_7
product_uom_qty: 8