From 512bb9753a99e7045683e3de98d7c27ef047a6c7 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Thu, 5 Feb 2015 14:41:27 +0100 Subject: [PATCH 01/13] Add module account_bank_statement_import_paypal Fix spelling mistake Add methods to inherit for language-specific or country-specific stuff Port to new API FIX super Update travis config --- .../README.rst | 41 ++++ .../__init__.py | 23 ++ .../__openerp__.py | 34 +++ .../account_bank_statement_import_paypal.py | 222 ++++++++++++++++++ 4 files changed, 320 insertions(+) create mode 100644 account_bank_statement_import_paypal/README.rst create mode 100644 account_bank_statement_import_paypal/__init__.py create mode 100644 account_bank_statement_import_paypal/__openerp__.py create mode 100644 account_bank_statement_import_paypal/account_bank_statement_import_paypal.py diff --git a/account_bank_statement_import_paypal/README.rst b/account_bank_statement_import_paypal/README.rst new file mode 100644 index 0000000..47095a6 --- /dev/null +++ b/account_bank_statement_import_paypal/README.rst @@ -0,0 +1,41 @@ +Import Paypal Bank Statements +============================= + +This module allows you to import the Paypal CSV files in Odoo as bank statements. + +Installation +============ + +This module depends on the module *account_bank_statement_import* which +is available: +* for Odoo version 8: in the OCA project `bank-statement-import ` +* for Odoo master (future version 9): it is an official module. + +Configuration +============= + +In the menu Accounting > Configuration > Accounts > Setup your Bank Accounts, make sure that you have your Paypal bank account with the following parameters: +* Bank Account Type: Normal Bank Account +* Account Number: the email address associated with your Paypal account +* Account Journal: the journal associated to your Paypal account + +Credits +======= + +Contributors +------------ + +* Alexis de Lattre + +Maintainer +---------- + +.. image:: http://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/account_bank_statement_import_paypal/__init__.py b/account_bank_statement_import_paypal/__init__.py new file mode 100644 index 0000000..f4992e1 --- /dev/null +++ b/account_bank_statement_import_paypal/__init__.py @@ -0,0 +1,23 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# account_bank_statement_import_paypal module for Odoo +# Copyright (C) 2014-2015 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 account_bank_statement_import_paypal diff --git a/account_bank_statement_import_paypal/__openerp__.py b/account_bank_statement_import_paypal/__openerp__.py new file mode 100644 index 0000000..3369755 --- /dev/null +++ b/account_bank_statement_import_paypal/__openerp__.py @@ -0,0 +1,34 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# account_bank_statement_import_paypal module for Odoo +# Copyright (C) 2014-2015 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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': 'Import Paypal Bank Statements', + 'version': '0.1', + 'license': 'AGPL-3', + 'author': 'Akretion', + 'website': 'http://www.akretion.com', + 'summary': 'Import Paypal CSV files as Bank Statements in Odoo', + 'depends': ['account_bank_statement_import'], + 'external_dependencies': {'python': ['unicodecsv']}, + 'data': [], + 'installable': True, +} diff --git a/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py b/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py new file mode 100644 index 0000000..bf1a9ff --- /dev/null +++ b/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py @@ -0,0 +1,222 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# account_bank_statement_import_paypal module for Odoo +# Copyright (C) 2014-2015 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# +# 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 . +# +############################################################################## + +import logging +from datetime import datetime +from openerp import models, fields, api, _ +from openerp.exceptions import Warning +import unicodecsv +import re +from cStringIO import StringIO + +_logger = logging.getLogger(__name__) + + +class AccountBankStatementImport(models.TransientModel): + _inherit = 'account.bank.statement.import' + + @api.model + def _prepare_paypal_encoding(self): + '''This method is designed to be inherited''' + return 'latin1' + + @api.model + def _prepare_paypal_date_format(self): + '''This method is designed to be inherited''' + return '%d/%m/%Y' + + @api.model + def _valid_paypal_line(self, line): + '''This method is designed to be inherited''' + if line[5].startswith('Termin'): + return True + else: + return False + + @api.model + def _paypal_convert_amount(self, amount_str): + '''This method is designed to be inherited''' + valstr = re.sub(r'[^\d,.-]', '', amount_str) + valstrdot = valstr.replace(',', '.') + return float(valstrdot) + + @api.model + def _check_paypal(self, data_file): + '''This method is designed to be inherited''' + return data_file.strip().startswith('Date,') + + @api.model + def _parse_file(self, data_file): + """ Import a file in Paypal CSV format""" + paypal = self._check_paypal(data_file) + if not paypal: + return super(AccountBankStatementImport, self)._parse_file( + data_file) + f = StringIO() + f.write(data_file) + f.seek(0) + transactions = [] + i = 0 + start_balance = end_balance = start_date_str = end_date_str = False + vals_line = False + company_currency_name = self.env.user.company_id.currency_id.name + commission_total = 0.0 + raw_lines = [] + paypal_email_account = False + # To confirm : is the encoding always latin1 ? + for line in unicodecsv.reader( + f, encoding=self._prepare_paypal_encoding()): + i += 1 + _logger.debug("Line %d: %s" % (i, line)) + if i == 1: + _logger.debug('Skip header line') + continue + if not line: + continue + if not self._valid_paypal_line(line): + _logger.info( + 'Skipping line %d because it is not in Done state' % i) + continue + date_dt = datetime.strptime( + line[0], self._prepare_paypal_date_format()) + rline = { + 'date': fields.Date.to_string(date_dt), + 'currency': line[6], + 'owner_name': line[3], + 'amount': line[7], + 'commission': line[8], + 'balance': line[34], + 'transac_ref': line[30], + 'ref': line[12], + 'line_nr': i, + } + for field in ['commission', 'amount', 'balance']: + _logger.debug('Trying to convert %s to float' % rline[field]) + try: + rline[field] = self._paypal_convert_amount(rline[field]) + except: + raise Warning( + _("Value '%s' for the field '%s' on line %d, " + "cannot be converted to float") + % (rline[field], field, i)) + if rline['amount'] > 0: + rline['name'] = line[3] + ' ' + line[10] + rline['partner_email'] = line[10] + if not paypal_email_account: + paypal_email_account = line[11] + else: + rline['name'] = line[3] + ' ' + line[11] + rline['partner_email'] = line[11] + if not paypal_email_account: + paypal_email_account = line[10] + raw_lines.append(rline) + + # Second pass to sort out the lines in other currencies + final_lines = [] + other_currency_line = {} + for wline in raw_lines: + if company_currency_name != wline['currency']: + if not wline['transac_ref'] and not other_currency_line: + currencies = self.env['res.currency'].search( + [('name', '=', wline['currency'])]) + if not currencies: + raise Warning( + _('Currency %s on line %d cannot be found in Odoo') + % (wline['currency'], wline['line_nr'])) + other_currency_line = { + 'amount_currency': wline['amount'], + 'currency_id': currencies[0].id, + 'currency': wline['currency'], + 'name': wline['name'], + 'owner_name': wline['owner_name'], + } + if wline['transac_ref'] and other_currency_line: + assert ( + wline['currency'] == other_currency_line['currency']),\ + 'WRONG currency' + assert ( + wline['amount'] == + other_currency_line['amount_currency'] * -1),\ + 'WRONG amount' + other_currency_line['transac_ref'] = wline['transac_ref'] + else: + if ( + other_currency_line + and wline['transac_ref'] == + other_currency_line['transac_ref']): + wline.update(other_currency_line) + # reset other_currency_line + other_currency_line = {} + final_lines.append(wline) + + # PayPal statements start with the end ! + final_lines.reverse() + j = 0 + for fline in final_lines: + j += 1 + commission_total += fline['commission'] + + if j == 1: + start_date_str = fline['date'] + start_balance = fline['balance'] - fline['amount'] + end_date_str = fline['date'] + end_balance = fline['balance'] + partners = False + if fline['partner_email']: + partners = self.env['res.partner'].search( + [('email', '=', fline['partner_email'])]) + if partners: + partner_id = partners[0].commercial_partner_id.id + else: + partner_id = False + vals_line = { + 'date': fline['date'], + 'name': fline['name'], + 'ref': fline['ref'], + 'unique_import_id': fline['ref'], + 'amount': fline['amount'], + 'partner_id': partner_id, + 'bank_account_id': False, + 'currency_id': fline.get('currency_id'), + 'amount_currency': fline.get('amount_currency'), + } + _logger.debug("vals_line = %s" % vals_line) + transactions.append(vals_line) + + if commission_total: + commission_line = { + 'date': end_date_str, + 'name': _('Paypal commissions'), + 'ref': _('PAYPAL-COSTS'), + 'amount': commission_total, + 'unique_import_id': False, + } + transactions.append(commission_line) + + vals_bank_statement = { + 'name': _('PayPal Import %s > %s') + % (start_date_str, end_date_str), + 'balance_start': start_balance, + 'balance_end_real': end_balance, + 'transactions': transactions, + } + return None, paypal_email_account, [vals_bank_statement] From 1849ab57d35cea0751b896696a068c178df8a96f Mon Sep 17 00:00:00 2001 From: "Adrien Peiffer (ACSONE)" Date: Wed, 6 Jan 2016 15:06:28 +0100 Subject: [PATCH 02/13] Consider refund line Avoid to return paypal_email_account as a bank account Put file transaction ref in name to work with base_transaction_ref Remove thousands separator --- .../account_bank_statement_import_paypal.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py b/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py index bf1a9ff..67012c9 100644 --- a/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py +++ b/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py @@ -47,7 +47,7 @@ class AccountBankStatementImport(models.TransientModel): @api.model def _valid_paypal_line(self, line): '''This method is designed to be inherited''' - if line[5].startswith('Termin'): + if line[5].startswith('Termin') or line[5].startswith('Rembours'): return True else: return False @@ -56,7 +56,8 @@ class AccountBankStatementImport(models.TransientModel): def _paypal_convert_amount(self, amount_str): '''This method is designed to be inherited''' valstr = re.sub(r'[^\d,.-]', '', amount_str) - valstrdot = valstr.replace(',', '.') + valstrdot = valstr.replace('.', '') + valstrdot = valstrdot.replace(',', '.') return float(valstrdot) @api.model @@ -190,8 +191,8 @@ class AccountBankStatementImport(models.TransientModel): partner_id = False vals_line = { 'date': fline['date'], - 'name': fline['name'], - 'ref': fline['ref'], + 'name': fline['ref'], + 'ref': fline['name'], 'unique_import_id': fline['ref'], 'amount': fline['amount'], 'partner_id': partner_id, @@ -219,4 +220,4 @@ class AccountBankStatementImport(models.TransientModel): 'balance_end_real': end_balance, 'transactions': transactions, } - return None, paypal_email_account, [vals_bank_statement] + return None, None, [vals_bank_statement] From ee597f44507f622306e4c6d83b86950723e0bec3 Mon Sep 17 00:00:00 2001 From: Mourad Elhadj Mimoune Date: Thu, 30 Jun 2016 17:43:35 +0200 Subject: [PATCH 03/13] adapt code to the new paypal format error when several line defined in currency != company currency handel utf-8 file handel csv with quotechar " skip BOM character in utf-8 --- .../account_bank_statement_import_paypal.py | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py b/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py index 67012c9..2e714b8 100644 --- a/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py +++ b/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py @@ -37,7 +37,7 @@ class AccountBankStatementImport(models.TransientModel): @api.model def _prepare_paypal_encoding(self): '''This method is designed to be inherited''' - return 'latin1' + return 'utf-8' @api.model def _prepare_paypal_date_format(self): @@ -47,7 +47,8 @@ class AccountBankStatementImport(models.TransientModel): @api.model def _valid_paypal_line(self, line): '''This method is designed to be inherited''' - if line[5].startswith('Termin') or line[5].startswith('Rembours'): + col_name = line[5].replace('"','') + if col_name.startswith('Termin') or col_name.startswith('Rembours'): return True else: return False @@ -63,11 +64,16 @@ class AccountBankStatementImport(models.TransientModel): @api.model def _check_paypal(self, data_file): '''This method is designed to be inherited''' - return data_file.strip().startswith('Date,') + paypal = data_file.strip().startswith('Date,') + if not paypal: + paypal = data_file.strip().startswith('"Date",') + + return paypal @api.model def _parse_file(self, data_file): """ Import a file in Paypal CSV format""" + data_file = data_file.replace("\xef\xbb\xbf", "") paypal = self._check_paypal(data_file) if not paypal: return super(AccountBankStatementImport, self)._parse_file( @@ -105,8 +111,8 @@ class AccountBankStatementImport(models.TransientModel): 'owner_name': line[3], 'amount': line[7], 'commission': line[8], - 'balance': line[34], - 'transac_ref': line[30], + 'balance': line[27], + 'transac_ref': line[23], 'ref': line[12], 'line_nr': i, } @@ -136,7 +142,7 @@ class AccountBankStatementImport(models.TransientModel): other_currency_line = {} for wline in raw_lines: if company_currency_name != wline['currency']: - if not wline['transac_ref'] and not other_currency_line: + if wline['transac_ref'] and not other_currency_line: currencies = self.env['res.currency'].search( [('name', '=', wline['currency'])]) if not currencies: @@ -149,8 +155,10 @@ class AccountBankStatementImport(models.TransientModel): 'currency': wline['currency'], 'name': wline['name'], 'owner_name': wline['owner_name'], - } - if wline['transac_ref'] and other_currency_line: + 'transac_ref': wline['transac_ref'], + } + + if other_currency_line and not wline['transac_ref']: assert ( wline['currency'] == other_currency_line['currency']),\ 'WRONG currency' @@ -158,11 +166,16 @@ class AccountBankStatementImport(models.TransientModel): wline['amount'] == other_currency_line['amount_currency'] * -1),\ 'WRONG amount' - other_currency_line['transac_ref'] = wline['transac_ref'] + if ( + other_currency_line and + wline['ref'] == + other_currency_line['transac_ref']): + # reset other_currency_line + other_currency_line = {} else: if ( - other_currency_line - and wline['transac_ref'] == + other_currency_line and + wline['transac_ref'] == other_currency_line['transac_ref']): wline.update(other_currency_line) # reset other_currency_line From 1810c385b12a4077122299ef9a1490ac42030e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Fri, 2 Feb 2018 15:06:43 +0100 Subject: [PATCH 04/13] refactor paypal import in order to use new bank statement from Paypal Remove deprecated information in readme solve issue on currency the main currency is not the first line (line are ordered by currency name). So we should take the journal currency or company currency to know the right currency to use. Also add full header to avoid issue if the paypal header change --- .../README.rst | 24 +- .../__init__.py | 24 +- .../__manifest__.py | 29 ++ .../__openerp__.py | 34 --- .../account_bank_statement_import_paypal.py | 236 --------------- .../models/__init__.py | 3 + .../account_bank_statement_import_paypal.py | 275 ++++++++++++++++++ .../static/description/icon.png | Bin 0 -> 6481 bytes .../static/description/paypal_backoffice.png | Bin 0 -> 97703 bytes 9 files changed, 325 insertions(+), 300 deletions(-) create mode 100644 account_bank_statement_import_paypal/__manifest__.py delete mode 100644 account_bank_statement_import_paypal/__openerp__.py delete mode 100644 account_bank_statement_import_paypal/account_bank_statement_import_paypal.py create mode 100644 account_bank_statement_import_paypal/models/__init__.py create mode 100644 account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py create mode 100644 account_bank_statement_import_paypal/static/description/icon.png create mode 100644 account_bank_statement_import_paypal/static/description/paypal_backoffice.png diff --git a/account_bank_statement_import_paypal/README.rst b/account_bank_statement_import_paypal/README.rst index 47095a6..1c80340 100644 --- a/account_bank_statement_import_paypal/README.rst +++ b/account_bank_statement_import_paypal/README.rst @@ -3,14 +3,6 @@ Import Paypal Bank Statements This module allows you to import the Paypal CSV files in Odoo as bank statements. -Installation -============ - -This module depends on the module *account_bank_statement_import* which -is available: -* for Odoo version 8: in the OCA project `bank-statement-import ` -* for Odoo master (future version 9): it is an official module. - Configuration ============= @@ -19,6 +11,15 @@ In the menu Accounting > Configuration > Accounts > Setup your Bank Accounts, ma * Account Number: the email address associated with your Paypal account * Account Journal: the journal associated to your Paypal account +============ + +Go to Paypal and download your Bank Statement + +.. image:: account_bank_statement_import_paypal/static/description/paypal_backoffice.png + :alt: . +.. image:: static/description/paypal_backoffice.png + :alt: . + Credits ======= @@ -26,6 +27,13 @@ Contributors ------------ * Alexis de Lattre +* Sebastien BEAU + +TIPS +-------- +For now only French and English report are supported +For adding new support you just need to add your header in model/account_bank_statement_import_paypal.py in the variables HEADERS. +Please help us and do a PR for adding new header ! Thanks Maintainer ---------- diff --git a/account_bank_statement_import_paypal/__init__.py b/account_bank_statement_import_paypal/__init__.py index f4992e1..cde864b 100644 --- a/account_bank_statement_import_paypal/__init__.py +++ b/account_bank_statement_import_paypal/__init__.py @@ -1,23 +1,3 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# account_bank_statement_import_paypal module for Odoo -# Copyright (C) 2014-2015 Akretion (http://www.akretion.com) -# @author Alexis de Lattre -# -# 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 . -# -############################################################################## +# -*- coding: utf-8 -*- -from . import account_bank_statement_import_paypal +from . import models diff --git a/account_bank_statement_import_paypal/__manifest__.py b/account_bank_statement_import_paypal/__manifest__.py new file mode 100644 index 0000000..4f20c11 --- /dev/null +++ b/account_bank_statement_import_paypal/__manifest__.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Copyright 2014-2017 Akretion (http://www.akretion.com). +# @author Alexis de Lattre +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Import Paypal Bank Statements", + 'summary': 'Import Paypal CSV files as Bank Statements in Odoo', + "version": "10.0.1.0.0", + "category": "Accounting", + "website": "https://github.com/OCA/bank-statement-import", + "author": " Akretion, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "external_dependencies": { + 'python': ['unicodecsv'], + "bin": [], + }, + "depends": [ + "account_bank_statement_import", + ], + "data": [ + ], + "demo": [ + ], + "qweb": [ + ] +} diff --git a/account_bank_statement_import_paypal/__openerp__.py b/account_bank_statement_import_paypal/__openerp__.py deleted file mode 100644 index 3369755..0000000 --- a/account_bank_statement_import_paypal/__openerp__.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# account_bank_statement_import_paypal module for Odoo -# Copyright (C) 2014-2015 Akretion (http://www.akretion.com) -# @author Alexis de Lattre -# -# 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': 'Import Paypal Bank Statements', - 'version': '0.1', - 'license': 'AGPL-3', - 'author': 'Akretion', - 'website': 'http://www.akretion.com', - 'summary': 'Import Paypal CSV files as Bank Statements in Odoo', - 'depends': ['account_bank_statement_import'], - 'external_dependencies': {'python': ['unicodecsv']}, - 'data': [], - 'installable': True, -} diff --git a/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py b/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py deleted file mode 100644 index 2e714b8..0000000 --- a/account_bank_statement_import_paypal/account_bank_statement_import_paypal.py +++ /dev/null @@ -1,236 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# account_bank_statement_import_paypal module for Odoo -# Copyright (C) 2014-2015 Akretion (http://www.akretion.com) -# @author Alexis de Lattre -# -# 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 . -# -############################################################################## - -import logging -from datetime import datetime -from openerp import models, fields, api, _ -from openerp.exceptions import Warning -import unicodecsv -import re -from cStringIO import StringIO - -_logger = logging.getLogger(__name__) - - -class AccountBankStatementImport(models.TransientModel): - _inherit = 'account.bank.statement.import' - - @api.model - def _prepare_paypal_encoding(self): - '''This method is designed to be inherited''' - return 'utf-8' - - @api.model - def _prepare_paypal_date_format(self): - '''This method is designed to be inherited''' - return '%d/%m/%Y' - - @api.model - def _valid_paypal_line(self, line): - '''This method is designed to be inherited''' - col_name = line[5].replace('"','') - if col_name.startswith('Termin') or col_name.startswith('Rembours'): - return True - else: - return False - - @api.model - def _paypal_convert_amount(self, amount_str): - '''This method is designed to be inherited''' - valstr = re.sub(r'[^\d,.-]', '', amount_str) - valstrdot = valstr.replace('.', '') - valstrdot = valstrdot.replace(',', '.') - return float(valstrdot) - - @api.model - def _check_paypal(self, data_file): - '''This method is designed to be inherited''' - paypal = data_file.strip().startswith('Date,') - if not paypal: - paypal = data_file.strip().startswith('"Date",') - - return paypal - - @api.model - def _parse_file(self, data_file): - """ Import a file in Paypal CSV format""" - data_file = data_file.replace("\xef\xbb\xbf", "") - paypal = self._check_paypal(data_file) - if not paypal: - return super(AccountBankStatementImport, self)._parse_file( - data_file) - f = StringIO() - f.write(data_file) - f.seek(0) - transactions = [] - i = 0 - start_balance = end_balance = start_date_str = end_date_str = False - vals_line = False - company_currency_name = self.env.user.company_id.currency_id.name - commission_total = 0.0 - raw_lines = [] - paypal_email_account = False - # To confirm : is the encoding always latin1 ? - for line in unicodecsv.reader( - f, encoding=self._prepare_paypal_encoding()): - i += 1 - _logger.debug("Line %d: %s" % (i, line)) - if i == 1: - _logger.debug('Skip header line') - continue - if not line: - continue - if not self._valid_paypal_line(line): - _logger.info( - 'Skipping line %d because it is not in Done state' % i) - continue - date_dt = datetime.strptime( - line[0], self._prepare_paypal_date_format()) - rline = { - 'date': fields.Date.to_string(date_dt), - 'currency': line[6], - 'owner_name': line[3], - 'amount': line[7], - 'commission': line[8], - 'balance': line[27], - 'transac_ref': line[23], - 'ref': line[12], - 'line_nr': i, - } - for field in ['commission', 'amount', 'balance']: - _logger.debug('Trying to convert %s to float' % rline[field]) - try: - rline[field] = self._paypal_convert_amount(rline[field]) - except: - raise Warning( - _("Value '%s' for the field '%s' on line %d, " - "cannot be converted to float") - % (rline[field], field, i)) - if rline['amount'] > 0: - rline['name'] = line[3] + ' ' + line[10] - rline['partner_email'] = line[10] - if not paypal_email_account: - paypal_email_account = line[11] - else: - rline['name'] = line[3] + ' ' + line[11] - rline['partner_email'] = line[11] - if not paypal_email_account: - paypal_email_account = line[10] - raw_lines.append(rline) - - # Second pass to sort out the lines in other currencies - final_lines = [] - other_currency_line = {} - for wline in raw_lines: - if company_currency_name != wline['currency']: - if wline['transac_ref'] and not other_currency_line: - currencies = self.env['res.currency'].search( - [('name', '=', wline['currency'])]) - if not currencies: - raise Warning( - _('Currency %s on line %d cannot be found in Odoo') - % (wline['currency'], wline['line_nr'])) - other_currency_line = { - 'amount_currency': wline['amount'], - 'currency_id': currencies[0].id, - 'currency': wline['currency'], - 'name': wline['name'], - 'owner_name': wline['owner_name'], - 'transac_ref': wline['transac_ref'], - } - - if other_currency_line and not wline['transac_ref']: - assert ( - wline['currency'] == other_currency_line['currency']),\ - 'WRONG currency' - assert ( - wline['amount'] == - other_currency_line['amount_currency'] * -1),\ - 'WRONG amount' - if ( - other_currency_line and - wline['ref'] == - other_currency_line['transac_ref']): - # reset other_currency_line - other_currency_line = {} - else: - if ( - other_currency_line and - wline['transac_ref'] == - other_currency_line['transac_ref']): - wline.update(other_currency_line) - # reset other_currency_line - other_currency_line = {} - final_lines.append(wline) - - # PayPal statements start with the end ! - final_lines.reverse() - j = 0 - for fline in final_lines: - j += 1 - commission_total += fline['commission'] - - if j == 1: - start_date_str = fline['date'] - start_balance = fline['balance'] - fline['amount'] - end_date_str = fline['date'] - end_balance = fline['balance'] - partners = False - if fline['partner_email']: - partners = self.env['res.partner'].search( - [('email', '=', fline['partner_email'])]) - if partners: - partner_id = partners[0].commercial_partner_id.id - else: - partner_id = False - vals_line = { - 'date': fline['date'], - 'name': fline['ref'], - 'ref': fline['name'], - 'unique_import_id': fline['ref'], - 'amount': fline['amount'], - 'partner_id': partner_id, - 'bank_account_id': False, - 'currency_id': fline.get('currency_id'), - 'amount_currency': fline.get('amount_currency'), - } - _logger.debug("vals_line = %s" % vals_line) - transactions.append(vals_line) - - if commission_total: - commission_line = { - 'date': end_date_str, - 'name': _('Paypal commissions'), - 'ref': _('PAYPAL-COSTS'), - 'amount': commission_total, - 'unique_import_id': False, - } - transactions.append(commission_line) - - vals_bank_statement = { - 'name': _('PayPal Import %s > %s') - % (start_date_str, end_date_str), - 'balance_start': start_balance, - 'balance_end_real': end_balance, - 'transactions': transactions, - } - return None, None, [vals_bank_statement] diff --git a/account_bank_statement_import_paypal/models/__init__.py b/account_bank_statement_import_paypal/models/__init__.py new file mode 100644 index 0000000..c950202 --- /dev/null +++ b/account_bank_statement_import_paypal/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import account_bank_statement_import_paypal diff --git a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py new file mode 100644 index 0000000..0d3975b --- /dev/null +++ b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py @@ -0,0 +1,275 @@ +# -*- coding: utf-8 -*- +# Copyright 2014-2017 Akretion (http://www.akretion.com). +# @author Alexis de Lattre +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging +from datetime import datetime +from openerp import models, fields, api, _ +from openerp.exceptions import UserError +import re +from cStringIO import StringIO +_logger = logging.getLogger(__name__) + +try: + import unicodecsv +except (ImportError, IOError) as err: + _logger.debug(err) + +# Paypal header depend of the country the order are the same but the +# value are translated. You can add you header here + +HEADERS = [ + # French + '"Date","Heure","Fuseau horaire","Description","Devise","Avant commission"' + ',"Commission","Net","Solde","Numéro de transaction","Adresse email de ' + 'l\'expéditeur","Nom","Nom de la banque","Compte bancaire","Montant des ' + 'frais de livraison et de traitement","TVA","Identifiant de facture",' + '"Numéro de la transaction de référence"', + # English + '"Date","Time","Time Zone","Description","Currency","Gross ","Fee ","Net",' + '"Balance","Transaction ID","From Email Address","Name","Bank Name",' + '"Bank Account","Shipping and Handling Amount","Sales Tax","Invoice ID",' + '"Reference Txn ID"' + ] + + +class AccountBankStatementImport(models.TransientModel): + _inherit = 'account.bank.statement.import' + + @api.model + def _get_paypal_encoding(self): + return 'utf-8' + + @api.model + def _get_paypal_date_format(self): + '''This method is designed to be inherited''' + return '%d/%m/%Y' + + @api.model + def _paypal_convert_amount(self, amount_str): + '''This method is designed to be inherited''' + valstr = re.sub(r'[^\d,.-]', '', amount_str) + valstrdot = valstr.replace('.', '') + valstrdot = valstrdot.replace(',', '.') + return float(valstrdot) + + @api.model + def _check_paypal(self, data_file): + for header in HEADERS: + if data_file.strip().startswith(header): + return True + return False + + def _convert_paypal_line_to_dict(self, idx, line): + date_dt = datetime.strptime(line[0], self._get_paypal_date_format()) + rline = { + 'date': fields.Date.to_string(date_dt), + 'time': line[1], + 'description': line[3], + 'currency': line[4], + 'amount': line[5], + 'commission': line[6], + 'balance': line[8], + 'transaction_id': line[9], + 'email': line[10], + 'partner_name': line[11], + # This two field are usefull for bank transfert + 'bank_name': line[12], + 'bank_account': line[13], + 'invoice_number': line[16], + 'origin_transaction_id': line[17], + 'idx': idx, + } + for field in ['commission', 'amount', 'balance']: + _logger.debug('Trying to convert %s to float' % rline[field]) + try: + rline[field] = self._paypal_convert_amount(rline[field]) + except: + raise UserError( + _("Value '%s' for the field '%s' on line %d, " + "cannot be converted to float") + % (rline[field], field, idx)) + return rline + + def _parse_paypal_file(self, data_file): + f = StringIO() + f.write(data_file) + f.seek(0) + raw_lines = [] + reader = unicodecsv.reader(f, encoding=self._get_paypal_encoding()) + reader.next() # Drop header + for idx, line in enumerate(reader): + _logger.debug("Line %d: %s" % (idx, line)) + raw_lines.append(self._convert_paypal_line_to_dict(idx, line)) + return raw_lines + + def _prepare_paypal_currency_vals(self, cline): + currencies = self.env['res.currency'].search( + [('name', '=', cline['currency'])]) + if not currencies: + raise UserError( + _('currency %s on line %d cannot be found in odoo') + % (cline['currency'], cline['idx'])) + return { + 'amount_currency': cline['amount'], + 'currency_id': currencies.id, + 'currency': cline['currency'], + 'partner_name': cline['partner_name'], + 'description': cline['description'], + 'email': cline['email'], + 'transaction_id': cline['transaction_id'], + } + + def _post_process_statement_line(self, raw_lines): + journal_id = self.env.context.get('journal_id') + if not journal_id: + raise UserError(_('You must run this wizard from the journal')) + journal = self.env['account.journal'].browse(journal_id) + currency = journal.currency_id or journal.company_id.currency_id + currency_change_lines = {} + real_transactions = [] + for line in raw_lines: + if line['currency'] != currency.name: + currency_change_lines[line['transaction_id']] = line + else: + real_transactions.append(line) + + for line in real_transactions: + # Check if the current transaction is linked with a + # transaction of currency change if yes merge the transaction + # as for odoo it's only one line + cline = currency_change_lines.get(line['origin_transaction_id']) + if cline: + # we update the current line with currency information + vals = self._prepare_paypal_currency_vals(cline) + line.update(vals) + return real_transactions + + def _prepare_paypal_statement_line(self, fline): + if fline['bank_name']: + name = '|'.join([ + fline['description'], + fline['bank_name'], + fline['bank_account'] + ]) + else: + name = '|'.join([ + fline['description'], + fline['partner_name'], + fline['email'], + fline['invoice_number'], + ]) + return { + 'date': fline['date'], + 'name': name, + 'ref': fline['transaction_id'], + 'unique_import_id': + fline['transaction_id'] + fline['date'] + fline['time'], + 'amount': fline['amount'], + 'bank_account_id': False, + 'currency_id': fline.get('currency_id'), + 'amount_currency': fline.get('amount_currency'), + } + + def _prepare_paypal_statement(self, lines): + return { + 'name': + _('PayPal Import %s > %s') + % (lines[0]['date'], lines[-1]['date']), + 'date': lines[-1]['date'], + 'balance_start': + lines[0]['balance'] - + lines[0]['amount'] - + lines[0]['commission'], + 'balance_end_real': lines[-1]['balance'], + } + + @api.model + def _parse_file(self, data_file): + """ Import a file in Paypal CSV format""" + paypal = self._check_paypal(data_file) + if not paypal: + return super(AccountBankStatementImport, self)._parse_file( + data_file) + + raw_lines = self._parse_paypal_file(data_file) + final_lines = self._post_process_statement_line(raw_lines) + + vals_bank_statement = self._prepare_paypal_statement(final_lines) + + transactions = [] + commission_total = 0 + for fline in final_lines: + commission_total += fline['commission'] + vals_line = self._prepare_paypal_statement_line(fline) + _logger.debug("vals_line = %s" % vals_line) + transactions.append(vals_line) + + if commission_total: + commission_line = { + 'date': vals_bank_statement['date'], + 'name': _('Paypal commissions'), + 'ref': _('PAYPAL-COSTS'), + 'amount': commission_total, + 'unique_import_id': False, + } + transactions.append(commission_line) + + vals_bank_statement['transactions'] = transactions + return None, None, [vals_bank_statement] + + @api.model + def _get_paypal_partner(self, description, partner_name, + partner_email, invoice_number): + if invoice_number: + # In most case e-commerce case invoice_number + # will contain the sale order number + sale = self.env['sale.order'].search([ + ('name', '=', invoice_number)]) + if sale and len(sale) == 1: + return sale.partner_id.commercial_partner_id + + invoice = self.env['account.invoice'].search([ + ('number', '=', invoice_number)]) + if invoice and len(invoice) == 1: + return invoice.partner_id.commercial_partner_id + + if partner_email: + partner = self.env['res.partner'].search([ + ('email', '=', partner_email), + ('parent_id', '=', False)]) + if partner and len(partner) == 1: + return partner.commercial_partner_id + + if partner_name: + partner = self.env['res.partner'].search([ + ('name', '=ilike', partner_name)]) + if partner and len(partner) == 1: + return partner.commercial_partner_id + return None + + @api.model + def _complete_paypal_statement_line(self, line): + _logger.debug('Process line %s', line['name']) + info = line['name'].split('|') + if len(info) == 4: + partner = self._get_paypal_partner(*info) + if partner: + return { + 'partner_id': partner.id, + 'account_id': partner.property_account_receivable.id, + } + return None + + @api.model + def _complete_statement(self, stmts_vals, journal_id, account_number): + '''Match the partner from paypal information''' + stmts_vals = super(AccountBankStatementImport, self).\ + _complete_statement(stmts_vals, journal_id, account_number) + for line in stmts_vals['transactions']: + vals = self._complete_paypal_statement_line(line) + if vals: + line.update(vals) + return stmts_vals diff --git a/account_bank_statement_import_paypal/static/description/icon.png b/account_bank_statement_import_paypal/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5a237c234e73e3af7b60ec4bfda61639506379b9 GIT binary patch literal 6481 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 Z9&9huf?ZJO?yH1ATU}4BTIF@r{{ZB>*t-A# literal 0 HcmV?d00001 diff --git a/account_bank_statement_import_paypal/static/description/paypal_backoffice.png b/account_bank_statement_import_paypal/static/description/paypal_backoffice.png new file mode 100644 index 0000000000000000000000000000000000000000..a902aee13bea035e22df7db0059629d3bf347eb7 GIT binary patch literal 97703 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
TYeiKEFlk;DkWdNtFpX#oRiKiSjqcabJ;~^ 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 Date: Wed, 4 Jul 2018 11:16:17 +0200 Subject: [PATCH 05/13] [MIG]account_bank_statement_import_paypal: Migration to 11.0 --- .../README.rst | 88 +++++++++++++++---- .../__init__.py | 2 - .../__manifest__.py | 13 +-- .../models/__init__.py | 2 - .../account_bank_statement_import_paypal.py | 40 +++++---- .../readme/CONFIGURE.rst | 12 +++ .../readme/CONTRIBUTORS.rst | 5 ++ .../readme/DESCRIPTION.rst | 1 + .../readme/USAGE.rst | 8 ++ 9 files changed, 122 insertions(+), 49 deletions(-) create mode 100644 account_bank_statement_import_paypal/readme/CONFIGURE.rst create mode 100644 account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst create mode 100644 account_bank_statement_import_paypal/readme/DESCRIPTION.rst create mode 100644 account_bank_statement_import_paypal/readme/USAGE.rst diff --git a/account_bank_statement_import_paypal/README.rst b/account_bank_statement_import_paypal/README.rst index 1c80340..bdbff9f 100644 --- a/account_bank_statement_import_paypal/README.rst +++ b/account_bank_statement_import_paypal/README.rst @@ -1,49 +1,105 @@ +============================= Import Paypal Bank Statements ============================= +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fbank--statement--import-lightgray.png?logo=github + :target: https://github.com/OCA/bank-statement-import/tree/11.0/account_bank_statement_import_paypal + :alt: OCA/bank-statement-import +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/bank-statement-import-11-0/bank-statement-import-11-0-account_bank_statement_import_paypal + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/174/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + This module allows you to import the Paypal CSV files in Odoo as bank statements. +**Table of contents** + +.. contents:: + :local: + Configuration ============= -In the menu Accounting > Configuration > Accounts > Setup your Bank Accounts, make sure that you have your Paypal bank account with the following parameters: +In the menu Accounting > Configuration > Accounting > Bank Accounts, +make sure that you have your Paypal bank account with the following parameters: * Bank Account Type: Normal Bank Account * Account Number: the email address associated with your Paypal account * Account Journal: the journal associated to your Paypal account -============ +TIPS +---- +For now only French and English report are supported. +For adding new support you just need to add your header in +model/account_bank_statement_import_paypal.py in the variables HEADERS. +Please help us and do a PR for adding new header ! Thanks + +Usage +===== + +To use this module, you need to: -Go to Paypal and download your Bank Statement +#. Go to Paypal and download your Bank Statement .. image:: account_bank_statement_import_paypal/static/description/paypal_backoffice.png :alt: . .. image:: static/description/paypal_backoffice.png :alt: . +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + Credits ======= +Authors +------- + +* Akretion + Contributors ------------- +~~~~~~~~~~~~ * Alexis de Lattre * Sebastien BEAU +* Tecnativa (https://www.tecnativa.com) -TIPS --------- -For now only French and English report are supported -For adding new support you just need to add your header in model/account_bank_statement_import_paypal.py in the variables HEADERS. -Please help us and do a PR for adding new header ! Thanks + * Vicent Cubells -Maintainer ----------- +Maintainers +~~~~~~~~~~~ -.. image:: http://odoo-community.org/logo.png +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association - :target: http://odoo-community.org + :target: https://odoo-community.org -This module is maintained by the OCA. +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. -OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. +This module is part of the `OCA/bank-statement-import `_ project on GitHub. -To contribute to this module, please visit http://odoo-community.org. +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_bank_statement_import_paypal/__init__.py b/account_bank_statement_import_paypal/__init__.py index cde864b..0650744 100644 --- a/account_bank_statement_import_paypal/__init__.py +++ b/account_bank_statement_import_paypal/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import models diff --git a/account_bank_statement_import_paypal/__manifest__.py b/account_bank_statement_import_paypal/__manifest__.py index 4f20c11..ada7c9a 100644 --- a/account_bank_statement_import_paypal/__manifest__.py +++ b/account_bank_statement_import_paypal/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2014-2017 Akretion (http://www.akretion.com). # @author Alexis de Lattre # @author Sébastien BEAU @@ -6,24 +5,14 @@ { "name": "Import Paypal Bank Statements", 'summary': 'Import Paypal CSV files as Bank Statements in Odoo', - "version": "10.0.1.0.0", + "version": "11.0.1.0.0", "category": "Accounting", "website": "https://github.com/OCA/bank-statement-import", "author": " Akretion, Odoo Community Association (OCA)", "license": "AGPL-3", "application": False, "installable": True, - "external_dependencies": { - 'python': ['unicodecsv'], - "bin": [], - }, "depends": [ "account_bank_statement_import", ], - "data": [ - ], - "demo": [ - ], - "qweb": [ - ] } diff --git a/account_bank_statement_import_paypal/models/__init__.py b/account_bank_statement_import_paypal/models/__init__.py index c950202..4e11f72 100644 --- a/account_bank_statement_import_paypal/models/__init__.py +++ b/account_bank_statement_import_paypal/models/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import account_bank_statement_import_paypal diff --git a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py index 0d3975b..e15d15f 100644 --- a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py +++ b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2014-2017 Akretion (http://www.akretion.com). # @author Alexis de Lattre # @author Sébastien BEAU @@ -6,14 +5,14 @@ import logging from datetime import datetime -from openerp import models, fields, api, _ -from openerp.exceptions import UserError +from odoo import _, api, fields, models +from odoo.exceptions import UserError import re -from cStringIO import StringIO +from io import StringIO _logger = logging.getLogger(__name__) try: - import unicodecsv + import csv except (ImportError, IOError) as err: _logger.debug(err) @@ -31,7 +30,7 @@ HEADERS = [ '"Date","Time","Time Zone","Description","Currency","Gross ","Fee ","Net",' '"Balance","Transaction ID","From Email Address","Name","Bank Name",' '"Bank Account","Shipping and Handling Amount","Sales Tax","Invoice ID",' - '"Reference Txn ID"' + '"Reference Txn ID"', ] @@ -40,16 +39,22 @@ class AccountBankStatementImport(models.TransientModel): @api.model def _get_paypal_encoding(self): - return 'utf-8' + return 'utf-8-sig' + + @api.model + def _get_paypal_str_data(self, data_file): + if not isinstance(data_file, str): + data_file = data_file.decode(self._get_paypal_encoding()) + return data_file.strip() @api.model def _get_paypal_date_format(self): - '''This method is designed to be inherited''' + """ This method is designed to be inherited """ return '%d/%m/%Y' @api.model def _paypal_convert_amount(self, amount_str): - '''This method is designed to be inherited''' + """ This method is designed to be inherited """ valstr = re.sub(r'[^\d,.-]', '', amount_str) valstrdot = valstr.replace('.', '') valstrdot = valstrdot.replace(',', '.') @@ -57,6 +62,7 @@ class AccountBankStatementImport(models.TransientModel): @api.model def _check_paypal(self, data_file): + data_file = self._get_paypal_str_data(data_file) for header in HEADERS: if data_file.strip().startswith(header): return True @@ -75,7 +81,7 @@ class AccountBankStatementImport(models.TransientModel): 'transaction_id': line[9], 'email': line[10], 'partner_name': line[11], - # This two field are usefull for bank transfert + # This two field are useful for bank transfer 'bank_name': line[12], 'bank_account': line[13], 'invoice_number': line[16], @@ -86,7 +92,7 @@ class AccountBankStatementImport(models.TransientModel): _logger.debug('Trying to convert %s to float' % rline[field]) try: rline[field] = self._paypal_convert_amount(rline[field]) - except: + except Exception: raise UserError( _("Value '%s' for the field '%s' on line %d, " "cannot be converted to float") @@ -94,12 +100,12 @@ class AccountBankStatementImport(models.TransientModel): return rline def _parse_paypal_file(self, data_file): - f = StringIO() - f.write(data_file) + data_file = self._get_paypal_str_data(data_file) + f = StringIO(data_file) f.seek(0) raw_lines = [] - reader = unicodecsv.reader(f, encoding=self._get_paypal_encoding()) - reader.next() # Drop header + reader = csv.reader(f) + next(reader) # Drop header for idx, line in enumerate(reader): _logger.debug("Line %d: %s" % (idx, line)) raw_lines.append(self._convert_paypal_line_to_dict(idx, line)) @@ -188,7 +194,7 @@ class AccountBankStatementImport(models.TransientModel): @api.model def _parse_file(self, data_file): - """ Import a file in Paypal CSV format""" + """ Import a file in Paypal CSV format """ paypal = self._check_paypal(data_file) if not paypal: return super(AccountBankStatementImport, self)._parse_file( @@ -265,7 +271,7 @@ class AccountBankStatementImport(models.TransientModel): @api.model def _complete_statement(self, stmts_vals, journal_id, account_number): - '''Match the partner from paypal information''' + """ Match the partner from paypal information """ stmts_vals = super(AccountBankStatementImport, self).\ _complete_statement(stmts_vals, journal_id, account_number) for line in stmts_vals['transactions']: diff --git a/account_bank_statement_import_paypal/readme/CONFIGURE.rst b/account_bank_statement_import_paypal/readme/CONFIGURE.rst new file mode 100644 index 0000000..33f0236 --- /dev/null +++ b/account_bank_statement_import_paypal/readme/CONFIGURE.rst @@ -0,0 +1,12 @@ +In the menu Accounting > Configuration > Accounting > Bank Accounts, +make sure that you have your Paypal bank account with the following parameters: +* Bank Account Type: Normal Bank Account +* Account Number: the email address associated with your Paypal account +* Account Journal: the journal associated to your Paypal account + +TIPS +---- +For now only French and English report are supported. +For adding new support you just need to add your header in +model/account_bank_statement_import_paypal.py in the variables HEADERS. +Please help us and do a PR for adding new header ! Thanks diff --git a/account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst b/account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..480d383 --- /dev/null +++ b/account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst @@ -0,0 +1,5 @@ +* Alexis de Lattre +* Sebastien BEAU +* Tecnativa (https://www.tecnativa.com) + + * Vicent Cubells diff --git a/account_bank_statement_import_paypal/readme/DESCRIPTION.rst b/account_bank_statement_import_paypal/readme/DESCRIPTION.rst new file mode 100644 index 0000000..4da61e3 --- /dev/null +++ b/account_bank_statement_import_paypal/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module allows you to import the Paypal CSV files in Odoo as bank statements. diff --git a/account_bank_statement_import_paypal/readme/USAGE.rst b/account_bank_statement_import_paypal/readme/USAGE.rst new file mode 100644 index 0000000..fdcc4da --- /dev/null +++ b/account_bank_statement_import_paypal/readme/USAGE.rst @@ -0,0 +1,8 @@ +To use this module, you need to: + +#. Go to Paypal and download your Bank Statement + +.. image:: account_bank_statement_import_paypal/static/description/paypal_backoffice.png + :alt: . +.. image:: static/description/paypal_backoffice.png + :alt: . From dd3164bc81df4dc6ec2518ce9a54bdcaf706e559 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Wed, 21 Nov 2018 01:16:44 +0000 Subject: [PATCH 06/13] [UPD] Update account_bank_statement_import_paypal.pot --- .../account_bank_statement_import_paypal.pot | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot diff --git a/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot b/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot new file mode 100644 index 0000000..c5b0e90 --- /dev/null +++ b/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot @@ -0,0 +1,56 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_bank_statement_import_paypal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \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_bank_statement_import_paypal +#: model:ir.model,name:account_bank_statement_import_paypal.model_account_bank_statement_import +msgid "Import Bank Statement" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:220 +#, python-format +msgid "PAYPAL-COSTS" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:185 +#, python-format +msgid "PayPal Import %s > %s" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:219 +#, python-format +msgid "Paypal commissions" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:97 +#, python-format +msgid "Value '%s' for the field '%s' on line %d, cannot be converted to float" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:134 +#, python-format +msgid "You must run this wizard from the journal" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:119 +#, python-format +msgid "currency %s on line %d cannot be found in odoo" +msgstr "" + From bc584b91def6d530dfeadecc993de473a95df6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul=20=28ACSONE=29?= Date: Sat, 24 Nov 2018 16:23:50 +0100 Subject: [PATCH 07/13] [FIX] title in readme fragment --- account_bank_statement_import_paypal/readme/CONFIGURE.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account_bank_statement_import_paypal/readme/CONFIGURE.rst b/account_bank_statement_import_paypal/readme/CONFIGURE.rst index 33f0236..0be0958 100644 --- a/account_bank_statement_import_paypal/readme/CONFIGURE.rst +++ b/account_bank_statement_import_paypal/readme/CONFIGURE.rst @@ -5,7 +5,7 @@ make sure that you have your Paypal bank account with the following parameters: * Account Journal: the journal associated to your Paypal account TIPS ----- +~~~~ For now only French and English report are supported. For adding new support you just need to add your header in model/account_bank_statement_import_paypal.py in the variables HEADERS. From 8609e0c22ceae04640bb659859f663203bcc6795 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Sat, 24 Nov 2018 15:24:22 +0000 Subject: [PATCH 08/13] [UPD] README.rst --- .../README.rst | 10 +- .../static/description/index.html | 453 ++++++++++++++++++ 2 files changed, 458 insertions(+), 5 deletions(-) create mode 100644 account_bank_statement_import_paypal/static/description/index.html diff --git a/account_bank_statement_import_paypal/README.rst b/account_bank_statement_import_paypal/README.rst index bdbff9f..b7558f3 100644 --- a/account_bank_statement_import_paypal/README.rst +++ b/account_bank_statement_import_paypal/README.rst @@ -23,7 +23,7 @@ Import Paypal Bank Statements :target: https://runbot.odoo-community.org/runbot/174/11.0 :alt: Try me on Runbot -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| This module allows you to import the Paypal CSV files in Odoo as bank statements. @@ -42,7 +42,7 @@ make sure that you have your Paypal bank account with the following parameters: * Account Journal: the journal associated to your Paypal account TIPS ----- +~~~~ For now only French and English report are supported. For adding new support you just need to add your header in model/account_bank_statement_import_paypal.py in the variables HEADERS. @@ -55,9 +55,9 @@ To use this module, you need to: #. Go to Paypal and download your Bank Statement -.. image:: account_bank_statement_import_paypal/static/description/paypal_backoffice.png +.. image:: https://raw.githubusercontent.com/OCA/bank-statement-import/11.0/account_bank_statement_import_paypal/account_bank_statement_import_paypal/static/description/paypal_backoffice.png :alt: . -.. image:: static/description/paypal_backoffice.png +.. image:: https://raw.githubusercontent.com/OCA/bank-statement-import/11.0/account_bank_statement_import_paypal/static/description/paypal_backoffice.png :alt: . Bug Tracker @@ -74,7 +74,7 @@ Credits ======= Authors -------- +~~~~~~~ * Akretion diff --git a/account_bank_statement_import_paypal/static/description/index.html b/account_bank_statement_import_paypal/static/description/index.html new file mode 100644 index 0000000..5c4b767 --- /dev/null +++ b/account_bank_statement_import_paypal/static/description/index.html @@ -0,0 +1,453 @@ + + + + + + +Import Paypal Bank Statements + + + + + + From 6614330ee5c21d3d89ffe67cc02e66cfde249ab9 Mon Sep 17 00:00:00 2001 From: cubells Date: Fri, 11 Jan 2019 11:48:33 +0100 Subject: [PATCH 09/13] [IMP] account_bank_statement_import_paypal: create paypal maps --- .../README.rst | 27 +- .../__init__.py | 1 + .../__manifest__.py | 12 +- .../data/paypal_map_data.xml | 114 ++++++ .../account_bank_statement_import_paypal.pot | 295 ++++++++++++++- .../i18n/es.po | 348 ++++++++++++++++++ .../models/__init__.py | 3 +- ...ccount_bank_statement_import_paypal_map.py | 64 ++++ .../models/account_journal.py | 13 + .../readme/CONFIGURE.rst | 22 +- .../readme/DESCRIPTION.rst | 3 +- .../security/ir.model.access.csv | 3 + .../static/description/index.html | 55 ++- .../tests/__init__.py | 1 + .../tests/paypal_en.csv | 3 + .../tests/test_paypal_statement_import.py | 54 +++ .../views/account_journal_views.xml | 16 + .../views/paypal_map_views.xml | 65 ++++ .../wizards/__init__.py | 2 + .../account_bank_statement_import_paypal.py | 83 +++-- .../account_bank_statement_import_view.xml | 14 + .../wizards/create_map_lines_from_file.py | 38 ++ .../create_map_lines_from_file_views.xml | 29 ++ 23 files changed, 1167 insertions(+), 98 deletions(-) create mode 100644 account_bank_statement_import_paypal/data/paypal_map_data.xml create mode 100644 account_bank_statement_import_paypal/i18n/es.po create mode 100644 account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py create mode 100644 account_bank_statement_import_paypal/models/account_journal.py create mode 100644 account_bank_statement_import_paypal/security/ir.model.access.csv create mode 100644 account_bank_statement_import_paypal/tests/__init__.py create mode 100644 account_bank_statement_import_paypal/tests/paypal_en.csv create mode 100644 account_bank_statement_import_paypal/tests/test_paypal_statement_import.py create mode 100644 account_bank_statement_import_paypal/views/account_journal_views.xml create mode 100644 account_bank_statement_import_paypal/views/paypal_map_views.xml create mode 100644 account_bank_statement_import_paypal/wizards/__init__.py rename account_bank_statement_import_paypal/{models => wizards}/account_bank_statement_import_paypal.py (83%) create mode 100644 account_bank_statement_import_paypal/wizards/account_bank_statement_import_view.xml create mode 100644 account_bank_statement_import_paypal/wizards/create_map_lines_from_file.py create mode 100644 account_bank_statement_import_paypal/wizards/create_map_lines_from_file_views.xml diff --git a/account_bank_statement_import_paypal/README.rst b/account_bank_statement_import_paypal/README.rst index b7558f3..0fa92e3 100644 --- a/account_bank_statement_import_paypal/README.rst +++ b/account_bank_statement_import_paypal/README.rst @@ -25,7 +25,8 @@ Import Paypal Bank Statements |badge1| |badge2| |badge3| |badge4| |badge5| -This module allows you to import the Paypal CSV files in Odoo as bank statements. +This module allows you to import the Paypal CSV files in Odoo as bank +statements. **Table of contents** @@ -35,18 +36,18 @@ This module allows you to import the Paypal CSV files in Odoo as bank statements Configuration ============= -In the menu Accounting > Configuration > Accounting > Bank Accounts, -make sure that you have your Paypal bank account with the following parameters: -* Bank Account Type: Normal Bank Account -* Account Number: the email address associated with your Paypal account -* Account Journal: the journal associated to your Paypal account - -TIPS -~~~~ -For now only French and English report are supported. -For adding new support you just need to add your header in -model/account_bank_statement_import_paypal.py in the variables HEADERS. -Please help us and do a PR for adding new header ! Thanks +* Create or go to a bank journal where you want to import Paypal statement. +* Edit that journal and set a Paypal map in **Paypal Map** section in **Advanced + Settings** tab. + +* Now you can import Paypal statements in that journal. + +Note: if existent Paypal Map does not fit to your file to import, you can +create another map in **Invoicing > Configuration > Accounting > Paypal +Mapping**. + +You can import headers from any Paypal file in **Action > Create Paypal Map +Lines** and set every line with which field of statement have to match. Usage ===== diff --git a/account_bank_statement_import_paypal/__init__.py b/account_bank_statement_import_paypal/__init__.py index 0650744..aee8895 100644 --- a/account_bank_statement_import_paypal/__init__.py +++ b/account_bank_statement_import_paypal/__init__.py @@ -1 +1,2 @@ from . import models +from . import wizards diff --git a/account_bank_statement_import_paypal/__manifest__.py b/account_bank_statement_import_paypal/__manifest__.py index ada7c9a..f4af623 100644 --- a/account_bank_statement_import_paypal/__manifest__.py +++ b/account_bank_statement_import_paypal/__manifest__.py @@ -5,14 +5,22 @@ { "name": "Import Paypal Bank Statements", 'summary': 'Import Paypal CSV files as Bank Statements in Odoo', - "version": "11.0.1.0.0", + "version": "11.0.2.0.0", "category": "Accounting", "website": "https://github.com/OCA/bank-statement-import", "author": " Akretion, Odoo Community Association (OCA)", "license": "AGPL-3", - "application": False, "installable": True, "depends": [ "account_bank_statement_import", + "sale", ], + "data": [ + "security/ir.model.access.csv", + "data/paypal_map_data.xml", + "wizards/create_map_lines_from_file_views.xml", + "wizards/account_bank_statement_import_view.xml", + "views/account_journal_views.xml", + "views/paypal_map_views.xml", + ] } diff --git a/account_bank_statement_import_paypal/data/paypal_map_data.xml b/account_bank_statement_import_paypal/data/paypal_map_data.xml new file mode 100644 index 0000000..be289c7 --- /dev/null +++ b/account_bank_statement_import_paypal/data/paypal_map_data.xml @@ -0,0 +1,114 @@ + + + + + Paypal Monthly Statement + + + + Date + 0 + + date + %m/%d/%Y + + + Time + 1 + + time + + + Time Zone + 2 + + + + Description + 3 + + description + + + Currency + 4 + + currency + + + Gross + 5 + + amount + + + Fee + 6 + + commission + + + Net + 7 + + + + Balance + 8 + + balance + + + Transaction ID + 9 + + transaction_id + + + From Email Address + 10 + + email + + + Name + 11 + + partner_name + + + Bank Name + 12 + + bank_name + + + Bank Account + 13 + + bank_account + + + Shipping and Handling Amount + 14 + + + + Sales Tax + 15 + + + + Invoice ID + 16 + + invoice_number + + + Reference Txn ID + 17 + + origin_transaction_id + + + diff --git a/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot b/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot index c5b0e90..3e24e19 100644 --- a/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot +++ b/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot @@ -6,6 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 11.0\n" "Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-01-11 10:43+0000\n" +"PO-Revision-Date: 2019-01-11 10:43+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -13,44 +15,325 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "All the Paypal map lines will be created automatically." +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Balance" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Bank Account" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Bank Name" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_data_file +msgid "Bank Statement File" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "Cancel" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "Choose a file to import..." +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "Create Lines" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.actions.act_window,name:account_bank_statement_import_paypal.action_create_paypal_map_lines +msgid "Create Paypal Map Lines" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_create_uid +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_create_uid +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_create_uid +msgid "Created by" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_create_date +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_create_date +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_create_date +msgid "Created on" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Currency" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Date" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_date_format +msgid "Date Format" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:90 +#, python-format +msgid "Date format of map file and Paypal date does not match." +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Description" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_display_name +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_display_name +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_display_name +msgid "Display Name" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "Download a bank statement from your bank and import it here." +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Fee" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_sequence +msgid "Field number" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_filename +msgid "Filename" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "From Email Address" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Gross" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_name +msgid "Header Name" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:73 +#, python-format +msgid "Headers of file to import and Paypal map lines does not match." +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_id +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_id +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_id +msgid "ID" +msgstr "" + #. module: account_bank_statement_import_paypal #: model:ir.model,name:account_bank_statement_import_paypal.model_account_bank_statement_import msgid "Import Bank Statement" msgstr "" #. module: account_bank_statement_import_paypal -#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:220 +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "Import Paypal Map Lines" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Invoice ID" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model,name:account_bank_statement_import_paypal.model_account_journal +msgid "Journal" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map___last_update +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line___last_update +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create___last_update +msgid "Last Modified on" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_write_uid +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_write_uid +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_write_date +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_write_date +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_write_date +msgid "Last Updated on" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_map_parent_id +msgid "Map Parent" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_map_line_ids +msgid "Map lines" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.statement_import_map_tax_form +msgid "Mapping Lines" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_name +msgid "Name" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Origin Transaction ID" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:226 #, python-format msgid "PAYPAL-COSTS" msgstr "" #. module: account_bank_statement_import_paypal -#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:185 +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:191 #, python-format msgid "PayPal Import %s > %s" msgstr "" #. module: account_bank_statement_import_paypal -#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:219 +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_journal_paypal_map_id +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.statement_import_map_tax_form +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.statement_import_map_tax_tree +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.view_account_journal_form_n43 +msgid "Paypal Map" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.actions.act_window,name:account_bank_statement_import_paypal.action_statement_import_paypal_mappging +#: model:ir.ui.menu,name:account_bank_statement_import_paypal.menu_statement_import_paypal_mapping +msgid "Paypal Mapping" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:225 #, python-format msgid "Paypal commissions" msgstr "" #. module: account_bank_statement_import_paypal -#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:97 +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_id_4185 +msgid "Paypal map" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.statement_import_map_tax_line_form +msgid "Paypal mapping line" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.statement_import_map_tax_line_tree +msgid "Paypal mapping lines" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.account_bank_statement_import_view +msgid "Paypal with Template:" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "Select a Paypal bank statement file to create all the map lines from headers file." +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_field_to_assign +msgid "Statement Field to Assign" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Time" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Transaction ID" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:100 #, python-format msgid "Value '%s' for the field '%s' on line %d, cannot be converted to float" msgstr "" #. module: account_bank_statement_import_paypal -#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:134 +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:137 #, python-format msgid "You must run this wizard from the journal" msgstr "" #. module: account_bank_statement_import_paypal -#: code:addons/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py:119 +#: model:ir.model,name:account_bank_statement_import_paypal.model_account_bank_statement_import_paypal_map +msgid "account.bank.statement.import.paypal.map" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model,name:account_bank_statement_import_paypal.model_account_bank_statement_import_paypal_map_line +msgid "account.bank.statement.import.paypal.map.line" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:122 #, python-format msgid "currency %s on line %d cannot be found in odoo" msgstr "" +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,date_format:0 +msgid "i.e. 12/15/2019" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,date_format:0 +msgid "i.e. 15/12/2019" +msgstr "" + +#. module: account_bank_statement_import_paypal +#: model:ir.model,name:account_bank_statement_import_paypal.model_wizard_paypal_map_create +msgid "wizard.paypal.map.create" +msgstr "" + diff --git a/account_bank_statement_import_paypal/i18n/es.po b/account_bank_statement_import_paypal/i18n/es.po new file mode 100644 index 0000000..d2c58f0 --- /dev/null +++ b/account_bank_statement_import_paypal/i18n/es.po @@ -0,0 +1,348 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_bank_statement_import_paypal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-01-11 10:43+0000\n" +"PO-Revision-Date: 2019-01-11 11:44+0100\n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"X-Generator: Poedit 2.1.1\n" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "All the Paypal map lines will be created automatically." +msgstr "Todas las lineas de la plantilla se crearán automáticamente." + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Balance" +msgstr "Saldo" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Bank Account" +msgstr "Cuenta bancaria" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Bank Name" +msgstr "Nombre del banco" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_data_file +msgid "Bank Statement File" +msgstr "Archivo de extracto bancario" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "Cancel" +msgstr "Cancelar" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "Choose a file to import..." +msgstr "Escoja un archivo a importar..." + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "Create Lines" +msgstr "Crear líneas" + +#. module: account_bank_statement_import_paypal +#: model:ir.actions.act_window,name:account_bank_statement_import_paypal.action_create_paypal_map_lines +msgid "Create Paypal Map Lines" +msgstr "Crear las líneas de la plantilla de Paypal" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_create_uid +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_create_uid +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_create_uid +msgid "Created by" +msgstr "Creado el" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_create_date +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_create_date +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_create_date +msgid "Created on" +msgstr "Creado por" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Currency" +msgstr "Moneda" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Date" +msgstr "Fecha" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_date_format +msgid "Date Format" +msgstr "Formato de fecha" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:90 +#, python-format +msgid "Date format of map file and Paypal date does not match." +msgstr "" +"El format de fecha del archivo de plantilla y la fecha de Paypal no " +"coinciden." + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Description" +msgstr "Descripción" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_display_name +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_display_name +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "Download a bank statement from your bank and import it here." +msgstr "Descargue un extracto bancario desde su banco e impórtelo aquí." + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Fee" +msgstr "Tarifa" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_sequence +msgid "Field number" +msgstr "Nº de campo" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_filename +msgid "Filename" +msgstr "Nombre de archivo" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "From Email Address" +msgstr "Correo electrónico del remitente" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Gross" +msgstr "Bruto" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_name +msgid "Header Name" +msgstr "Nombre de la cabecera" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:73 +#, python-format +msgid "Headers of file to import and Paypal map lines does not match." +msgstr "" +"La cabecera del archivo a importar y las lineas de la plantilla de Paypal no " +"coinciden." + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_id +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_id +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_id +msgid "ID" +msgstr "ID" + +#. module: account_bank_statement_import_paypal +#: model:ir.model,name:account_bank_statement_import_paypal.model_account_bank_statement_import +msgid "Import Bank Statement" +msgstr "Importar Extracto Bancario" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "Import Paypal Map Lines" +msgstr "Importar las lineas de la plantilla de Paypal" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Invoice ID" +msgstr "Número de factura" + +#. module: account_bank_statement_import_paypal +#: model:ir.model,name:account_bank_statement_import_paypal.model_account_journal +msgid "Journal" +msgstr "Diario" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map___last_update +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line___last_update +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create___last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_write_uid +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_write_uid +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_write_date +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_write_date +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_wizard_paypal_map_create_write_date +msgid "Last Updated on" +msgstr "Última actualización en" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_map_parent_id +msgid "Map Parent" +msgstr "Plantilla padre" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_map_line_ids +msgid "Map lines" +msgstr "Líneas de la plantilla" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.statement_import_map_tax_form +msgid "Mapping Lines" +msgstr "Líneas de la plantilla" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_name +msgid "Name" +msgstr "Nombre" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Origin Transaction ID" +msgstr "Id. de transacción" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:226 +#, python-format +msgid "PAYPAL-COSTS" +msgstr "COSTES-PAYPAL" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:191 +#, python-format +msgid "PayPal Import %s > %s" +msgstr "Importación de Paypal %s > %s" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_journal_paypal_map_id +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.statement_import_map_tax_form +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.statement_import_map_tax_tree +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.view_account_journal_form_n43 +msgid "Paypal Map" +msgstr "Plantilla de Paypal" + +#. module: account_bank_statement_import_paypal +#: model:ir.actions.act_window,name:account_bank_statement_import_paypal.action_statement_import_paypal_mappging +#: model:ir.ui.menu,name:account_bank_statement_import_paypal.menu_statement_import_paypal_mapping +msgid "Paypal Mapping" +msgstr "Plantilla de Paypal" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:225 +#, python-format +msgid "Paypal commissions" +msgstr "Tarifa de Paypal" + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_id_4185 +msgid "Paypal map" +msgstr "Plantilla de Paypal" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.statement_import_map_tax_line_form +msgid "Paypal mapping line" +msgstr "Línea de plantilla de Paypal" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.statement_import_map_tax_line_tree +msgid "Paypal mapping lines" +msgstr "Líneas de plantilla de Paypal" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.account_bank_statement_import_view +msgid "Paypal with Template:" +msgstr "Paypal con la plantilla:" + +#. module: account_bank_statement_import_paypal +#: model:ir.ui.view,arch_db:account_bank_statement_import_paypal.create_paypal_map_lines_view +msgid "" +"Select a Paypal bank statement file to create all the map lines from headers " +"file." +msgstr "" +"Seleccione un archivo de extracto bancario de Paypal para crear todas las " +"lineas de la plantilla a partir de la cabecera del archivo." + +#. module: account_bank_statement_import_paypal +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_line_field_to_assign +msgid "Statement Field to Assign" +msgstr "Campo del extracto a asignar" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Time" +msgstr "Hora" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,field_to_assign:0 +msgid "Transaction ID" +msgstr "Id. de transacción" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:100 +#, python-format +msgid "Value '%s' for the field '%s' on line %d, cannot be converted to float" +msgstr "" +"El valor '%s' del campo '%s' en la línea %d, no se puede convertir a float" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:137 +#, python-format +msgid "You must run this wizard from the journal" +msgstr "Debe ejecutar este asistente des del diario" + +#. module: account_bank_statement_import_paypal +#: model:ir.model,name:account_bank_statement_import_paypal.model_account_bank_statement_import_paypal_map +msgid "account.bank.statement.import.paypal.map" +msgstr "account.bank.statement.import.paypal.map" + +#. module: account_bank_statement_import_paypal +#: model:ir.model,name:account_bank_statement_import_paypal.model_account_bank_statement_import_paypal_map_line +msgid "account.bank.statement.import.paypal.map.line" +msgstr "account.bank.statement.import.paypal.map.line" + +#. module: account_bank_statement_import_paypal +#: code:addons/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py:122 +#, python-format +msgid "currency %s on line %d cannot be found in odoo" +msgstr "la monea %s de la línea %d no se puede encontrar en odoo" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,date_format:0 +msgid "i.e. 12/15/2019" +msgstr "p.e. 12/15/2019" + +#. module: account_bank_statement_import_paypal +#: selection:account.bank.statement.import.paypal.map.line,date_format:0 +msgid "i.e. 15/12/2019" +msgstr "p.e. 15/12/2019" + +#. module: account_bank_statement_import_paypal +#: model:ir.model,name:account_bank_statement_import_paypal.model_wizard_paypal_map_create +msgid "wizard.paypal.map.create" +msgstr "wizard.paypal.map.create" diff --git a/account_bank_statement_import_paypal/models/__init__.py b/account_bank_statement_import_paypal/models/__init__.py index 4e11f72..4257b3b 100644 --- a/account_bank_statement_import_paypal/models/__init__.py +++ b/account_bank_statement_import_paypal/models/__init__.py @@ -1 +1,2 @@ -from . import account_bank_statement_import_paypal +from . import account_bank_statement_import_paypal_map +from . import account_journal diff --git a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py new file mode 100644 index 0000000..159dfd4 --- /dev/null +++ b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py @@ -0,0 +1,64 @@ +# Copyright 2019 Tecnativa - Vicent Cubells +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class AccountBankStatementImportPaypalMap(models.Model): + _name = 'account.bank.statement.import.paypal.map' + + name = fields.Char( + required=True, + ) + map_line_ids = fields.One2many( + comodel_name='account.bank.statement.import.paypal.map.line', + inverse_name='map_parent_id', + string="Map lines", + required=True, + copy=True, + ) + + +class AccountBankStatementImportPaypalMapLIne(models.Model): + _name = 'account.bank.statement.import.paypal.map.line' + _order = "sequence asc, id asc" + + sequence = fields.Integer( + string="Field number", + required=True, + ) + name = fields.Char( + string="Header Name", + required=True, + ) + map_parent_id = fields.Many2one( + comodel_name='account.bank.statement.import.paypal.map', + required=True, + ondelete='cascade', + ) + field_to_assign = fields.Selection( + selection=[ + ('date', 'Date'), + ('time', 'Time'), + ('description', 'Description'), + ('currency', 'Currency'), + ('amount', 'Gross'), + ('commission', 'Fee'), + ('balance', 'Balance'), + ('transaction_id', 'Transaction ID'), + ('email', 'From Email Address'), + ('partner_name', 'Name'), + ('bank_name', 'Bank Name'), + ('bank_account', 'Bank Account'), + ('invoice_number', 'Invoice ID'), + ('origin_transaction_id', 'Origin Transaction ID'), + ], + string="Statement Field to Assign", + ) + date_format = fields.Selection( + selection=[ + ('%d/%m/%Y', 'i.e. 15/12/2019'), + ('%m/%d/%Y', 'i.e. 12/15/2019'), + ], + string="Date Format", + ) diff --git a/account_bank_statement_import_paypal/models/account_journal.py b/account_bank_statement_import_paypal/models/account_journal.py new file mode 100644 index 0000000..5123a3c --- /dev/null +++ b/account_bank_statement_import_paypal/models/account_journal.py @@ -0,0 +1,13 @@ +# Copyright 2019 Tecnativa - Vicent Cubells +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class AccountJournal(models.Model): + _inherit = "account.journal" + + paypal_map_id = fields.Many2one( + comodel_name='account.bank.statement.import.paypal.map', + string='Paypal Map', + ) diff --git a/account_bank_statement_import_paypal/readme/CONFIGURE.rst b/account_bank_statement_import_paypal/readme/CONFIGURE.rst index 0be0958..7693cae 100644 --- a/account_bank_statement_import_paypal/readme/CONFIGURE.rst +++ b/account_bank_statement_import_paypal/readme/CONFIGURE.rst @@ -1,12 +1,12 @@ -In the menu Accounting > Configuration > Accounting > Bank Accounts, -make sure that you have your Paypal bank account with the following parameters: -* Bank Account Type: Normal Bank Account -* Account Number: the email address associated with your Paypal account -* Account Journal: the journal associated to your Paypal account +* Create or go to a bank journal where you want to import Paypal statement. +* Edit that journal and set a Paypal map in **Paypal Map** section in **Advanced + Settings** tab. -TIPS -~~~~ -For now only French and English report are supported. -For adding new support you just need to add your header in -model/account_bank_statement_import_paypal.py in the variables HEADERS. -Please help us and do a PR for adding new header ! Thanks +* Now you can import Paypal statements in that journal. + +Note: if existent Paypal Map does not fit to your file to import, you can +create another map in **Invoicing > Configuration > Accounting > Paypal +Mapping**. + +You can import headers from any Paypal file in **Action > Create Paypal Map +Lines** and set every line with which field of statement have to match. diff --git a/account_bank_statement_import_paypal/readme/DESCRIPTION.rst b/account_bank_statement_import_paypal/readme/DESCRIPTION.rst index 4da61e3..492b95d 100644 --- a/account_bank_statement_import_paypal/readme/DESCRIPTION.rst +++ b/account_bank_statement_import_paypal/readme/DESCRIPTION.rst @@ -1 +1,2 @@ -This module allows you to import the Paypal CSV files in Odoo as bank statements. +This module allows you to import the Paypal CSV files in Odoo as bank +statements. diff --git a/account_bank_statement_import_paypal/security/ir.model.access.csv b/account_bank_statement_import_paypal/security/ir.model.access.csv new file mode 100644 index 0000000..db61ffb --- /dev/null +++ b/account_bank_statement_import_paypal/security/ir.model.access.csv @@ -0,0 +1,3 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +access_account_bank_statement_import_paypal_map,Paypal map manager,model_account_bank_statement_import_paypal_map,account.group_account_manager,1,1,1,1 +access_account_bank_statement_import_paypal_map_line,Paypal map line manager,model_account_bank_statement_import_paypal_map_line,account.group_account_manager,1,1,1,1 diff --git a/account_bank_statement_import_paypal/static/description/index.html b/account_bank_statement_import_paypal/static/description/index.html index 5c4b767..e3553e1 100644 --- a/account_bank_statement_import_paypal/static/description/index.html +++ b/account_bank_statement_import_paypal/static/description/index.html @@ -368,41 +368,38 @@ ul.auto-toc { !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/bank-statement-import Translate me on Weblate Try me on Runbot

