From 7011bc5e602dac17b0a3f14f8875c6934efdbdd4 Mon Sep 17 00:00:00 2001 From: Alexey Pelykh Date: Sun, 10 Nov 2019 12:09:25 +0000 Subject: [PATCH] [IMP] account_bank_statement_import_paypal --- .../README.rst | 2 +- .../__init__.py | 2 + .../__manifest__.py | 51 +-- .../data/maps.xml | 54 ++++ .../data/paypal_map_data.xml | 116 ------- .../migrations/12.0.2.0.0/post-migration.py | 103 ++++++ .../models/__init__.py | 6 +- .../models/account_bank_statement_import.py | 35 ++ ...ccount_bank_statement_import_paypal_map.py | 110 ------- ...nt_bank_statement_import_paypal_mapping.py | 130 ++++++++ ...unt_bank_statement_import_paypal_parser.py | 265 ++++++++++++++++ .../models/account_journal.py | 14 +- .../readme/CONFIGURE.rst | 14 +- .../readme/CONTRIBUTORS.rst | 2 + .../readme/DESCRIPTION.rst | 2 +- .../readme/HISTORY.rst | 7 + .../readme/USAGE.rst | 23 +- .../security/ir.model.access.csv | 4 +- .../static/description/index.html | 2 +- .../static/description/paypal_backoffice.png | Bin 97703 -> 0 bytes .../tests/__init__.py | 4 +- .../tests/fixtures/activity_en.csv | 77 +++++ .../tests/fixtures/empty_activity.csv | 1 + .../tests/fixtures/statement_en.csv | 20 ++ .../tests/paypal_en.csv | 3 - ...st_account_bank_statement_import_paypal.py | 132 ++++++++ .../tests/test_paypal_statement_import.py | 62 ---- .../account_bank_statement_import.xml} | 11 +- ...t_bank_statement_import_paypal_mapping.xml | 78 +++++ .../views/account_journal_views.xml | 16 - .../views/paypal_map_views.xml | 67 ---- .../wizards/__init__.py | 5 +- .../account_bank_statement_import_paypal.py | 300 ------------------ ..._statement_import_paypal_mapping_wizard.py | 84 +++++ ...statement_import_paypal_mapping_wizard.xml | 34 ++ .../wizards/create_map_lines_from_file.py | 39 --- .../create_map_lines_from_file_views.xml | 29 -- 37 files changed, 1104 insertions(+), 800 deletions(-) create mode 100644 account_bank_statement_import_paypal/data/maps.xml delete mode 100644 account_bank_statement_import_paypal/data/paypal_map_data.xml create mode 100644 account_bank_statement_import_paypal/migrations/12.0.2.0.0/post-migration.py create mode 100644 account_bank_statement_import_paypal/models/account_bank_statement_import.py delete 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_bank_statement_import_paypal_mapping.py create mode 100644 account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_parser.py create mode 100644 account_bank_statement_import_paypal/readme/HISTORY.rst delete mode 100644 account_bank_statement_import_paypal/static/description/paypal_backoffice.png create mode 100644 account_bank_statement_import_paypal/tests/fixtures/activity_en.csv create mode 100644 account_bank_statement_import_paypal/tests/fixtures/empty_activity.csv create mode 100644 account_bank_statement_import_paypal/tests/fixtures/statement_en.csv delete mode 100644 account_bank_statement_import_paypal/tests/paypal_en.csv create mode 100644 account_bank_statement_import_paypal/tests/test_account_bank_statement_import_paypal.py delete mode 100644 account_bank_statement_import_paypal/tests/test_paypal_statement_import.py rename account_bank_statement_import_paypal/{wizards/account_bank_statement_import_view.xml => views/account_bank_statement_import.xml} (51%) create mode 100644 account_bank_statement_import_paypal/views/account_bank_statement_import_paypal_mapping.xml delete mode 100644 account_bank_statement_import_paypal/views/account_journal_views.xml delete mode 100644 account_bank_statement_import_paypal/views/paypal_map_views.xml delete mode 100644 account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py create mode 100644 account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal_mapping_wizard.py create mode 100644 account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal_mapping_wizard.xml delete mode 100644 account_bank_statement_import_paypal/wizards/create_map_lines_from_file.py delete 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 f14ab9d..0d6f696 100644 --- a/account_bank_statement_import_paypal/README.rst +++ b/account_bank_statement_import_paypal/README.rst @@ -11,7 +11,7 @@ Import Paypal Bank Statements :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 + :target: https://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/12.0/account_bank_statement_import_paypal diff --git a/account_bank_statement_import_paypal/__init__.py b/account_bank_statement_import_paypal/__init__.py index aee8895..adc6207 100644 --- a/account_bank_statement_import_paypal/__init__.py +++ b/account_bank_statement_import_paypal/__init__.py @@ -1,2 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + 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 4322ee1..78e9b00 100644 --- a/account_bank_statement_import_paypal/__manifest__.py +++ b/account_bank_statement_import_paypal/__manifest__.py @@ -1,26 +1,33 @@ # 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). +# Copyright 2019 Tecnativa - Vicent Cubells +# Copyright 2019-2020 Brainbean Apps (https://brainbeanapps.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + { - "name": "Import Paypal Bank Statements", - 'summary': 'Import Paypal CSV files as Bank Statements in Odoo', - "version": "12.0.1.1.0", - "category": "Accounting", - "website": "https://github.com/OCA/bank-statement-import", - "author": " Akretion, Odoo Community Association (OCA)", - "license": "AGPL-3", - "installable": True, - "depends": [ - "account_bank_statement_import", - "sale", + 'name': 'PayPal CSV Format Bank Statements Import', + 'summary': 'Import PayPal CSV files as Bank Statements in Odoo', + 'version': '12.0.2.0.0', + 'category': 'Accounting', + 'website': 'https://github.com/OCA/bank-statement-import', + 'author': + 'Akretion, ' + 'Brainbean Apps, ' + 'Odoo Community Association (OCA)', + 'license': 'AGPL-3', + 'installable': True, + 'depends': [ + 'account_bank_statement_import', + ], + 'external_dependencies': { + 'python': [ + 'csv', + ] + }, + 'data': [ + 'security/ir.model.access.csv', + 'data/maps.xml', + 'views/account_bank_statement_import_paypal_mapping.xml', + 'views/account_bank_statement_import.xml', + 'wizards/account_bank_statement_import_paypal_mapping_wizard.xml', ], - "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/maps.xml b/account_bank_statement_import_paypal/data/maps.xml new file mode 100644 index 0000000..78337e4 --- /dev/null +++ b/account_bank_statement_import_paypal/data/maps.xml @@ -0,0 +1,54 @@ + + + + + + PayPal Statement (EN) + comma + dot + %m/%d/%Y + %H:%M:%S + Date + Time + Time Zone + Name + Currency + Gross + Fee + Balance + Transaction ID + Description + From Email Address + Invoice ID + Bank Name + Bank Account + + + + PayPal Activity (EN) + comma + dot + %d/%m/%Y + %H:%M:%S + Date + Time + TimeZone + Name + Currency + Gross + Fee + Balance + Transaction ID + Type + From Email Address + To Email Address + Invoice ID + Subject + Note + + + diff --git a/account_bank_statement_import_paypal/data/paypal_map_data.xml b/account_bank_statement_import_paypal/data/paypal_map_data.xml deleted file mode 100644 index 0eeef9b..0000000 --- a/account_bank_statement_import_paypal/data/paypal_map_data.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - - Paypal Monthly Statement - comma - dot - - - - 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/migrations/12.0.2.0.0/post-migration.py b/account_bank_statement_import_paypal/migrations/12.0.2.0.0/post-migration.py new file mode 100644 index 0000000..e215572 --- /dev/null +++ b/account_bank_statement_import_paypal/migrations/12.0.2.0.0/post-migration.py @@ -0,0 +1,103 @@ +# Copyright 2020 Brainbean Apps (https://brainbeanapps.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.logged_query( + env.cr, + """ +WITH _mappings AS ( + SELECT + m.id, + l.field_to_assign, + l.name, + l.date_format + FROM + account_bank_statement_import_paypal_map AS m + RIGHT OUTER JOIN ( + SELECT + *, + ROW_NUMBER() OVER ( + PARTITION BY map_parent_id, field_to_assign + ORDER BY id ASC + ) AS row_number + FROM account_bank_statement_import_paypal_map_line + WHERE field_to_assign IS NOT NULL + ) AS l ON m.id = l.map_parent_id AND l.row_number = 1 +) + +INSERT INTO account_bank_statement_import_paypal_mapping ( + name, + float_thousands_sep, + float_decimal_sep, + date_format, + time_format, + date_column, + time_column, + tz_column, + name_column, + currency_column, + gross_column, + fee_column, + balance_column, + transaction_id_column, + description_column, + from_email_address_column, + invoice_id_column, + bank_name_column, + bank_account_column +) +SELECT + m.name, + m.float_thousands_sep, + m.float_decimal_sep, + COALESCE(_date.date_format, '%m/%d/%Y') AS date_format, + '%H:%M:%S' AS time_format, + COALESCE(_date.name, 'Date') AS date_column, + COALESCE(_time.name, 'Time') AS time_column, + 'Time Zone' AS tz_column, + COALESCE(_name.name, 'Name') AS name_column, + COALESCE(_currency.name, 'Currency') AS currency_column, + COALESCE(_gross.name, 'Gross') AS gross_column, + COALESCE(_fee.name, 'Fee') AS fee_column, + COALESCE(_balance.name, 'Balance') AS balance_column, + COALESCE(_tid.name, 'Transaction ID') AS transaction_id_column, + COALESCE(_description.name, 'Description') AS description_column, + COALESCE(_from_email.name, 'From Email Address') + AS from_email_address_column, + COALESCE(_invoice.name, 'Invoice ID') AS invoice_id_column, + COALESCE(_bank_name.name, 'Bank Name') AS bank_name_column, + COALESCE(_bank_acc.name, 'Bank Account') AS bank_account_column +FROM + account_bank_statement_import_paypal_map AS m +LEFT JOIN _mappings AS _date + ON m.id = _date.id AND _date.field_to_assign = 'date' +LEFT JOIN _mappings AS _time + ON m.id = _time.id AND _time.field_to_assign = 'time' +LEFT JOIN _mappings AS _name + ON m.id = _name.id AND _name.field_to_assign = 'partner_name' +LEFT JOIN _mappings AS _currency + ON m.id = _currency.id AND _currency.field_to_assign = 'currency' +LEFT JOIN _mappings AS _gross + ON m.id = _gross.id AND _gross.field_to_assign = 'amount' +LEFT JOIN _mappings AS _fee + ON m.id = _fee.id AND _fee.field_to_assign = 'commission' +LEFT JOIN _mappings AS _balance + ON m.id = _balance.id AND _balance.field_to_assign = 'balance' +LEFT JOIN _mappings AS _tid + ON m.id = _tid.id AND _tid.field_to_assign = 'transaction_id' +LEFT JOIN _mappings AS _description + ON m.id = _description.id AND _description.field_to_assign = 'description' +LEFT JOIN _mappings AS _from_email + ON m.id = _from_email.id AND _from_email.field_to_assign = 'email' +LEFT JOIN _mappings AS _invoice + ON m.id = _invoice.id AND _invoice.field_to_assign = 'invoice_number' +LEFT JOIN _mappings AS _bank_name + ON m.id = _bank_name.id AND _bank_name.field_to_assign = 'bank_name' +LEFT JOIN _mappings AS _bank_acc + ON m.id = _bank_acc.id AND _bank_acc.field_to_assign = 'bank_account'; + """ + ) diff --git a/account_bank_statement_import_paypal/models/__init__.py b/account_bank_statement_import_paypal/models/__init__.py index 4257b3b..42211d5 100644 --- a/account_bank_statement_import_paypal/models/__init__.py +++ b/account_bank_statement_import_paypal/models/__init__.py @@ -1,2 +1,6 @@ -from . import account_bank_statement_import_paypal_map +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import account_bank_statement_import_paypal_mapping +from . import account_bank_statement_import_paypal_parser +from . import account_bank_statement_import from . import account_journal diff --git a/account_bank_statement_import_paypal/models/account_bank_statement_import.py b/account_bank_statement_import_paypal/models/account_bank_statement_import.py new file mode 100644 index 0000000..4d3d08c --- /dev/null +++ b/account_bank_statement_import_paypal/models/account_bank_statement_import.py @@ -0,0 +1,35 @@ +# Copyright 2014-2017 Akretion (http://www.akretion.com). +# Copyright 2019 Tecnativa - Vicent Cubells +# Copyright 2019 Brainbean Apps (https://brainbeanapps.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + +import logging +_logger = logging.getLogger(__name__) + + +class AccountBankStatementImport(models.TransientModel): + _inherit = 'account.bank.statement.import' + + paypal_mapping_id = fields.Many2one( + string='PayPal mapping', + comodel_name='account.bank.statement.import.paypal.mapping', + ) + + @api.multi + def _parse_file(self, data_file): + self.ensure_one() + try: + Parser = self.env['account.bank.statement.import.paypal.parser'] + return Parser.parse( + self.paypal_mapping_id, + data_file, + self.filename + ) + except: + if self.env.context.get( + 'account_bank_statement_import_paypal_test'): + raise + _logger.warning('PayPal parser error', exc_info=True) + return super()._parse_file(data_file) 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 deleted file mode 100644 index 06c82ef..0000000 --- a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_map.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright 2019 Tecnativa - Vicent Cubells -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -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, - ) - 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, - ) - 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): - _name = 'account.bank.statement.import.paypal.map.line' - _description = '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_bank_statement_import_paypal_mapping.py b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_mapping.py new file mode 100644 index 0000000..e87ddcf --- /dev/null +++ b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_mapping.py @@ -0,0 +1,130 @@ +# Copyright 2019 Tecnativa - Vicent Cubells +# Copyright 2019 Brainbean Apps (https://brainbeanapps.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class AccountBankStatementImportPayPalMapping(models.Model): + _name = 'account.bank.statement.import.paypal.mapping' + _description = 'Account Bank Statement Import PayPal Mapping' + + name = fields.Char( + required=True, + ) + float_thousands_sep = fields.Selection( + string='Thousands Separator', + selection=[ + ('dot', 'dot (.)'), + ('comma', 'comma (,)'), + ('none', 'none'), + ], + default='dot', + required=True, + ) + float_decimal_sep = fields.Selection( + string='Decimals Separator', + selection=[ + ('dot', 'dot (.)'), + ('comma', 'comma (,)'), + ('none', 'none'), + ], + default='comma', + required=True, + ) + date_format = fields.Char( + string='Date Format', + required=True, + ) + time_format = fields.Char( + string='Time Format', + required=True, + ) + date_column = fields.Char( + string='"Date" column', + required=True, + ) + time_column = fields.Char( + string='"Time" column', + required=True, + ) + tz_column = fields.Char( + string='"Timezone" column', + required=True, + ) + name_column = fields.Char( + string='"Name" column', + required=True, + ) + currency_column = fields.Char( + string='"Currency" column', + required=True, + ) + gross_column = fields.Char( + string='"Gross" column', + required=True, + ) + fee_column = fields.Char( + string='"Fee" column', + required=True, + ) + balance_column = fields.Char( + string='"Balance" column', + required=True, + ) + transaction_id_column = fields.Char( + string='"Transaction ID" column', + required=True, + ) + description_column = fields.Char( + string='"Description" column', + ) + type_column = fields.Char( + string='"Type" column', + ) + from_email_address_column = fields.Char( + string='"From Email Address" column', + ) + to_email_address_column = fields.Char( + string='"To Email Address" column', + ) + invoice_id_column = fields.Char( + string='"Invoice ID" column', + ) + subject_column = fields.Char( + string='"Subject" column', + ) + note_column = fields.Char( + string='"Note" column', + ) + bank_name_column = fields.Char( + string='"Bank Name" column', + ) + bank_account_column = fields.Char( + string='"Bank Account" column', + ) + + @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' + + @api.multi + def _get_float_separators(self): + self.ensure_one() + separators = { + 'dot': '.', + 'comma': ',', + 'none': '', + } + return (separators[self.float_thousands_sep], + separators[self.float_decimal_sep]) diff --git a/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_parser.py b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_parser.py new file mode 100644 index 0000000..cdc9bcc --- /dev/null +++ b/account_bank_statement_import_paypal/models/account_bank_statement_import_paypal_parser.py @@ -0,0 +1,265 @@ +# Copyright 2019 Tecnativa - Vicent Cubells +# Copyright 2019 Brainbean Apps (https://brainbeanapps.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, models, _ + +from datetime import datetime +from decimal import Decimal +from io import StringIO +from os import path +import itertools +from pytz import timezone, utc + +import logging +_logger = logging.getLogger(__name__) + +try: + from csv import reader +except (ImportError, IOError) as err: + _logger.error(err) + + +class AccountBankStatementImportPayPalParser(models.TransientModel): + _name = 'account.bank.statement.import.paypal.parser' + _description = 'Account Bank Statement Import PayPal Parser' + + @api.model + def parse_header(self, data_file): + data = StringIO(data_file.decode('utf-8-sig')) + csv_data = reader(data) + return list(next(csv_data)) + + @api.model + def parse(self, mapping, data_file, filename): + journal = self.env['account.journal'].browse( + self.env.context.get('journal_id') + ) + currency_code = ( + journal.currency_id or journal.company_id.currency_id + ).name + account_number = journal.bank_account_id.acc_number + + name = _('%s: %s') % ( + journal.code, + path.basename(filename), + ) + lines = self._parse_lines(mapping, data_file, currency_code) + if not lines: + return currency_code, account_number, [{ + 'name': name, + 'transactions': [], + }] + + lines = list(sorted( + lines, + key=lambda line: line['timestamp'] + )) + first_line = lines[0] + balance_start = first_line['balance_amount'] + balance_start -= first_line['gross_amount'] + balance_start -= first_line['fee_amount'] + last_line = lines[-1] + balance_end = last_line['balance_amount'] + + transactions = list(itertools.chain.from_iterable(map( + lambda line: self._convert_line_to_transactions(line), + lines + ))) + + return currency_code, account_number, [{ + 'name': name, + 'date': first_line['timestamp'].date(), + 'balance_start': float(balance_start), + 'balance_end_real': float(balance_end), + 'transactions': transactions, + }] + + def _parse_lines(self, mapping, data_file, currency_code): + data = StringIO(data_file.decode('utf-8-sig')) + csv_data = reader(data) + + header = list(next(csv_data)) + date_column = header.index(mapping.date_column) + time_column = header.index(mapping.time_column) + tz_column = header.index(mapping.tz_column) + name_column = header.index(mapping.name_column) + currency_column = header.index(mapping.currency_column) + gross_column = header.index(mapping.gross_column) + fee_column = header.index(mapping.fee_column) + balance_column = header.index(mapping.balance_column) + transaction_id_column = header.index(mapping.transaction_id_column) + try: + description_column = header.index(mapping.description_column) + except ValueError: + description_column = None + try: + type_column = header.index(mapping.type_column) + except ValueError: + type_column = None + try: + from_email_address_column = header.index( + mapping.from_email_address_column + ) + except ValueError: + from_email_address_column = None + try: + to_email_address_column = header.index( + mapping.to_email_address_column + ) + except ValueError: + to_email_address_column = None + try: + invoice_id_column = header.index(mapping.invoice_id_column) + except ValueError: + invoice_id_column = None + try: + subject_column = header.index(mapping.subject_column) + except ValueError: + subject_column = None + try: + note_column = header.index(mapping.note_column) + except ValueError: + note_column = None + try: + bank_name_column = header.index(mapping.bank_name_column) + except ValueError: + bank_name_column = None + try: + bank_account_column = header.index(mapping.bank_account_column) + except ValueError: + bank_account_column = None + + lines = [] + for row in csv_data: + row = list(row) + date_value = row[date_column] + time_value = row[time_column] + tz_value = row[tz_column] + name_value = row[name_column] + currency_value = row[currency_column] + gross_value = row[gross_column] + fee_value = row[fee_column] + balance_value = row[balance_column] + transaction_id_value = row[transaction_id_column] + description_value = row[description_column] \ + if description_column is not None else None + type_value = row[type_column] \ + if type_column is not None else None + from_email_address_value = row[from_email_address_column] \ + if from_email_address_column is not None else None + to_email_address_value = row[to_email_address_column] \ + if to_email_address_column is not None else None + invoice_id_value = row[invoice_id_column] \ + if invoice_id_column is not None else None + subject_value = row[subject_column] \ + if subject_column is not None else None + note_value = row[note_column] \ + if note_column is not None else None + bank_name_value = row[bank_name_column] \ + if bank_name_column is not None else None + bank_account_value = row[bank_account_column] \ + if bank_account_column is not None else None + + if currency_value != currency_code: + continue + + date = datetime.strptime(date_value, mapping.date_format).date() + time = datetime.strptime(time_value, mapping.time_format).time() + timestamp = datetime.combine(date, time) + tz_value = self._normalize_tz(tz_value) + tz = timezone(tz_value) + timestamp = timestamp.replace(tzinfo=tz) + timestamp = timestamp.astimezone(utc).replace(tzinfo=None) + gross_amount = self._parse_decimal(gross_value, mapping) + fee_amount = self._parse_decimal(fee_value, mapping) + balance_amount = self._parse_decimal(balance_value, mapping) + bank = '%s - %s' % ( + bank_name_value, + bank_account_value, + ) if bank_name_value and bank_account_value else None + if to_email_address_column is None: + payer_email = from_email_address_value + else: + payer_email = to_email_address_value \ + if gross_amount < 0.0 else from_email_address_value + + lines.append({ + 'transaction_id': transaction_id_value, + 'invoice': invoice_id_value, + 'description': description_value or type_value, + 'details': subject_value or note_value or bank, + 'timestamp': timestamp, + 'gross_amount': gross_amount, + 'fee_amount': fee_amount, + 'balance_amount': balance_amount, + 'payer_name': name_value, + 'payer_email': payer_email, + }) + return lines + + @api.model + def _convert_line_to_transactions(self, line): + transactions = [] + + transaction_id = line['transaction_id'] + invoice = line['invoice'] + description = line['description'] + details = line['details'] + timestamp = line['timestamp'] + gross_amount = line['gross_amount'] + fee_amount = line['fee_amount'] + payer_name = line['payer_name'] + payer_email = line['payer_email'] + if invoice: + invoice = _('Invoice %s') % invoice + note = '%s %s' % ( + description, + transaction_id, + ) + if details: + note += ': %s' % details + if payer_email: + note += ' (%s)' % payer_email + + unique_import_id = '%s-%s' % ( + transaction_id, + int(timestamp.timestamp()), + ) + name = invoice or details or description or '', + transaction = { + 'name': invoice or details or description or '', + 'amount': str(gross_amount), + 'date': timestamp, + 'note': note, + 'unique_import_id': unique_import_id, + } + if payer_name: + line.update({ + 'partner_name': payer_name, + }) + transactions.append(transaction) + + if fee_amount: + transactions.append({ + 'name': _('Fee for %s') % (name or transaction_id), + 'amount': str(fee_amount), + 'date': timestamp, + 'partner_name': 'PayPal', + 'unique_import_id': '%s-FEE' % unique_import_id, + 'note': _('Transaction fee for %s') % note, + }) + return transactions + + @api.model + def _parse_decimal(self, value, mapping): + thousands, decimal = mapping._get_float_separators() + value = value.replace(thousands, '') + value = value.replace(decimal, '.') + return Decimal(value) + + @api.model + def _normalize_tz(self, value): + if value in ['PDT', 'PST']: + return 'PST8PDT' + return value diff --git a/account_bank_statement_import_paypal/models/account_journal.py b/account_bank_statement_import_paypal/models/account_journal.py index 0bc634e..20fbe50 100644 --- a/account_bank_statement_import_paypal/models/account_journal.py +++ b/account_bank_statement_import_paypal/models/account_journal.py @@ -1,18 +1,14 @@ # Copyright 2019 Tecnativa - Vicent Cubells -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# Copyright 2019 Brainbean Apps (https://brainbeanapps.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import models class AccountJournal(models.Model): - _inherit = "account.journal" - - paypal_map_id = fields.Many2one( - comodel_name='account.bank.statement.import.paypal.map', - string='Paypal Map', - ) + _inherit = 'account.journal' def _get_bank_statements_available_import_formats(self): res = super()._get_bank_statements_available_import_formats() - res.append('Paypal') + res.append('PayPal Reports') return res diff --git a/account_bank_statement_import_paypal/readme/CONFIGURE.rst b/account_bank_statement_import_paypal/readme/CONFIGURE.rst index 7693cae..f8dc121 100644 --- a/account_bank_statement_import_paypal/readme/CONFIGURE.rst +++ b/account_bank_statement_import_paypal/readme/CONFIGURE.rst @@ -1,12 +1,4 @@ -* 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. +To adjust PayPal report columns mapping: -* 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. +#. Open *Invoicing > Configuration > Accounting > PayPal Report Mappings* +#. Modify mapping(s) according to your CSV reports format. diff --git a/account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst b/account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst index 6e9a18d..c85a59a 100644 --- a/account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst +++ b/account_bank_statement_import_paypal/readme/CONTRIBUTORS.rst @@ -4,3 +4,5 @@ * Vicent Cubells * Victor M.M. Torres + +* Alexey Pelykh diff --git a/account_bank_statement_import_paypal/readme/DESCRIPTION.rst b/account_bank_statement_import_paypal/readme/DESCRIPTION.rst index 492b95d..2c29a68 100644 --- a/account_bank_statement_import_paypal/readme/DESCRIPTION.rst +++ b/account_bank_statement_import_paypal/readme/DESCRIPTION.rst @@ -1,2 +1,2 @@ -This module allows you to import the Paypal CSV files in Odoo as bank +This module allows you to import the PayPal CSV files in Odoo as bank statements. diff --git a/account_bank_statement_import_paypal/readme/HISTORY.rst b/account_bank_statement_import_paypal/readme/HISTORY.rst new file mode 100644 index 0000000..fe7b1ad --- /dev/null +++ b/account_bank_statement_import_paypal/readme/HISTORY.rst @@ -0,0 +1,7 @@ +12.0.2.0.0 +~~~~~~~~~~ + +* [BREAKING] New mapping, please review mappings after upgrade. +* [BREAKING] Different bank accounts have to be used per each currency. +* [ADD] Support for both Statement and Activity reports. +* [ADD] Separate fee and currency exchange parsing. diff --git a/account_bank_statement_import_paypal/readme/USAGE.rst b/account_bank_statement_import_paypal/readme/USAGE.rst index fdcc4da..0e2851c 100644 --- a/account_bank_statement_import_paypal/readme/USAGE.rst +++ b/account_bank_statement_import_paypal/readme/USAGE.rst @@ -1,8 +1,19 @@ -To use this module, you need to: +To import statements from PayPal via Statement report: -#. Go to Paypal and download your Bank Statement +#. Go to `PayPal `__ +#. Open *Reports > Statements* and select *Monthly* or *Custom* +#. Select date range of interest and click *Request* under *CSV* column +#. Wait for statement report to be generated +#. Download it and save to a file +#. Go to Odoo and and import saved statement file, selecting corresponding format -.. image:: account_bank_statement_import_paypal/static/description/paypal_backoffice.png - :alt: . -.. image:: static/description/paypal_backoffice.png - :alt: . +To import statements from PayPal via Activity report: + +#. Go to `PayPal `__ +#. Open *Reports > Activity download* +#. Set *Transaction type* to *Balance affecting* +#. Set *Format* to *CSV* +#. Select date range of interest and click *Create report* +#. Wait for activity report to be generated +#. Download it and save to a file +#. Go to Odoo and and import saved statement file, selecting corresponding format diff --git a/account_bank_statement_import_paypal/security/ir.model.access.csv b/account_bank_statement_import_paypal/security/ir.model.access.csv index db61ffb..bd4912b 100644 --- a/account_bank_statement_import_paypal/security/ir.model.access.csv +++ b/account_bank_statement_import_paypal/security/ir.model.access.csv @@ -1,3 +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 +access_account_bank_statement_import_paypal_mapping_manager,account.bank.statement.import.paypal.mapping:account.group_account_manager,model_account_bank_statement_import_paypal_mapping,account.group_account_manager,1,1,1,1 +access_account_bank_statement_import_paypal_mapping_user,account.bank.statement.import.paypal.mapping:account.group_account_user,model_account_bank_statement_import_paypal_mapping,account.group_account_user,1,0,0,0 diff --git a/account_bank_statement_import_paypal/static/description/index.html b/account_bank_statement_import_paypal/static/description/index.html index 402d195..740eeae 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

