diff --git a/account_statement_import_txt_xlsx/models/account_statement_import_sheet_mapping.py b/account_statement_import_txt_xlsx/models/account_statement_import_sheet_mapping.py index a0a9e26fba..2a1a181bf4 100644 --- a/account_statement_import_txt_xlsx/models/account_statement_import_sheet_mapping.py +++ b/account_statement_import_txt_xlsx/models/account_statement_import_sheet_mapping.py @@ -157,7 +157,18 @@ class AccountStatementImportSheetMapping(models.Model): string="Bank Account column", help="Partner's bank account", ) - + footer_lines_skip_count = fields.Integer( + string="Footer lines skip count", + help="Set the Footer lines number." + "Used in some csv/xlsx file that integrate meta data in" + "last lines.", + default="0", + ) + header_lines_skip_count = fields.Integer( + string="Header lines skip count", + help="Set the Header lines number.", + default="0", + ) _sql_constraints = [ ( "check_amount_columns", diff --git a/account_statement_import_txt_xlsx/models/account_statement_import_sheet_parser.py b/account_statement_import_txt_xlsx/models/account_statement_import_sheet_parser.py index 1d56b6ab87..22b1a56952 100644 --- a/account_statement_import_txt_xlsx/models/account_statement_import_sheet_parser.py +++ b/account_statement_import_txt_xlsx/models/account_statement_import_sheet_parser.py @@ -4,6 +4,7 @@ import itertools import logging +from collections.abc import Iterable from datetime import datetime from decimal import Decimal from io import StringIO @@ -36,21 +37,24 @@ class AccountStatementImportSheetParser(models.TransientModel): _description = "Bank Statement Import Sheet Parser" @api.model - def parse_header(self, data_file, encoding, csv_options): + def parse_header(self, data_file, encoding, csv_options, header_lines_skip_count=0): try: workbook = xlrd.open_workbook( file_contents=data_file, encoding_override=encoding if encoding else None, ) sheet = workbook.sheet_by_index(0) - values = sheet.row_values(0) + values = sheet.row_values(header_lines_skip_count - 1) return [str(value) for value in values] except xlrd.XLRDError: pass data = StringIO(data_file.decode(encoding or "utf-8")) csv_data = reader(data, **csv_options) - return list(next(csv_data)) + csv_data_lst = list(csv_data) + header = [value.strip() for value in csv_data_lst[header_lines_skip_count - 1]] + return header + # return list(next(csv_data)) @api.model def parse(self, data_file, mapping, filename): @@ -95,7 +99,11 @@ def parse(self, data_file, mapping, filename): def _get_column_indexes(self, header, column_name, mapping): column_indexes = [] - if mapping[column_name] and "," in mapping[column_name]: + if ( + mapping[column_name] + and isinstance(mapping[column_name], Iterable) + and "," in mapping[column_name] + ): # We have to concatenate the values column_names_or_indexes = mapping[column_name].split(",") else: @@ -169,10 +177,10 @@ def _parse_lines(self, mapping, data_file, currency_code): csv_or_xlsx = reader(StringIO(decoded_file), **csv_options) header = False if not mapping.no_header: + header_line = mapping.header_lines_skip_count - 1 if isinstance(csv_or_xlsx, tuple): header = [ - str(value) - for value in csv_or_xlsx[1].row_values(mapping.offset_row) + str(value) for value in csv_or_xlsx[1].row_values(header_line) ] else: header = [value.strip() for value in next(csv_or_xlsx)] @@ -182,7 +190,9 @@ def _parse_lines(self, mapping, data_file, currency_code): columns[column_name] = self._get_column_indexes( header, column_name, mapping ) - return self._parse_rows(mapping, currency_code, csv_or_xlsx, columns) + return self._parse_rows( + mapping, currency_code, csv_or_xlsx, columns, data_file=data_file + ) def _get_values_from_column(self, values, columns, column_name): indexes = columns[column_name] @@ -324,25 +334,40 @@ def _decimal(column_name): line["bank_account"] = bank_account return line - def _parse_rows(self, mapping, currency_code, csv_or_xlsx, columns): # noqa: C901 + def _parse_rows( + self, mapping, currency_code, csv_or_xlsx, columns, data_file=None + ): # noqa: C901 + # csv_or_xlsx, data_file = data + + # Get the numbers of rows of the file + if isinstance(csv_or_xlsx, tuple): + numrows = csv_or_xlsx[1].nrows + else: + numrows = len(str(data_file.strip()).split("\\n")) + + label_line = mapping.header_lines_skip_count + footer_line = numrows - mapping.footer_lines_skip_count + if isinstance(csv_or_xlsx, tuple): - rows = range(mapping.offset_row + 1, csv_or_xlsx[1].nrows) + rows = range(mapping.header_lines_skip_count, footer_line) else: rows = csv_or_xlsx lines = [] - for row in rows: + for index, row in enumerate(rows, label_line): if isinstance(csv_or_xlsx, tuple): book = csv_or_xlsx[0] sheet = csv_or_xlsx[1] values = [] - for col_index in range(mapping.offset_column, sheet.row_len(row)): + for col_index in range(0, sheet.row_len(row)): cell_type = sheet.cell_type(row, col_index) cell_value = sheet.cell_value(row, col_index) if cell_type == xlrd.XL_CELL_DATE: cell_value = xldate_as_datetime(cell_value, book.datemode) values.append(cell_value) else: + if index >= footer_line: + continue values = list(row) if mapping.skip_empty_lines and not any(values): continue diff --git a/account_statement_import_txt_xlsx/views/account_statement_import_sheet_mapping.xml b/account_statement_import_txt_xlsx/views/account_statement_import_sheet_mapping.xml index 0ab58c800d..afe6a70593 100644 --- a/account_statement_import_txt_xlsx/views/account_statement_import_sheet_mapping.xml +++ b/account_statement_import_txt_xlsx/views/account_statement_import_sheet_mapping.xml @@ -72,6 +72,10 @@ attrs="{'required': [('debit_credit_column', '!=', False)]}" /> + + + +