-

This module allows you to import the Paypal CSV files in Odoo as bank statements.

+

This module allows you to import the Paypal CSV files in Odoo as bank +statements.

Table of contents

Configuration

-

In the menu Accounting > Configuration > Accounting > Bank Accounts, -make sure that you have your Paypal bank account with the following parameters: -* Bank Account Type: Normal Bank Account -* Account Number: the email address associated with your Paypal account -* Account Journal: the journal associated to your Paypal account

-
-

TIPS

-

For now only French and English report are supported. -For adding new support you just need to add your header in -model/account_bank_statement_import_paypal.py in the variables HEADERS. -Please help us and do a PR for adding new header ! Thanks

-
+
    +
  • Create or go to a bank journal where you want to import Paypal statement.
  • +
  • Edit that journal and set a Paypal map in Paypal Map section in Advanced +Settings tab.
  • +
  • Now you can import Paypal statements in that journal.
  • +
+

Note: if existent Paypal Map does not fit to your file to import, you can +create another map in Invoicing > Configuration > Accounting > Paypal +Mapping.

+

You can import headers from any Paypal file in Action > Create Paypal Map +Lines and set every line with which field of statement have to match.

-

Usage

+

Usage

To use this module, you need to:

  1. Go to Paypal and download your Bank Statement
  2. @@ -411,7 +408,7 @@ Please help us and do a PR for adding new header ! Thanks

    .