diff --git a/account_bank_statement_import_paypal/static/description/paypal_backoffice.png b/account_bank_statement_import_paypal/static/description/paypal_backoffice.png deleted file mode 100644 index a902aee13bea035e22df7db0059629d3bf347eb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97703 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
TYeiKEFlk;DkWdNtFpX#oRiKiSjqcabJ;~^ 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 + + account.bank.statement.import account.bank.statement.import -
  • Paypal with Template:
  • +
  • + PayPal Report mapping: +
  • diff --git a/account_bank_statement_import_paypal/views/account_bank_statement_import_paypal_mapping.xml b/account_bank_statement_import_paypal/views/account_bank_statement_import_paypal_mapping.xml new file mode 100644 index 0000000..05ed5fe --- /dev/null +++ b/account_bank_statement_import_paypal/views/account_bank_statement_import_paypal_mapping.xml @@ -0,0 +1,78 @@ + + + + + + account.bank.statement.import.paypal.mapping.form + account.bank.statement.import.paypal.mapping + + + + + + + + + account.bank.statement.import.paypal.mapping.tree + account.bank.statement.import.paypal.mapping + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + + + + + PayPal Report Mappings + account.bank.statement.import.paypal.mapping + form + tree,form + + + + +
    diff --git a/account_bank_statement_import_paypal/views/account_journal_views.xml b/account_bank_statement_import_paypal/views/account_journal_views.xml deleted file mode 100644 index 3e565b7..0000000 --- a/account_bank_statement_import_paypal/views/account_journal_views.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - 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 deleted file mode 100644 index 1fee4a6..0000000 --- a/account_bank_statement_import_paypal/views/paypal_map_views.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - 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 index 006d24c..5aeccea 100644 --- a/account_bank_statement_import_paypal/wizards/__init__.py +++ b/account_bank_statement_import_paypal/wizards/__init__.py @@ -1,2 +1,3 @@ -from . import create_map_lines_from_file -from . import account_bank_statement_import_paypal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import account_bank_statement_import_paypal_mapping_wizard 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 deleted file mode 100644 index 5eb194b..0000000 --- a/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal.py +++ /dev/null @@ -1,300 +0,0 @@ -# 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 odoo import _, api, fields, models -from odoo.exceptions import UserError -import re -from io import StringIO -_logger = logging.getLogger(__name__) - -try: - import csv -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' - - 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' - - @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 _paypal_convert_amount(self, amount_str): - 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 - def _check_paypal(self, data_file): - if not self.paypal_map_id: - return False - try: - data_file = self._get_paypal_str_data(data_file) - except UnicodeDecodeError as e: - _logger.debug(e) - 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): - rline = dict() - for item in range(len(line)): - paypal_map = self.mapped('paypal_map_id.map_line_ids')[item] - value = line[item] - if not paypal_map.field_to_assign: - continue - if paypal_map.date_format: - try: - value = fields.Date.to_string( - datetime.strptime(value, paypal_map.date_format)) - except Exception: - raise UserError( - _("Date format of map file and Paypal date does " - "not match.")) - rline[paypal_map.field_to_assign] = value - - 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 Exception: - 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): - data_file = self._get_paypal_str_data(data_file) - f = StringIO(data_file) - f.seek(0) - raw_lines = [] - 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)) - 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 _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')) - 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 = [] - 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, - } - return None - - @api.model - def _complete_stmts_vals(self, stmts_vals, journal_id, account_number): - """ Match the partner from paypal information """ - 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_paypal_mapping_wizard.py b/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal_mapping_wizard.py new file mode 100644 index 0000000..e7eb45c --- /dev/null +++ b/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal_mapping_wizard.py @@ -0,0 +1,84 @@ +# Copyright 2019 Tecnativa - Vicent Cubells +# Copyright 2019 Brainbean Apps (https://brainbeanapps.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models, _ +from odoo.exceptions import UserError + +from base64 import b64decode +from os import path + + +class AccountBankStatementImportPayPalMappingWizard(models.TransientModel): + _name = 'account.bank.statement.import.paypal.mapping.wizard' + _description = 'Account Bank Statement Import PayPal Mapping Wizard' + + data_file = fields.Binary( + string='Bank Statement File', + required=True, + ) + filename = fields.Char() + + @api.multi + def import_mapping(self): + self.ensure_one() + mapping_values = { + 'name': _('Mapping from %s') % path.basename(self.filename), + 'float_thousands_sep': 'comma', + 'float_decimal_sep': 'dot', + 'date_format': '%d/%m/%Y', + 'time_format': '%H:%M:%S', + } + header = self.env['account.bank.statement.import.paypal.parser'] \ + .parse_header(b64decode(self.data_file)) + if len(header) == 22: + mapping_values.update({ + 'date_column': header[0], + 'time_column': header[1], + 'tz_column': header[2], + 'name_column': header[3], + 'currency_column': header[6], + 'gross_column': header[7], + 'fee_column': header[8], + 'balance_column': header[18], + 'transaction_id_column': header[12], + 'type_column': header[4], + 'from_email_address_column': header[10], + 'to_email_address_column': header[11], + 'invoice_id_column': header[16], + 'subject_column': header[20], + 'note_column': header[21], + }) + elif len(header) == 18: + mapping_values.update({ + 'date_column': header[0], + 'time_column': header[1], + 'tz_column': header[2], + 'name_column': header[11], + 'currency_column': header[4], + 'gross_column': header[5], + 'fee_column': header[6], + 'balance_column': header[8], + 'transaction_id_column': header[9], + 'description_column': header[3], + 'from_email_address_column': header[10], + 'invoice_id_column': header[16], + 'bank_name_column': header[12], + 'bank_account_column': header[13], + }) + else: + raise UserError(_( + 'File structure does not look like a PayPal report, please ' + 'check the file or create the mapping manually.' + )) + mapping = self.env['account.bank.statement.import.paypal.mapping']\ + .create(mapping_values) + return { + 'type': 'ir.actions.act_window', + 'name': _('Imported Mapping'), + 'res_model': 'account.bank.statement.import.paypal.mapping', + 'res_id': mapping.id, + 'view_mode': 'form', + 'view_id': False, + 'target': 'new', + } diff --git a/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal_mapping_wizard.xml b/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal_mapping_wizard.xml new file mode 100644 index 0000000..148e5b4 --- /dev/null +++ b/account_bank_statement_import_paypal/wizards/account_bank_statement_import_paypal_mapping_wizard.xml @@ -0,0 +1,34 @@ + + + + + + account.bank.statement.import.paypal.mapping.wizard.form + account.bank.statement.import.paypal.mapping.wizard + +
    +

    Select a PayPal report file to import mapping.

    + + +
    +
    + +
    +
    + + + Import Mapping + account.bank.statement.import.paypal.mapping.wizard + account.bank.statement.import.paypal.mapping + form + form + new + + +
    \ No newline at end of file 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 deleted file mode 100644 index a9ca019..0000000 --- a/account_bank_statement_import_paypal/wizards/create_map_lines_from_file.py +++ /dev/null @@ -1,39 +0,0 @@ -# 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' - _description = '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 deleted file mode 100644 index c61136d..0000000 --- a/account_bank_statement_import_paypal/wizards/create_map_lines_from_file_views.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - 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.

    - - -
    -
    - -
    -
    - - -