Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions account_statement_import_sheet_file_bg/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import account_statement_import
from . import account_statement_import_sheet_parser
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,22 @@ class AccountStatementImport(models.TransientModel):
def import_file_button(self, wizard_data=None):
"""Process the file chosen in the wizard, create a bank statement
and return a link to its reconciliation page."""
if not self._context.get("bg_job"):
if self.sheet_mapping_id:
header_column = self.sheet_mapping_id.header_lines_skip_count
# Get row limit from system parameter
rows_limit = (
self.env["ir.config_parameter"]
.sudo()
.get_param("account_statement_import_sheet_file_bg.rows_per_file_limit")
)
# Only split if parameter exists and has a valid value
files = []
if rows_limit:
try:
rows_limit = int(rows_limit)
files = self.split_base64_excel(header_column, rows_limit)
except (ValueError, TypeError):
files = []
rows_limit = (
self.env["ir.config_parameter"]
.sudo()
.get_param("account_statement_import_sheet_file_bg.rows_per_file_limit")
)
if not self._context.get("bg_job") and rows_limit:
# Validate parameter is a valid integer
try:
rows_limit = int(rows_limit)
except (ValueError, TypeError):
# Parameter not valid, skip bg processing
rows_limit = None

if rows_limit and self.sheet_mapping_id:
header_column = self.sheet_mapping_id.header_lines_skip_count
files = self.split_base64_excel(header_column, rows_limit)
if files:
for idx, file in enumerate(files):
# Encode the file to string format, because background jobs cannot
Expand Down Expand Up @@ -171,15 +169,16 @@ def split_base64_excel(self, header_rows_count, rows_per_file_limit):
mapping = self.sheet_mapping_id
journal = self.env["account.journal"].browse(self.env.context.get("journal_id"))
currency_code = (journal.currency_id or journal.company_id.currency_id).name

try:
file_bytes = base64.b64decode(self.statement_file)
read_buffer = BytesIO(file_bytes)

# Try openpyxl (xlsx)
input_workbook = load_workbook(read_buffer)
input_worksheet = input_workbook.active
all_rows = list(input_worksheet.rows)
# Normalize rows to plain values to keep parser/output logic
# consistent with CSV/xls flows.
all_rows = [[cell.value for cell in row] for row in input_worksheet.rows]

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Para XLSX, en vez de iterar input_worksheet.rows y extraer cell.value manualmente, se puede usar input_worksheet.iter_rows(values_only=True) (y opcionalmente filtrar filas totalmente vacías como en CSV) para reducir overhead/memoria en archivos grandes y mantener el comportamiento más consistente.

Suggested change
all_rows = [[cell.value for cell in row] for row in input_worksheet.rows]
all_rows = [list(row) for row in input_worksheet.iter_rows(values_only=True)]

Copilot uses AI. Check for mistakes.
csv_or_xlsx = (input_workbook, input_worksheet)

except Exception:
Expand Down Expand Up @@ -277,10 +276,14 @@ def _filter_rows_with_date(self, data_rows, date_column_index):

filtered_rows = []
for row in data_rows:
date_value = None
if len(row) > date_column_index:
date_cell_or_value = row[date_column_index]
date_value = date_cell_or_value.value if hasattr(date_cell_or_value, "value") else date_cell_or_value
# Check if the row has enough columns and the date column is not empty
if len(row) > date_column_index and row[date_column_index].value:
if len(row) > date_column_index and date_value:
filtered_rows.append(row)
elif len(row) > date_column_index and not row[date_column_index].value:
elif len(row) > date_column_index and not date_value:
# Stop processing when we find the first empty date
break

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2026 ADHOC SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, models


class AccountStatementImportSheetParser(models.TransientModel):
_inherit = "account.statement.import.sheet.parser"

@api.model
def parse_header(self, csv_or_xlsx, mapping):
if mapping.no_header:
return []

header_line = mapping.header_lines_skip_count
# Prevent negative indexes.
if header_line > 0:
header_line -= 1

if isinstance(csv_or_xlsx, tuple):
return super().parse_header(csv_or_xlsx, mapping)

[next(csv_or_xlsx) for _i in range(header_line)]
header = []
for value in next(csv_or_xlsx):
raw_value = value.value if hasattr(value, "value") else value
header.append(str(raw_value).strip() if raw_value is not None else "")
Comment on lines +23 to +27

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En parse_header(), el salto de líneas usa una list comprehension solo por efectos laterales, creando una lista innecesaria. Además, si header_lines_skip_count excede las filas disponibles, next(csv_or_xlsx) va a lanzar StopIteration y romper el import; conviene iterar con un for y capturar StopIteration para devolver un error de usuario más claro (o [] si corresponde).

Copilot uses AI. Check for mistakes.

if mapping.offset_column:
header = header[mapping.offset_column :]
return header
Loading