-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed @@ -419,15 +416,15 @@ If you spotted it first, help us smashing it by providing a detailed and welcome

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Akretion
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association

OCA, or the Odoo Community Association, is a nonprofit organization whose diff --git a/account_bank_statement_import_paypal/tests/__init__.py b/account_bank_statement_import_paypal/tests/__init__.py new file mode 100644 index 0000000..8160886 --- /dev/null +++ b/account_bank_statement_import_paypal/tests/__init__.py @@ -0,0 +1 @@ +from . import test_paypal_statement_import diff --git a/account_bank_statement_import_paypal/tests/paypal_en.csv b/account_bank_statement_import_paypal/tests/paypal_en.csv new file mode 100644 index 0000000..f3ef282 --- /dev/null +++ b/account_bank_statement_import_paypal/tests/paypal_en.csv @@ -0,0 +1,3 @@ +"Date","Time","Time Zone","Description","Currency","Gross","Fee ","Net","Balance","Transaction ID","From Email Address","Name","Bank Name","Bank Account","Shipping and Handling Amount","Sales Tax","Invoice ID","Reference Txn ID" +"12/15/2018","20:07:53","CET","Your best supplier","USD","-33,50","-2,3","-31,2","-31,2","53820712527632627","","John Doe","Bank of America","123456789","0","0","INV25","23" +"12/15/2018","22:07:53","CET","Your payment","USD","525","0","525","493,80","34731322767782103","","Agrolait","","","0","0","INV/2019/0003","24" diff --git a/account_bank_statement_import_paypal/tests/test_paypal_statement_import.py b/account_bank_statement_import_paypal/tests/test_paypal_statement_import.py new file mode 100644 index 0000000..7b24197 --- /dev/null +++ b/account_bank_statement_import_paypal/tests/test_paypal_statement_import.py @@ -0,0 +1,54 @@ +# Copyright 2019 Tecnativa - Vicent Cubells +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import os +import base64 +from odoo.tests import common + + +class TestPaypalFile(common.SavepointCase): + @classmethod + def setUpClass(cls): + super(TestPaypalFile, cls).setUpClass() + + cls.map = cls.env['account.bank.statement.import.paypal.map'].create({ + 'name': 'Paypal Map Test', + }) + cls.journal = cls.env['account.journal'].create({ + 'name': 'Paypal Bank', + 'type': 'bank', + 'code': 'PYPAL', + }) + + def _do_import(self, file_name): + file_name = os.path.join(os.path.dirname(__file__), file_name) + return open(file_name).read() + + def test_import_header(self): + file = self._do_import('paypal_en.csv') + file = base64.b64encode(file.encode("utf-8")) + wizard = self.env['wizard.paypal.map.create'].with_context({ + 'journal_id': self.journal.id, + 'active_ids': [self.map.id], + }).create({'data_file': file}) + wizard.create_map_lines() + self.assertEqual(len(self.map.map_line_ids.ids), 18) + + def test_import_paypal_file(self): + # Current statements before to run the wizard + old_statements = self.env['account.bank.statement'].search([]) + # This journal is for Paypal statements + map = self.env.ref('account_bank_statement_import_paypal.paypal_map') + self.journal.paypal_map_id = map.id + file = self._do_import('paypal_en.csv') + file = base64.b64encode(file.encode("utf-8")) + wizard = self.env['account.bank.statement.import'].with_context({ + 'journal_id': self.journal.id, + }).create({'data_file': file}) + wizard.import_file() + staments_now = self.env['account.bank.statement'].search([]) + statement = staments_now - old_statements + self.assertEqual(len(statement.line_ids), 3) + self.assertEqual(len(statement.mapped('line_ids').filtered( + lambda x: x.partner_id and x.account_id)), 1) + self.assertAlmostEqual(sum(statement.mapped('line_ids.amount')), 489.2) diff --git a/account_bank_statement_import_paypal/views/account_journal_views.xml b/account_bank_statement_import_paypal/views/account_journal_views.xml new file mode 100644 index 0000000..3e565b7 --- /dev/null +++ b/account_bank_statement_import_paypal/views/account_journal_views.xml @@ -0,0 +1,16 @@ + + + + + account.journal + + + + + + + + + + + diff --git a/account_bank_statement_import_paypal/views/paypal_map_views.xml b/account_bank_statement_import_paypal/views/paypal_map_views.xml new file mode 100644 index 0000000..c3a40ba --- /dev/null +++ b/account_bank_statement_import_paypal/views/paypal_map_views.xml @@ -0,0 +1,65 @@ + + + + + account.bank.statement.import.paypal.map + + + + + + + + + account.bank.statement.import.paypal.map + +

