diff --git a/account_advanced_reconcile_transaction_ref/__openerp__.py b/account_advanced_reconcile_transaction_ref/__openerp__.py index d806ba9e..bd1586cf 100644 --- a/account_advanced_reconcile_transaction_ref/__openerp__.py +++ b/account_advanced_reconcile_transaction_ref/__openerp__.py @@ -25,7 +25,7 @@ Advanced reconciliation method for the module account_easy_reconcile Reconcile rules with transaction_ref """, - 'version': '1.0', + 'version': '1.0.1', 'author': 'Camptocamp', 'category': 'Finance', 'website': 'http://www.camptocamp.com', diff --git a/account_advanced_reconcile_transaction_ref/account.py b/account_advanced_reconcile_transaction_ref/account.py index 0525de31..b3e2cd24 100644 --- a/account_advanced_reconcile_transaction_ref/account.py +++ b/account_advanced_reconcile_transaction_ref/account.py @@ -30,7 +30,7 @@ class AccountMoveLine(Model): 'transaction_ref': fields.char('Transaction Ref.', size=128), } -class AccountBankSatement(Model): +class AccountBankStatement(Model): """ Inherit account.bank.statement class in order to set transaction_ref info on account.move.line """ @@ -43,7 +43,7 @@ class AccountBankSatement(Model): if context is None: context = {} - res = super(AccountBankSatement, self)._prepare_move_line_vals( + res = super(AccountBankStatement, self)._prepare_move_line_vals( cr, uid, st_line, move_id, debit, credit, currency_id=currency_id, amount_currency=amount_currency, diff --git a/account_statement_bankaccount_completion/__openerp__.py b/account_statement_bankaccount_completion/__openerp__.py index 0986b97d..c4f4b06e 100644 --- a/account_statement_bankaccount_completion/__openerp__.py +++ b/account_statement_bankaccount_completion/__openerp__.py @@ -20,7 +20,7 @@ # {'name': "Bank statement completion from bank account number", - 'version': '1.0', + 'version': '1.0.1', 'author': 'ACSONE SA/NV', 'maintainer': 'ACSONE SA/NV', 'category': 'Finance', @@ -29,21 +29,20 @@ 'account_statement_base_completion', ], 'description': """ - Add a completion method based on the partner bank account number provided by the bank/office. + Add a completion method based on the partner bank account number + provided by the bank/office. - Completion will look in the partner with that bank account number to match the partner, - then it will fill in the bank statement line with it to ease the reconciliation. + Completion will look in the partner with that bank account number + to match the partner, then it will fill in the bank statement line + with it to ease the reconciliation. """, 'website': 'http://www.acsone.eu', - 'init_xml': [], - 'update_xml': [ + 'data': [ "data.xml", ], - 'demo_xml': [], - 'test': [], + 'demo': [], 'installable': True, - 'images': [], - 'auto_install': True, + 'auto_install': False, 'license': 'AGPL-3', } diff --git a/account_statement_bankaccount_completion/data.xml b/account_statement_bankaccount_completion/data.xml index a6a84429..685c4008 100644 --- a/account_statement_bankaccount_completion/data.xml +++ b/account_statement_bankaccount_completion/data.xml @@ -3,7 +3,7 @@ - Match from bank account number (Nomal or IBAN)) + Match from bank account number (Normal or IBAN)) 10 get_from_bank_account diff --git a/account_statement_base_completion/__init__.py b/account_statement_base_completion/__init__.py index 6c9ef1bc..a219be6a 100644 --- a/account_statement_base_completion/__init__.py +++ b/account_statement_base_completion/__init__.py @@ -19,5 +19,5 @@ # ############################################################################## -import statement -import partner +from . import partner +from . import statement diff --git a/account_statement_base_completion/__openerp__.py b/account_statement_base_completion/__openerp__.py index f6a9e806..ece29e6f 100644 --- a/account_statement_base_completion/__openerp__.py +++ b/account_statement_base_completion/__openerp__.py @@ -20,7 +20,7 @@ ############################################################################## {'name': "Bank statement base completion", - 'version': '1.0', + 'version': '1.0.1', 'author': 'Camptocamp', 'maintainer': 'Camptocamp', 'category': 'Finance', diff --git a/account_statement_base_completion/statement.py b/account_statement_base_completion/statement.py index 0ab8e854..61e3dbde 100644 --- a/account_statement_base_completion/statement.py +++ b/account_statement_base_completion/statement.py @@ -319,9 +319,12 @@ class AccountStatementCompletionRule(orm.Model): if not context['partner_memoizer']: return res st_obj = self.pool.get('account.bank.statement.line') - sql = "SELECT id FROM res_partner WHERE name ~* %s and id in %s" - pattern = ".*%s.*" % re.escape(st_line['name']) - cr.execute(sql, (pattern, context['partner_memoizer'])) + # regexp_replace(name,'([^a-zA-Z0-9 -])', '\\\1', 'g'), 'i') escape the column name to avoid false positive. (ex 'jho..doe' -> 'joh\.\.doe' + sql = """SELECT id FROM ( + SELECT id, regexp_matches(%s, regexp_replace(name,'([^[:alpha:]0-9 -])', %s, 'g'), 'i') AS name_match FROM res_partner + WHERE id IN %s) AS res_patner_matcher + WHERE name_match IS NOT NULL""" + cr.execute(sql, (st_line['name'], r"\\\1", context['partner_memoizer'])) result = cr.fetchall() if not result: return res @@ -332,7 +335,7 @@ class AccountStatementCompletionRule(orm.Model): res['partner_id'] = result[0][0] st_vals = st_obj.get_values_for_line(cr, uid, - profile_id=st_line['porfile_id'], + profile_id=st_line['profile_id'], master_account_id=st_line['master_account_id'], partner_id=res['partner_id'], line_type=False, @@ -469,7 +472,7 @@ class AccountStatementLine(orm.Model): sql_err.pgerror) -class AccountBankSatement(orm.Model): +class AccountBankStatement(orm.Model): """ We add a basic button and stuff to support the auto-completion of the bank statement once line have been imported or manually fullfill. diff --git a/account_statement_base_completion/tests/__init__.py b/account_statement_base_completion/tests/__init__.py new file mode 100644 index 00000000..6f9e09ea --- /dev/null +++ b/account_statement_base_completion/tests/__init__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# +# +# Authors: Laurent Mignon +# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu) +# All Rights Reserved +# +# 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 . +# +# + +from . import test_base_completion + +checks = [ + test_base_completion +] diff --git a/account_statement_base_completion/tests/test_base_completion.py b/account_statement_base_completion/tests/test_base_completion.py new file mode 100644 index 00000000..d9416eff --- /dev/null +++ b/account_statement_base_completion/tests/test_base_completion.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# +# +# Authors: Laurent Mignon +# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu) +# All Rights Reserved +# +# 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 . +# +# +from openerp.tests import common +import time +from collections import namedtuple + +name_completion_case = namedtuple("name_completion_case", ["partner_name", "line_label", "should_match"]) +NAMES_COMPLETION_CASES = [ + name_completion_case("Acsone", "Line for Acsone SA", True), + name_completion_case("Acsone", "Line for Acsone", True), + name_completion_case("Acsone", "Acsone for line", True), + name_completion_case("acsone", "Acsone for line", True), + name_completion_case("Acsone SA", "Line for Acsone SA test", True), + name_completion_case("Ac..ne", "Acsone for line", False), + name_completion_case("é@|r{}", "Acsone é@|r{} for line", True), + name_completion_case("Acsone", "A..one for line", False), + name_completion_case("A.one SA", "A.one SA for line", True), + name_completion_case("Acsone SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA test", False), + name_completion_case("Acsone ([^a-zA-Z0-9 -]) SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA test", True), + ] + + +class base_completion(common.TransactionCase): + + def setUp(self): + super(base_completion, self).setUp() + self.company_a = self.browse_ref('base.main_company') + self.profile_obj = self.registry("account.statement.profile") + self.partner_obj = self.registry("res.partner") + self.account_bank_statement_obj = self.registry("account.bank.statement") + self.account_bank_statement_line_obj = self.registry("account.bank.statement.line") + self.journal_id = self.ref("account.bank_journal") + self.partner_id = self.ref('base.main_partner') + self.account_id = self.ref("account.a_recv") + self.partner_id = self.ref("base.res_partner_12") + + def test_name_completion(self): + """Test complete partner_id from statement line label + Test the automatic completion of the partner_id based if the name of the partner appears in + the statement line label + """ + self.completion_rule_id = self.ref('account_statement_base_completion.bank_statement_completion_rule_3') + # Create the profile + self.profile_id = self.profile_obj.create(self.cr, self.uid, { + "name": "TEST", + "commission_account_id": self.account_id, + "journal_id": self.journal_id, + "rule_ids": [(6, 0, [self.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": self.journal_id, + "profile_id": self.profile_id + }) + + for case in NAMES_COMPLETION_CASES: + self.partner_obj.write(self.cr, self.uid, self.partner_id, {'name': case.partner_name}) + statement_line_id = self.account_bank_statement_line_obj.create(self.cr, self.uid, { + 'amount': 1000.0, + 'name': case.line_label, + 'ref': 'My ref', + 'statement_id': self.statement_id, + }) + statement_line = self.account_bank_statement_line_obj.browse(self.cr, self.uid, statement_line_id) + self.assertFalse(statement_line.partner_id, "Partner_id must be blank before completion") + statement_obj = self.account_bank_statement_obj.browse(self.cr, self.uid, self.statement_id) + statement_obj.button_auto_completion() + statement_line = self.account_bank_statement_line_obj.browse(self.cr, self.uid, statement_line_id) + if case.should_match: + self.assertEquals(self.partner_id, statement_line.partner_id['id'], + "Missing expected partner id after completion (partner_name: %s, line_name: %s)" % (case.partner_name, case.line_label)) + else: + self.assertNotEquals(self.partner_id, statement_line.partner_id['id'], + "Partner id should be empty after completion(partner_name: %s, line_name: %s)" % (case.partner_name, case.line_label)) diff --git a/account_statement_base_import/__openerp__.py b/account_statement_base_import/__openerp__.py index 086af048..f33743ca 100644 --- a/account_statement_base_import/__openerp__.py +++ b/account_statement_base_import/__openerp__.py @@ -20,7 +20,7 @@ ############################################################################## {'name': "Bank statement base import", - 'version': '1.0', + 'version': '1.1.0', 'author': 'Camptocamp', 'maintainer': 'Camptocamp', 'category': 'Finance', diff --git a/account_statement_base_import/parser/file_parser.py b/account_statement_base_import/parser/file_parser.py index 2f4fbdaf..125605f2 100644 --- a/account_statement_base_import/parser/file_parser.py +++ b/account_statement_base_import/parser/file_parser.py @@ -35,24 +35,24 @@ def float_or_zero(val): class FileParser(BankStatementImportParser): """ - Generic abstract class for defining parser for .csv or .xls file format. + Generic abstract class for defining parser for .csv, .xls or .xlsx file format. """ def __init__(self, parse_name, ftype='csv', extra_fields=None, header=None, **kwargs): """ :param char: parse_name: The name of the parser - :param char: ftype: extension of the file (could be csv or xls) + :param char: ftype: extension of the file (could be csv, xls or xlsx) :param dict: extra_fields: extra fields to add to the conversion dict. In the format {fieldname: fieldtype} :param list: header : specify header fields if the csv file has no header """ super(FileParser, self).__init__(parse_name, **kwargs) - if ftype in ('csv', 'xls'): - self.ftype = ftype + if ftype in ('csv', 'xls' ,'xlsx'): + self.ftype = ftype[0:3] else: raise except_osv(_('User Error'), - _('Invalid file type %s. Please use csv or xls') % ftype) + _('Invalid file type %s. Please use csv, xls or xlsx') % ftype) self.conversion_dict = { 'ref': unicode, 'label': unicode, @@ -81,7 +81,7 @@ class FileParser(BankStatementImportParser): def _parse(self, *args, **kwargs): """ - Launch the parsing through .csv or .xls depending on the + Launch the parsing through .csv, .xls or .xlsx depending on the given ftype """ @@ -128,7 +128,7 @@ class FileParser(BankStatementImportParser): def _parse_xls(self): """ - :return: dict of dict from xls file (line/rows) + :return: dict of dict from xls/xlsx file (line/rows) """ wb_file = tempfile.NamedTemporaryFile() wb_file.write(self.filebuffer) @@ -180,7 +180,7 @@ class FileParser(BankStatementImportParser): def _from_xls(self, result_set, conversion_rules): """ Handle the converstion from the dict and handle date format from - an .xls file. + an .csv, .xls or .xlsx file. """ for line in result_set: for rule in conversion_rules: diff --git a/account_statement_base_import/parser/parser.py b/account_statement_base_import/parser/parser.py index 39d4d313..b17aded1 100644 --- a/account_statement_base_import/parser/parser.py +++ b/account_statement_base_import/parser/parser.py @@ -20,12 +20,13 @@ ############################################################################## import base64 import csv +from datetime import datetime def UnicodeDictReader(utf8_data, **kwargs): sniffer = csv.Sniffer() pos = utf8_data.tell() - sample_data = utf8_data.read(1024) + sample_data = utf8_data.read(2048) utf8_data.seek(pos) dialect = sniffer.sniff(sample_data, delimiters=',;\t') csv_reader = csv.DictReader(utf8_data, dialect=dialect, **kwargs) @@ -121,10 +122,10 @@ class BankStatementImportParser(object): :return: dict of vals that represent additional infos for the statement """ return { - 'name': self.statement_name, + 'name': self.statement_name or '/', 'balance_start': self.balance_start, 'balance_end_real': self.balance_end, - 'date': self.statement_date + 'date': self.statement_date or datetime.now() } def get_st_line_vals(self, line, *args, **kwargs): diff --git a/account_statement_base_import/statement.py b/account_statement_base_import/statement.py index 8da3c7b3..5a56c4ab 100644 --- a/account_statement_base_import/statement.py +++ b/account_statement_base_import/statement.py @@ -26,17 +26,19 @@ import datetime from openerp.osv.orm import Model from openerp.osv import fields, osv from parser import new_bank_statement_parser +from openerp.tools.config import config class AccountStatementProfil(Model): _inherit = "account.statement.profile" def get_import_type_selection(self, cr, uid, context=None): - """ - Has to be inherited to add parser - """ + """This is the method to be inherited for adding the parser""" return [('generic_csvxls_so', 'Generic .csv/.xls based on SO Name')] + def _get_import_type_selection(self, cr, uid, context=None): + return self.get_import_type_selection(cr, uid, context=context) + _columns = { 'launch_import_completion': fields.boolean( "Launch completion after import", @@ -46,14 +48,17 @@ class AccountStatementProfil(Model): # we remove deprecated as it floods logs in standard/warning level sob... 'rec_log': fields.text('log', readonly=True), # Deprecated 'import_type': fields.selection( - get_import_type_selection, + _get_import_type_selection, 'Type of import', required=True, help="Choose here the method by which you want to import bank" "statement for this profile."), - } + _defaults = { + 'import_type': 'generic_csvxls_so' + } + def _write_extra_statement_lines( self, cr, uid, parser, result_row_list, profile, statement_id, context): """Insert extra lines after the main statement lines. @@ -85,7 +90,11 @@ class AccountStatementProfil(Model): context=context) return True - def prepare_statetement_lines_vals( + #Deprecated remove on V8 + def prepare_statetement_lines_vals(self, *args, **kwargs): + return self.prepare_statement_lines_vals(*args, **kwargs) + + def prepare_statement_lines_vals( self, cr, uid, parser_vals, account_payable, account_receivable, statement_id, context): """ @@ -183,7 +192,7 @@ class AccountStatementProfil(Model): statement_store = [] for line in result_row_list: parser_vals = parser.get_st_line_vals(line) - values = self.prepare_statetement_lines_vals( + values = self.prepare_statement_lines_vals( cr, uid, parser_vals, account_payable, account_receivable, statement_id, context) statement_store.append(values) @@ -218,10 +227,14 @@ class AccountStatementProfil(Model): context) except Exception: - statement_obj.unlink(cr, uid, [statement_id], context=context) error_type, error_value, trbk = sys.exc_info() st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value) st += ''.join(traceback.format_tb(trbk, 30)) + #TODO we should catch correctly the exception with a python + #Exception and only re-catch some special exception. + #For now we avoid re-catching error in debug mode + if config['debug_mode']: + raise raise osv.except_osv(_("Statement import error"), _("The statement cannot be created: %s") % st) return statement_id diff --git a/account_statement_ext/__openerp__.py b/account_statement_ext/__openerp__.py index c6000063..0eb73d8d 100644 --- a/account_statement_ext/__openerp__.py +++ b/account_statement_ext/__openerp__.py @@ -20,7 +20,7 @@ ############################################################################## {'name': "Bank statement extension and profiles", - 'version': '1.3.0', + 'version': '1.3.3', 'author': 'Camptocamp', 'maintainer': 'Camptocamp', 'category': 'Finance', diff --git a/account_statement_ext/statement.py b/account_statement_ext/statement.py index 3a5a95cf..2cbd9e8f 100644 --- a/account_statement_ext/statement.py +++ b/account_statement_ext/statement.py @@ -31,9 +31,10 @@ def fixed_write(self, cr, uid, ids, vals, context=None): I will do it when I have time.""" res = super(stat_mod.account_bank_statement, self).write(cr, uid, ids, vals, context=context) - cr.execute("UPDATE account_bank_statement_line" - " SET sequence = account_bank_statement_line.id + 1" - " where statement_id in %s", (tuple(ids),)) + if ids: # will be false for an new empty bank statement + cr.execute("UPDATE account_bank_statement_line" + " SET sequence = account_bank_statement_line.id + 1" + " where statement_id in %s", (tuple(ids),)) return res stat_mod.account_bank_statement.write = fixed_write @@ -118,7 +119,7 @@ class AccountStatementProfile(Model): -class AccountBankSatement(Model): +class AccountBankStatement(Model): """ We improve the bank statement class mostly for : - Removing the period and compute it from the date of each line. @@ -207,7 +208,8 @@ class AccountBankSatement(Model): profile_obj = self.pool.get('account.statement.profile') profile = profile_obj.browse(cr, uid, vals['profile_id'], context=context) vals['journal_id'] = profile.journal_id.id - return super(AccountBankSatement, self).create(cr, uid, vals, context=context) + return super(AccountBankStatement, self + ).create(cr, uid, vals, context=context) def _get_period(self, cr, uid, date, context=None): """Return matching period for a date.""" @@ -255,8 +257,9 @@ class AccountBankSatement(Model): """ if context is None: context = {} - res = super(AccountBankSatement, self)._prepare_move( - cr, uid, st_line, st_line_number, context=context) + res = super(AccountBankStatement, self + )._prepare_move(cr, uid, st_line, st_line_number, + context=context) ctx = context.copy() ctx['company_id'] = st_line.company_id.id period_id = self._get_period(cr, uid, st_line.date, context=ctx) @@ -285,7 +288,7 @@ class AccountBankSatement(Model): """ if context is None: context = {} - res = super(AccountBankSatement, self)._prepare_move_line_vals( + res = super(AccountBankStatement, self)._prepare_move_line_vals( cr, uid, st_line, move_id, debit, credit, currency_id=currency_id, amount_currency=amount_currency, @@ -309,10 +312,9 @@ class AccountBankSatement(Model): create the move from. :return: int/long of the res.partner to use as counterpart """ - bank_partner_id = super(AccountBankSatement, self)._get_counter_part_partner(cr, - uid, - st_line, - context=context) + bank_partner_id = super(AccountBankStatement, self + )._get_counter_part_partner(cr, uid, st_line, + context=context) # get the right partner according to the chosen profile if st_line.statement_id.profile_id.force_partner_on_bank: bank_partner_id = st_line.statement_id.profile_id.partner_id.id @@ -542,8 +544,9 @@ class AccountBankSatement(Model): """ st = self.browse(cr, uid, st_id, context=context) if st.balance_check: - return super(AccountBankSatement, self).balance_check( - cr, uid, st_id, journal_type, context=context) + return super(AccountBankStatement, self + ).balance_check(cr, uid, st_id, journal_type, + context=context) else: return True @@ -563,7 +566,7 @@ class AccountBankSatement(Model): 'balance_check': import_config.balance_check}} -class AccountBankSatementLine(Model): +class AccountBankStatementLine(Model): """ Override to compute the period from the date of the line, add a method to retrieve the values for a line from the profile. Override the on_change method to take care of @@ -658,6 +661,11 @@ class AccountBankSatementLine(Model): # This can be quite a performance killer as we read ir.properity fields if partner_id: part = obj_partner.browse(cr, uid, partner_id, context=context) + part = part.commercial_partner_id + # When the method is called from bank statement completion, + # ensure that the line's partner is a commercial + # (accounting) entity + res['partner_id'] = part.id pay_account = part.property_account_payable.id receiv_account = part.property_account_receivable.id # If no value, look on the default company property @@ -693,11 +701,9 @@ class AccountBankSatementLine(Model): Keep the same features as in standard and call super. If an account is returned, call the method to compute line values. """ - res = super(AccountBankSatementLine, self).onchange_type(cr, uid, - line_id, - partner_id, - line_type, - context=context) + res = super(AccountBankStatementLine, self + ).onchange_type(cr, uid, line_id, partner_id, + line_type, context=context) if 'account_id' in res['value']: result = self.get_values_for_line(cr, uid, profile_id=profile_id, diff --git a/account_statement_ext_point_of_sale/__init__.py b/account_statement_ext_point_of_sale/__init__.py new file mode 100644 index 00000000..05435f81 --- /dev/null +++ b/account_statement_ext_point_of_sale/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Alexandre Fayolle +# 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 . +# +############################################################################## + +from . import point_of_sale diff --git a/account_statement_ext_point_of_sale/__openerp__.py b/account_statement_ext_point_of_sale/__openerp__.py new file mode 100644 index 00000000..6d4b7a8f --- /dev/null +++ b/account_statement_ext_point_of_sale/__openerp__.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Nicolas Bessi, Joel Grand-Guillaume +# Copyright 2011-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 . +# +############################################################################## + +{'name': "Bank statement extension and profiles for Point of Sale", + 'version': '1.0.0', + 'author': 'Camptocamp', + 'maintainer': 'Camptocamp', + 'category': 'Point Of Sale', + 'complexity': 'normal', + 'depends': ['point_of_sale', + 'account_statement_ext', + ], + 'description': """ + Update the point of sale code to work with improved bank statements. + """, + 'website': 'http://www.camptocamp.com', + 'data': [], + 'demo': [], + 'test': [], + 'installable': True, + 'images': [], + 'auto_install': True, + 'license': 'AGPL-3', + 'active': False, + } diff --git a/account_statement_ext_point_of_sale/point_of_sale.py b/account_statement_ext_point_of_sale/point_of_sale.py new file mode 100644 index 00000000..31e23418 --- /dev/null +++ b/account_statement_ext_point_of_sale/point_of_sale.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Alexandre Fayolle +# 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 . +# +############################################################################## +from openerp.addons.point_of_sale.point_of_sale import pos_session as std_pos_session +from openerp.osv import orm, osv +from openerp.tools.translate import _ + +if not hasattr(std_pos_session, '_prepare_bank_statement'): + # monkey patch to fix lp:1245375 + # + # We replace pos_session.create with the implementation in + # mp_create below which is essentially the same, only with a call + # to self._prepare_bank_statement. + # + # The default implementation has been extracted in + # mp_prepare_bank_statement below, and can be overridden in models + # which _inherit pos.session + # + # This change has been proposed for merging to fix lp:125375 + def mp_prepare_bank_statement(self, cr, uid, pos_config, journal, context=None): + bank_values = { + 'journal_id' : journal.id, + 'user_id' : uid, + 'company_id' : pos_config.shop_id.company_id.id + } + return bank_values + + def mp_create(self, cr, uid, values, context=None): + context = context or {} + config_id = values.get('config_id', False) or context.get('default_config_id', False) + if not config_id: + raise osv.except_osv( _('Error!'), + _("You should assign a Point of Sale to your session.")) + + # journal_id is not required on the pos_config because it does not + # exists at the installation. If nothing is configured at the + # installation we do the minimal configuration. Impossible to do in + # the .xml files as the CoA is not yet installed. + jobj = self.pool.get('pos.config') + pos_config = jobj.browse(cr, uid, config_id, context=context) + context.update({'company_id': pos_config.shop_id.company_id.id}) + if not pos_config.journal_id: + jid = jobj.default_get(cr, uid, ['journal_id'], context=context)['journal_id'] + if jid: + jobj.write(cr, uid, [pos_config.id], {'journal_id': jid}, context=context) + else: + raise osv.except_osv( _('error!'), + _("Unable to open the session. You have to assign a sale journal to your point of sale.")) + + # define some cash journal if no payment method exists + if not pos_config.journal_ids: + journal_proxy = self.pool.get('account.journal') + cashids = journal_proxy.search(cr, uid, [('journal_user', '=', True), ('type','=','cash')], context=context) + if not cashids: + cashids = journal_proxy.search(cr, uid, [('type', '=', 'cash')], context=context) + if not cashids: + cashids = journal_proxy.search(cr, uid, [('journal_user','=',True)], context=context) + + jobj.write(cr, uid, [pos_config.id], {'journal_ids': [(6,0, cashids)]}) + + + pos_config = jobj.browse(cr, uid, config_id, context=context) + bank_statement_ids = [] + for journal in pos_config.journal_ids: + bank_values = self._prepare_bank_statement(cr, uid, pos_config, journal, context) + statement_id = self.pool.get('account.bank.statement').create(cr, uid, bank_values, context=context) + bank_statement_ids.append(statement_id) + + values.update({ + 'name' : pos_config.sequence_id._next(), + 'statement_ids' : [(6, 0, bank_statement_ids)], + 'config_id': config_id + }) + return super(std_pos_session, self).create(cr, uid, values, context=context) + + std_pos_session._prepare_bank_statement = mp_prepare_bank_statement + std_pos_session.create = mp_create + + +class pos_session(orm.Model): + _inherit = 'pos.session' + + def _prepare_bank_statement(self, cr, uid, pos_config, journal, context=None): + """ Override the function _mp_create. To add the bank profile to the statement + + Function That was previously added to pos.session model using monkey patching + + """ + + bank_values = super(pos_session, self)._prepare_bank_statement(cr, uid, + pos_config, + journal, context) + user_obj = self.pool.get('res.users') + profile_obj = self.pool.get('account.statement.profile') + user = user_obj.browse(cr, uid, uid, context=context) + defaults = self.pool['account.bank.statement'].default_get(cr, uid, + ['profile_id', 'period_id'], + context=context) + profile_ids = profile_obj.search(cr, uid, + [('company_id', '=', user.company_id.id), + ('journal_id', '=', bank_values['journal_id'])], + context=context) + if profile_ids: + defaults['profile_id'] = profile_ids[0] + bank_values.update(defaults) + return bank_values diff --git a/account_statement_ofx_import/statement.py b/account_statement_ofx_import/statement.py index c5b06fe8..15060360 100644 --- a/account_statement_ofx_import/statement.py +++ b/account_statement_ofx_import/statement.py @@ -33,14 +33,3 @@ class AccountStatementProfil(orm.Model): context=context) selection.append(('ofx_so', _('OFX - Open Financial Exchange'))) return selection - - _columns = { - 'import_type': fields.selection( - get_import_type_selection, - 'Type of import', - required=True, - help="Choose here the method by which you want to import bank" - "statement for this profile."), - - } - diff --git a/account_statement_one_move/__init__.py b/account_statement_one_move/__init__.py new file mode 100644 index 00000000..141c95fc --- /dev/null +++ b/account_statement_one_move/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# account_statement_one_move for OpenERP +# Copyright (C) 2013-TODAY Akretion . +# @author Sébastien BEAU +# +# 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 . +# +############################################################################### + +from . import statement diff --git a/account_statement_one_move/__openerp__.py b/account_statement_one_move/__openerp__.py new file mode 100644 index 00000000..ed9275ab --- /dev/null +++ b/account_statement_one_move/__openerp__.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# account_statement_one_move for OpenERP +# Copyright (C) 2013-TODAY Akretion . +# @author Sébastien BEAU +# +# 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 . +# +############################################################################### + +{ + 'name': 'Bank statement one move', + 'version': '0.1', + 'category': 'Generic Modules/Others', + 'license': 'AGPL-3', + 'description': """ + This module allows to group all lines of a bank statement in only one move. + This feature is optional and can be activated with a checkbox in the bank + statement's profile. This is very useful for credit card deposit for + example, you won't have a move for each line. + + """, + 'author': 'Akretion', + 'website': 'http://www.akretion.com/', + 'depends': ['account_statement_ext'], + 'data': [ + 'statement_view.xml' + ], + 'demo': [], + 'installable': True, + 'auto_install': False, + 'active': False, +} diff --git a/account_statement_one_move/statement.py b/account_statement_one_move/statement.py new file mode 100644 index 00000000..c16698ec --- /dev/null +++ b/account_statement_one_move/statement.py @@ -0,0 +1,225 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# account_statement_one_move for OpenERP +# Copyright (C) 2013-TODAY Akretion . +# @author Sébastien BEAU +# +# 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 . +# +############################################################################### + +from openerp.osv import fields, orm, osv + + +class AccountStatementProfile(orm.Model): + _inherit = "account.statement.profile" + _columns = { + 'one_move': fields.boolean( + 'Group Journal Items', + help="Only one Journal Entry will be generated on the " + "validation of the bank statement."), + 'split_transfer_line': fields.boolean( + 'Split Transfer Line', + help="Two transfer lines will be automatically generated : one " + "for the refunds and one for the payments.") + } + +class account_bank_statement(orm.Model): + _inherit = "account.bank.statement" + + def _prepare_move_line_vals(self, cr, uid, st_line, *args, **kwargs): + res = super(account_bank_statement, self)._prepare_move_line_vals(cr, uid, st_line, + *args, **kwargs) + period_id = self._get_period(cr, uid, st_line.statement_id.date, + context=kwargs.get('context')) + if st_line.statement_id.profile_id.one_move: + res.update({ + 'period_id': period_id, + 'date': st_line.statement_id.date, + 'name': st_line.ref, + }) + return res + + + return res + + def _prepare_move(self, cr, uid, st_line, st_line_number, context=None): + res = super(account_bank_statement, self).\ + _prepare_move(cr, uid, st_line, st_line_number, context=context) + res.update({ + 'ref': st_line.statement_id.name, + 'name': st_line.statement_id.name, + 'date': st_line.statement_id.date, + }) + return res + + + def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, + st_line_number, context=None): + if context is None: + context = {} + context['from_parent_object'] = True #For compability with module account_constraints + account_move_obj = self.pool.get('account.move') + account_bank_statement_line_obj = self.pool.get('account.bank.statement.line') + st_line = account_bank_statement_line_obj.browse(cr, uid, st_line_id, + context=context) + st = st_line.statement_id + + if st.profile_id.one_move: + if not context.get('move_id'): + move_vals = self._prepare_move(cr, uid, st_line, st_line_number, context=context) + context['move_id'] = account_move_obj.create(cr, uid, move_vals, context=context) + self.create_move_line_from_st_line(cr, uid, context['move_id'], + st_line_id, company_currency_id, + context=context) + return context['move_id'] + else: + return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line_id, + company_currency_id, + st_line_number, + context=context) + + def create_move_line_from_st_line(self, cr, uid, move_id, st_line_id, + company_currency_id, context=None): + """Create the account move line from the statement line. + + :param int/long move_id: ID of the account.move + :param int/long st_line_id: ID of the account.bank.statement.line to create the move line from. + :param int/long company_currency_id: ID of the res.currency of the company + :return: ID of the account.move created + """ + if context is None: + context = {} + res_currency_obj = self.pool.get('res.currency') + account_move_line_obj = self.pool.get('account.move.line') + account_bank_statement_line_obj = self.pool.get('account.bank.statement.line') + st_line = account_bank_statement_line_obj.browse(cr, uid, st_line_id, context=context) + st = st_line.statement_id + + context.update({'date': st_line.date}) + acc_cur = ((st_line.amount<=0) and st.journal_id.default_debit_account_id) or st_line.account_id + + context.update({ + 'res.currency.compute.account': acc_cur, + }) + amount = res_currency_obj.compute(cr, uid, st.currency.id, + company_currency_id, + st_line.amount, + context=context) + + bank_move_vals = self._prepare_bank_move_line(cr, uid, st_line, move_id, amount, + company_currency_id, context=context) + return account_move_line_obj.create(cr, uid, bank_move_vals, context=context) + + def _valid_move(self, cr, uid, move_id, context=None): + move_obj = self.pool.get('account.move') + move = move_obj.browse(cr, uid, move_id, context=context) + move_obj.post(cr, uid, [move_id], context=context) + return True + + + def _prepare_transfer_move_line_vals(self, cr, uid, st, name, amount, move_id, context=None): + """ + Prepare the dict of values to create the transfer move lines. + """ + account_id = st.profile_id.journal_id.default_debit_account_id.id + partner_id = st.profile_id.partner_id and profile.partner_id.id or False + if amount < 0.0: + debit = 0.0 + credit = -amount + else: + debit = amount + credit = 0.0 + vals = { + 'name': name, + 'date': st.date, + 'partner_id': partner_id, + 'statement_id': st.id, + 'account_id': account_id, + 'ref': name, + 'move_id': move_id, + 'credit': credit, + 'debit': debit, + 'journal_id': st.journal_id.id, + 'period_id': st.period_id.id, + } + return vals + + + def create_move_transfer_lines(self, cr, uid, move, st, context=None): + move_line_obj = self.pool.get('account.move.line') + move_id = move.id + refund = 0.0 + payment = 0.0 + transfer_lines = [] + transfer_line_ids = [] + #Calculate the part of the refund amount and the payment amount + for move_line in move.line_id: + refund -= move_line.debit + payment += move_line.credit + #Create 2 Transfer lines or One global tranfer line + if st.profile_id.split_transfer_line: + if refund: + transfer_lines.append(['Refund Transfer', refund]) + if payment: + transfer_lines.append(['Payment Transfer', payment]) + else: + amount = payment + refund + if amount: + transfer_lines.append(['Transfer', amount]) + for transfer_line in transfer_lines: + vals = self._prepare_transfer_move_line_vals(cr, uid, st, + transfer_line[0], + transfer_line[1], + move_id, + context=context) + transfer_line_ids.append(move_line_obj.create(cr, uid, vals, context=context)) + return transfer_line_ids + + + def button_confirm_bank(self, cr, uid, ids, context=None): + st_line_obj = self.pool.get('account.bank.statement.line') + move_obj = self.pool.get('account.move') + if context is None: + context = {} + for st in self.browse(cr, uid, ids, context=context): + super(account_bank_statement, self).button_confirm_bank(cr, uid, ids, + context=context) + if st.profile_id.one_move and context.get('move_id', False): + move_id = context['move_id'] + move = move_obj.browse(cr, uid, move_id, context=context) + transfe_line_ids = self.create_move_transfer_lines(cr, uid, move, st, context=context) + self._valid_move(cr, uid, move_id, context=context) + lines_ids = [x.id for x in st.line_ids] + st_line_obj.write(cr, uid, lines_ids, + {'move_ids': [(4, move_id, False)]}, + context=context) + return True + + def button_cancel(self, cr, uid, ids, context=None): + done = [] + for st in self.browse(cr, uid, ids, context=context): + if st.profile_id.one_move and st.line_ids: + for move in st.line_ids[0].move_ids: + if move.state != 'draft': + move.button_cancel(context=context) + move.unlink(context=context) + st.write({'state':'draft'}, context=context) + else: + super(account_bank_statement, self).button_cancel(cr, uid, ids, + context=context) + return True + + diff --git a/account_statement_one_move/statement_view.xml b/account_statement_one_move/statement_view.xml new file mode 100644 index 00000000..e0e19fc8 --- /dev/null +++ b/account_statement_one_move/statement_view.xml @@ -0,0 +1,26 @@ + + + + + + + + + + account_statement_one_move.account_statement.view_form + account.statement.profile + + + + + + + + + + + diff --git a/account_statement_regex_account_completion/i18n/fr.po b/account_statement_regex_account_completion/i18n/fr.po index 4d414f52..b2bdf2a0 100644 --- a/account_statement_regex_account_completion/i18n/fr.po +++ b/account_statement_regex_account_completion/i18n/fr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-02-10 05:56+0000\n" -"X-Generator: Launchpad (build 16916)\n" +"X-Launchpad-Export-Date: 2014-03-01 05:55+0000\n" +"X-Generator: Launchpad (build 16948)\n" #. module: account_statement_regex_account_completion #: field:account.statement.completion.rule,regex:0