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]