+ + + + + + + + + + + account.bank.statement.import.paypal.map.line + + + + + + + + + + + + account.bank.statement.import.paypal.map.line + +
+ + + + + + + +
+
+
+ + + Paypal Mapping + account.bank.statement.import.paypal.map + form + tree,form + + + + + diff --git a/account_bank_statement_import_paypal/wizards/__init__.py b/account_bank_statement_import_paypal/wizards/__init__.py new file mode 100644 index 0000000..006d24c --- /dev/null +++ b/account_bank_statement_import_paypal/wizards/__init__.py @@ -0,0 +1,2 @@ +from . import create_map_lines_from_file +from . import account_bank_statement_import_paypal diff --git a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py b/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py similarity index 83% rename from account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py rename to account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py index e15d15f..be82f8c 100644 --- a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal.py +++ b/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py @@ -37,6 +37,12 @@ HEADERS = [ class AccountBankStatementImport(models.TransientModel): _inherit = 'account.bank.statement.import' + paypal_map_id = fields.Many2one( + comodel_name='account.bank.statement.import.paypal.map', + string='Paypal map', + readonly=True, + ) + @api.model def _get_paypal_encoding(self): return 'utf-8-sig' @@ -47,11 +53,6 @@ class AccountBankStatementImport(models.TransientModel): data_file = data_file.decode(self._get_paypal_encoding()) return data_file.strip() - @api.model - def _get_paypal_date_format(self): - """ This method is designed to be inherited """ - return '%d/%m/%Y' - @api.model def _paypal_convert_amount(self, amount_str): """ This method is designed to be inherited """ @@ -63,31 +64,33 @@ class AccountBankStatementImport(models.TransientModel): @api.model def _check_paypal(self, data_file): data_file = self._get_paypal_str_data(data_file) - for header in HEADERS: - if data_file.strip().startswith(header): - return True - return False + if not self.paypal_map_id: + return False + headers = self.mapped('paypal_map_id.map_line_ids.name') + file_headers = data_file.split('\n', 1)[0] + if any(item not in file_headers for item in headers): + raise UserError( + _("Headers of file to import and Paypal map lines does not " + "match.")) + return True def _convert_paypal_line_to_dict(self, idx, line): - date_dt = datetime.strptime(line[0], self._get_paypal_date_format()) - rline = { - 'date': fields.Date.to_string(date_dt), - 'time': line[1], - 'description': line[3], - 'currency': line[4], - 'amount': line[5], - 'commission': line[6], - 'balance': line[8], - 'transaction_id': line[9], - 'email': line[10], - 'partner_name': line[11], - # This two field are useful for bank transfer - 'bank_name': line[12], - 'bank_account': line[13], - 'invoice_number': line[16], - 'origin_transaction_id': line[17], - 'idx': idx, - } + rline = dict() + for item in range(len(line)): + map = self.mapped('paypal_map_id.map_line_ids')[item] + value = line[item] + if not map.field_to_assign: + continue + if map.date_format: + try: + value = fields.Date.to_string( + datetime.strptime(value, map.date_format)) + except Exception: + raise UserError( + _("Date format of map file and Paypal date does " + "not match.")) + rline[map.field_to_assign] = value + for field in ['commission', 'amount', 'balance']: _logger.debug('Trying to convert %s to float' % rline[field]) try: @@ -128,11 +131,14 @@ class AccountBankStatementImport(models.TransientModel): 'transaction_id': cline['transaction_id'], } - def _post_process_statement_line(self, raw_lines): + def _get_journal(self): journal_id = self.env.context.get('journal_id') if not journal_id: raise UserError(_('You must run this wizard from the journal')) - journal = self.env['account.journal'].browse(journal_id) + return self.env['account.journal'].browse(journal_id) + + def _post_process_statement_line(self, raw_lines): + journal = self._get_journal() currency = journal.currency_id or journal.company_id.currency_id currency_change_lines = {} real_transactions = [] @@ -265,17 +271,24 @@ class AccountBankStatementImport(models.TransientModel): if partner: return { 'partner_id': partner.id, - 'account_id': partner.property_account_receivable.id, + 'account_id': partner.property_account_receivable_id.id, } return None @api.model - def _complete_statement(self, stmts_vals, journal_id, account_number): + def _complete_stmts_vals(self, stmts_vals, journal_id, account_number): """ Match the partner from paypal information """ - stmts_vals = super(AccountBankStatementImport, self).\ - _complete_statement(stmts_vals, journal_id, account_number) - for line in stmts_vals['transactions']: + stmts_vals = super(AccountBankStatementImport, self). \ + _complete_stmts_vals(stmts_vals, journal_id, account_number) + for line in stmts_vals[0]['transactions']: vals = self._complete_paypal_statement_line(line) if vals: line.update(vals) return stmts_vals + + @api.model + def default_get(self, fields): + res = super(AccountBankStatementImport, self).default_get(fields) + journal = self._get_journal() + res['paypal_map_id'] = journal.paypal_map_id.id + return res diff --git a/account_bank_statement_import_paypal/wizards/account_bank_statement_import_view.xml b/account_bank_statement_import_paypal/wizards/account_bank_statement_import_view.xml new file mode 100644 index 0000000..ecb7364 --- /dev/null +++ b/account_bank_statement_import_paypal/wizards/account_bank_statement_import_view.xml @@ -0,0 +1,14 @@ + + + + + account.bank.statement.import + + + +
  • Paypal with Template:
  • +
    +
    +
    + +
    diff --git a/account_bank_statement_import_paypal/wizards/create_map_lines_from_file.py b/account_bank_statement_import_paypal/wizards/create_map_lines_from_file.py new file mode 100644 index 0000000..139ae53 --- /dev/null +++ b/account_bank_statement_import_paypal/wizards/create_map_lines_from_file.py @@ -0,0 +1,38 @@ +# Copyright 2019 Tecnativa - Vicent Cubells +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import csv +import base64 +from odoo import api, fields, models +from io import StringIO + + +class WizardPaypalMapCreate(models.TransientModel): + _name = 'wizard.paypal.map.create' + + data_file = fields.Binary( + string='Bank Statement File', + required=True, + ) + filename = fields.Char() + + @api.multi + def create_map_lines(self): + statement_obj = self.env['account.bank.statement.import.paypal.map'] + data_file = base64.b64decode(self.data_file) + if not isinstance(data_file, str): + data_file = data_file.decode('utf-8-sig').strip() + file = StringIO(data_file) + file.seek(0) + reader = csv.reader(file) + headers = [] + for row in reader: + headers = row + break + lines = [] + for idx, title in enumerate(headers): + lines.append((0, 0, {'sequence': idx, 'name': title})) + if lines: + for statement in statement_obj.browse( + self.env.context.get('active_ids')): + statement.map_line_ids = lines diff --git a/account_bank_statement_import_paypal/wizards/create_map_lines_from_file_views.xml b/account_bank_statement_import_paypal/wizards/create_map_lines_from_file_views.xml new file mode 100644 index 0000000..c61136d --- /dev/null +++ b/account_bank_statement_import_paypal/wizards/create_map_lines_from_file_views.xml @@ -0,0 +1,29 @@ + + + + + Create Paypal Map Lines + wizard.paypal.map.create + +
    +

    Select a Paypal bank statement file to create all the map lines from headers file.

    +

    Download a bank statement from your bank and import it here.

    +

    All the Paypal map lines will be created automatically.

    + + +
    +
    + +
    +
    + + +
    From f380ca70acbdffb1939ed6d2d860619797f2b79f Mon Sep 17 00:00:00 2001 From: oca-travis Date: Fri, 12 Apr 2019 15:23:09 +0000 Subject: [PATCH 10/13] [UPD] Update account_bank_statement_import_paypal.pot --- .../i18n/account_bank_statement_import_paypal.pot | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot b/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot index 3e24e19..21eaa16 100644 --- a/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot +++ b/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot @@ -6,8 +6,6 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 11.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-01-11 10:43+0000\n" -"PO-Revision-Date: 2019-01-11 10:43+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -255,7 +253,7 @@ msgid "Paypal commissions" msgstr "" #. module: account_bank_statement_import_paypal -#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_id_4185 +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_id_4207 msgid "Paypal map" msgstr "" From fef7459adc0cdd9a4973b267b9790c42952aeeaf Mon Sep 17 00:00:00 2001 From: cubells Date: Fri, 11 Jan 2019 11:48:33 +0100 Subject: [PATCH 11/13] [IMP] account_bank_statement_import_paypal: create paypal maps --- .../i18n/account_bank_statement_import_paypal.pot | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot b/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot index 21eaa16..3e24e19 100644 --- a/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot +++ b/account_bank_statement_import_paypal/i18n/account_bank_statement_import_paypal.pot @@ -6,6 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 11.0\n" "Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-01-11 10:43+0000\n" +"PO-Revision-Date: 2019-01-11 10:43+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -253,7 +255,7 @@ msgid "Paypal commissions" msgstr "" #. module: account_bank_statement_import_paypal -#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_id_4207 +#: model:ir.model.fields,field_description:account_bank_statement_import_paypal.field_account_bank_statement_import_paypal_map_id_4185 msgid "Paypal map" msgstr "" From f951bb81c54840489521c40d7bcab662602898d5 Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Mon, 13 May 2019 15:17:38 +0200 Subject: [PATCH 12/13] [FIX] float parsing in paypal import In English version of the files, paypal uses ',' as thousands separator and '.' as decimal separator -> the previous code would populate the bank statement with wrong values without complaining. We fix this by allowing to configure the separators in the paypal map. --- .../data/paypal_map_data.xml | 4 +- ...ccount_bank_statement_import_paypal_map.py | 46 ++++++++++++++++++- .../tests/paypal_en.csv | 4 +- .../tests/test_paypal_statement_import.py | 10 ++-- .../views/paypal_map_views.xml | 2 + .../account_bank_statement_import_paypal.py | 21 +++++---- 6 files changed, 71 insertions(+), 16 deletions(-) diff --git a/account_bank_statement_import_paypal/data/paypal_map_data.xml b/account_bank_statement_import_paypal/data/paypal_map_data.xml index be289c7..0eeef9b 100644 --- a/account_bank_statement_import_paypal/data/paypal_map_data.xml +++ b/account_bank_statement_import_paypal/data/paypal_map_data.xml @@ -2,7 +2,9 @@ - Paypal Monthly Statement + Paypal Monthly Statement + comma + dot diff --git a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py index 159dfd4..cd848db 100644 --- a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py +++ b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py @@ -1,7 +1,7 @@ # Copyright 2019 Tecnativa - Vicent Cubells # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import fields, models, api class AccountBankStatementImportPaypalMap(models.Model): @@ -17,6 +17,50 @@ class AccountBankStatementImportPaypalMap(models.Model): required=True, copy=True, ) + float_thousands_sep = fields.Selection( + [('dot', 'dot (.)'), + ('comma', 'comma (,)'), + ('none', 'none'), + ], + string='Thousands separator', + # forward compatibility: this was the value assumed + # before the field was added. + default='dot', + required=True + ) + float_decimal_sep = fields.Selection( + [('dot', 'dot (.)'), + ('comma', 'comma (,)'), + ('none', 'none'), + ], + string='Decimals separator', + # forward compatibility: this was the value assumed + # before the field was added. + default='comma', + required=True + ) + + @api.onchange('float_thousands_sep') + def onchange_thousands_separator(self): + if 'dot' == self.float_thousands_sep == self.float_decimal_sep: + self.float_decimal_sep = 'comma' + elif 'comma' == self.float_thousands_sep == self.float_decimal_sep: + self.float_decimal_sep = 'dot' + + @api.onchange('float_decimal_sep') + def onchange_decimal_separator(self): + if 'dot' == self.float_thousands_sep == self.float_decimal_sep: + self.float_thousands_sep = 'comma' + elif 'comma' == self.float_thousands_sep == self.float_decimal_sep: + self.float_thousands_sep = 'dot' + + def _get_separators(self): + separators = {'dot': '.', + 'comma': ',', + 'none': '', + } + return (separators[self.float_thousands_sep], + separators[self.float_decimal_sep]) class AccountBankStatementImportPaypalMapLIne(models.Model): diff --git a/account_bank_statement_import_paypal/tests/paypal_en.csv b/account_bank_statement_import_paypal/tests/paypal_en.csv index f3ef282..1bef2f8 100644 --- a/account_bank_statement_import_paypal/tests/paypal_en.csv +++ b/account_bank_statement_import_paypal/tests/paypal_en.csv @@ -1,3 +1,3 @@ "Date","Time","Time Zone","Description","Currency","Gross","Fee ","Net","Balance","Transaction ID","From Email Address","Name","Bank Name","Bank Account","Shipping and Handling Amount","Sales Tax","Invoice ID","Reference Txn ID" -"12/15/2018","20:07:53","CET","Your best supplier","USD","-33,50","-2,3","-31,2","-31,2","53820712527632627","","John Doe","Bank of America","123456789","0","0","INV25","23" -"12/15/2018","22:07:53","CET","Your payment","USD","525","0","525","493,80","34731322767782103","","Agrolait","","","0","0","INV/2019/0003","24" +"12/15/2018","20:07:53","CET","Your best supplier","USD","-33.50","-2.3","-31.2","-31.2","53820712527632627","","John Doe","Bank of America","123456789","0","0","INV25","23" +"12/15/2018","22:07:53","CET","Your payment","USD","1,525.00","0","1,525.00","1,493.80","34731322767782103","","Agrolait","","","0","0","INV/2019/0003","24" diff --git a/account_bank_statement_import_paypal/tests/test_paypal_statement_import.py b/account_bank_statement_import_paypal/tests/test_paypal_statement_import.py index 7b24197..8bd106f 100644 --- a/account_bank_statement_import_paypal/tests/test_paypal_statement_import.py +++ b/account_bank_statement_import_paypal/tests/test_paypal_statement_import.py @@ -38,8 +38,10 @@ class TestPaypalFile(common.SavepointCase): # Current statements before to run the wizard old_statements = self.env['account.bank.statement'].search([]) # This journal is for Paypal statements - map = self.env.ref('account_bank_statement_import_paypal.paypal_map') - self.journal.paypal_map_id = map.id + paypal_map = self.env.ref( + 'account_bank_statement_import_paypal.paypal_map' + ) + self.journal.paypal_map_id = paypal_map.id file = self._do_import('paypal_en.csv') file = base64.b64encode(file.encode("utf-8")) wizard = self.env['account.bank.statement.import'].with_context({ @@ -51,4 +53,6 @@ class TestPaypalFile(common.SavepointCase): self.assertEqual(len(statement.line_ids), 3) self.assertEqual(len(statement.mapped('line_ids').filtered( lambda x: x.partner_id and x.account_id)), 1) - self.assertAlmostEqual(sum(statement.mapped('line_ids.amount')), 489.2) + self.assertAlmostEqual( + sum(statement.mapped('line_ids.amount')), 1489.2 + ) diff --git a/account_bank_statement_import_paypal/views/paypal_map_views.xml b/account_bank_statement_import_paypal/views/paypal_map_views.xml index c3a40ba..1fee4a6 100644 --- a/account_bank_statement_import_paypal/views/paypal_map_views.xml +++ b/account_bank_statement_import_paypal/views/paypal_map_views.xml @@ -16,6 +16,8 @@
    + + diff --git a/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py b/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py index be82f8c..ba79b53 100644 --- a/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py +++ b/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py @@ -55,10 +55,13 @@ class AccountBankStatementImport(models.TransientModel): @api.model def _paypal_convert_amount(self, amount_str): - """ This method is designed to be inherited """ - valstr = re.sub(r'[^\d,.-]', '', amount_str) - valstrdot = valstr.replace('.', '') - valstrdot = valstrdot.replace(',', '.') + if self.paypal_map_id: + thousands, decimal = self.paypal_map_id._get_separators() + else: + thousands, decimal = ',', '.' + valstr = re.sub(r'[^\d%s%s.-]' % (thousands, decimal), '', amount_str) + valstrdot = valstr.replace(thousands, '') + valstrdot = valstrdot.replace(decimal, '.') return float(valstrdot) @api.model @@ -77,19 +80,19 @@ class AccountBankStatementImport(models.TransientModel): def _convert_paypal_line_to_dict(self, idx, line): rline = dict() for item in range(len(line)): - map = self.mapped('paypal_map_id.map_line_ids')[item] + paypal_map = self.mapped('paypal_map_id.map_line_ids')[item] value = line[item] - if not map.field_to_assign: + if not paypal_map.field_to_assign: continue - if map.date_format: + if paypal_map.date_format: try: value = fields.Date.to_string( - datetime.strptime(value, map.date_format)) + datetime.strptime(value, paypal_map.date_format)) except Exception: raise UserError( _("Date format of map file and Paypal date does " "not match.")) - rline[map.field_to_assign] = value + rline[paypal_map.field_to_assign] = value for field in ['commission', 'amount', 'balance']: _logger.debug('Trying to convert %s to float' % rline[field]) From 4f3cc1a8f2d151a5cd92c0b3f2c6ba3d0f82a3ec Mon Sep 17 00:00:00 2001 From: Victor Martin Date: Mon, 20 May 2019 15:24:37 +0200 Subject: [PATCH 13/13] [MIG] account_bank_statement_import_paypal: Migration to 12.0 --- account_bank_statement_import_paypal/README.rst | 15 ++++++++------- .../__manifest__.py | 2 +- .../account_bank_statement_import_paypal_map.py | 4 +++- .../readme/CONTRIBUTORS.rst | 1 + .../static/description/index.html | 11 ++++++----- .../wizards/create_map_lines_from_file.py | 1 + 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/account_bank_statement_import_paypal/README.rst b/account_bank_statement_import_paypal/README.rst index 0fa92e3..f14ab9d 100644 --- a/account_bank_statement_import_paypal/README.rst +++ b/account_bank_statement_import_paypal/README.rst @@ -14,13 +14,13 @@ Import Paypal Bank Statements :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fbank--statement--import-lightgray.png?logo=github - :target: https://github.com/OCA/bank-statement-import/tree/11.0/account_bank_statement_import_paypal + :target: https://github.com/OCA/bank-statement-import/tree/12.0/account_bank_statement_import_paypal :alt: OCA/bank-statement-import .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/bank-statement-import-11-0/bank-statement-import-11-0-account_bank_statement_import_paypal + :target: https://translation.odoo-community.org/projects/bank-statement-import-12-0/bank-statement-import-12-0-account_bank_statement_import_paypal :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/174/11.0 + :target: https://runbot.odoo-community.org/runbot/174/12.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -56,9 +56,9 @@ To use this module, you need to: #. Go to Paypal and download your Bank Statement -.. image:: https://raw.githubusercontent.com/OCA/bank-statement-import/11.0/account_bank_statement_import_paypal/account_bank_statement_import_paypal/static/description/paypal_backoffice.png +.. image:: https://raw.githubusercontent.com/OCA/bank-statement-import/12.0/account_bank_statement_import_paypal/account_bank_statement_import_paypal/static/description/paypal_backoffice.png :alt: . -.. image:: https://raw.githubusercontent.com/OCA/bank-statement-import/11.0/account_bank_statement_import_paypal/static/description/paypal_backoffice.png +.. image:: https://raw.githubusercontent.com/OCA/bank-statement-import/12.0/account_bank_statement_import_paypal/static/description/paypal_backoffice.png :alt: . Bug Tracker @@ -67,7 +67,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -87,6 +87,7 @@ Contributors * Tecnativa (https://www.tecnativa.com) * Vicent Cubells + * Victor M.M. Torres Maintainers ~~~~~~~~~~~ @@ -101,6 +102,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/bank-statement-import `_ project on GitHub. +This module is part of the `OCA/bank-statement-import `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_bank_statement_import_paypal/__manifest__.py b/account_bank_statement_import_paypal/__manifest__.py index f4af623..701d6bf 100644 --- a/account_bank_statement_import_paypal/__manifest__.py +++ b/account_bank_statement_import_paypal/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Import Paypal Bank Statements", 'summary': 'Import Paypal CSV files as Bank Statements in Odoo', - "version": "11.0.2.0.0", + "version": "12.0.1.0.0", "category": "Accounting", "website": "https://github.com/OCA/bank-statement-import", "author": " Akretion, Odoo Community Association (OCA)", diff --git a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py index cd848db..06c82ef 100644 --- a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py +++ b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py @@ -6,6 +6,7 @@ from odoo import fields, models, api class AccountBankStatementImportPaypalMap(models.Model): _name = 'account.bank.statement.import.paypal.map' + _description = 'Account Bank Statement Import Paypal Map' name = fields.Char( required=True, @@ -63,8 +64,9 @@ class AccountBankStatementImportPaypalMap(models.Model): separators[self.float_decimal_sep]) -class AccountBankStatementImportPaypalMapLIne(models.Model): +class AccountBankStatementImportPaypalMapLine(models.Model): _name = 'account.bank.statement.import.paypal.map.line' + _description = 'Account Bank Statement Import Paypal Map Line' _order = "sequence asc, id asc" sequence = fields.Integer( diff --git a/account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst b/account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst index 480d383..6e9a18d 100644 --- a/account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst +++ b/account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst @@ -3,3 +3,4 @@ * Tecnativa (https://www.tecnativa.com) * Vicent Cubells + * Victor M.M. Torres diff --git a/account_bank_statement_import_paypal/static/description/index.html b/account_bank_statement_import_paypal/static/description/index.html index e3553e1..779071f 100644 --- a/account_bank_statement_import_paypal/static/description/index.html +++ b/account_bank_statement_import_paypal/static/description/index.html @@ -367,7 +367,7 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

    Beta License: AGPL-3 OCA/bank-statement-import Translate me on Weblate Try me on Runbot

    +

    Beta License: AGPL-3 OCA/bank-statement-import Translate me on Weblate Try me on Runbot

    This module allows you to import the Paypal CSV files in Odoo as bank statements.

    Table of contents

    @@ -404,15 +404,15 @@ Lines and set every line with which field of statement have to match.
  • Go to Paypal and download your Bank Statement
  • -. -. +. +.

    Bug Tracker

    Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

    +feedback.

    Do not contact contributors directly about support or help with technical issues.

    @@ -430,6 +430,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
  • Sebastien BEAU <sebastien.beau@akretion.com>
  • Tecnativa (https://www.tecnativa.com)
  • @@ -441,7 +442,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome

    OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

    -

    This module is part of the OCA/bank-statement-import project on GitHub.

    +

    This module is part of the OCA/bank-statement-import project on GitHub.

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    diff --git a/account_bank_statement_import_paypal/wizards/create_map_lines_from_file.py b/account_bank_statement_import_paypal/wizards/create_map_lines_from_file.py index 139ae53..a9ca019 100644 --- a/account_bank_statement_import_paypal/wizards/create_map_lines_from_file.py +++ b/account_bank_statement_import_paypal/wizards/create_map_lines_from_file.py @@ -9,6 +9,7 @@ from io import StringIO class WizardPaypalMapCreate(models.TransientModel): _name = 'wizard.paypal.map.create' + _description = 'Wizard Paypal Map Create' data_file = fields.Binary( string='Bank Statement File',
    +

    Import Paypal Bank Statements

    + + +

    Beta License: AGPL-3 OCA/bank-statement-import Translate me on Weblate Try me on Runbot

    +

    This module allows you to import the Paypal CSV files in Odoo as bank statements.

    +

    Table of contents

    + +
    +

    Configuration

    +

    In the menu Accounting > Configuration > Accounting > Bank Accounts, +make sure that you have your Paypal bank account with the following parameters: +* Bank Account Type: Normal Bank Account +* Account Number: the email address associated with your Paypal account +* Account Journal: the journal associated to your Paypal account

    +
    +

    TIPS

    +

    For now only French and English report are supported. +For adding new support you just need to add your header in +model/account_bank_statement_import_paypal.py in the variables HEADERS. +Please help us and do a PR for adding new header ! Thanks

    +
    +
    +
    +

    Usage

    +

    To use this module, you need to:

    +
      +
    1. Go to Paypal and download your Bank Statement
    2. +
    +. +. +
    +
    +

    Bug Tracker

    +

    Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

    +

    Do not contact contributors directly about support or help with technical issues.

    +
    +
    +

    Credits

    +
    +

    Authors

    +
      +
    • Akretion
    • +
    +
    +
    +

    Contributors

    + +
    +
    +

    Maintainers

    +

    This module is maintained by the OCA.

    +Odoo Community Association +

    OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

    +

    This module is part of the OCA/bank-statement-import project on GitHub.

    +

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    +
    +
    +