Skip to content
Open
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
186 changes: 186 additions & 0 deletions account_statement_import_required_fields/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
========================================
Account Statement Import Required Fields
========================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:3759e889c444ee30225568d494d864a150c07386c44f6796f73d3063477a2ca7
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |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/18.0/account_statement_import_required_fields
: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-18-0/bank-statement-import-18-0-account_statement_import_required_fields
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/bank-statement-import&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module allows you to enforce mandatory columns when importing bank
statements via TXT/CSV/XLSX in Odoo. It extends the mapping profiles
from the Odoo Community Association (OCA) module
``account_statement_import_sheet_file``.

--------------

Key Features
------------

1. **Mandatory Column Configuration**

- In your Bank Statement Sheet Mappings, you can designate specific
mapped columns as "Mandatory Mapping Columns".
- Only columns that you have mapped (e.g., Date, Description, Amount)
will be available to select.
- If a column is cleared from the mapping, it is automatically
deselected from the mandatory fields list.

2. **Pre-Import Data Validation**

- When importing a file, the parser intercepts the raw TXT/CSV/XLSX
cells before Odoo processes them.
- If any of the mandatory columns are completely empty or blank in
the file for a particular line, the import is halted immediately.
- Generates a clear, user-friendly error message detailing exactly
which required columns are missing data.

3. **Smart Debit/Credit Amount Handling**

- Bank statements often represent amounts by placing values
exclusively in either a Debit column or a Credit column.
- If you set **both** the *Debit amount column* and *Credit amount
column* as mandatory, the module intelligently requires that **at
least one** of them must have a value on any given row.

**Table of contents**

.. contents::
:local:

Configuration
=============

This document explains how to configure mandatory columns for your bank
statement imports.

--------------

Configuring Mandatory Columns
-----------------------------

To enforce that specific mapped columns are present during bank
statement imports:

1. Go to **Accounting > Configuration > Statement Sheet Mappings** (or
**Invoicing > Configuration > Statement Sheet Mappings** depending on
your installed apps).
2. Open an existing mapping profile or create a new one according to
your bank's file format.
3. Under the **Columns** section, fill in the names/indexes of the
columns you are mapping (e.g., ``Timestamp Column`` = Date,
``Amount column`` = Amount).
4. Locate the **Required Fields** (or **Mandatory Mapping Columns**)
section on the form.
5. Click to add fields. *Note: Only columns that you have mapped in step
3 will appear as available options.*
6. Select all the columns that you want to enforce as mandatory for
every row in the imported file.

--------------

Special Configuration: Debit and Credit Columns
-----------------------------------------------

If your bank statement format splits amounts into separate Debit and
Credit columns rather than a single Amount column, you can enforce them
intelligently:

1. Map both **Debit amount column** and **Credit amount column** in the
Columns section.
2. Add **both** of them to the **Mandatory Mapping Columns** field.
3. **Result**: The module understands that a transaction is usually
either a debit or a credit. During import, the parser will verify
that **at least one** of these two fields has a value on every single
row. If a row is entirely blank for both debit and credit, the import
will be safely halted.

Usage
=====

To use this module, follow these steps:

1. Export your bank statement from your online banking portal in **CSV**
or **XLSX** or **TXT** format.
2. In Odoo, navigate to your Accounting Dashboard and click **Import
Statement** on the relevant bank journal.
3. Upload your statement file and select the **Sheet Mapping** profile
you configured with your Mandatory Columns.
4. Click **Import**.
5. **Validation in Action**: Odoo will scan the file before creating any
records. If any row is missing data in one of the columns you marked
as mandatory, the import will be safely halted.
6. An error message will appear detailing exactly which required columns
are missing data. Correct your file (or remove the junk row) and
retry the import!

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/bank-statement-import/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/bank-statement-import/issues/new?body=module:%20account_statement_import_required_fields%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

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

Credits
=======

Authors
-------

* Heliconia Solutions Pvt. Ltd.

Contributors
------------

- `Heliconia Solutions Pvt. Ltd. <https://www.heliconia.io>`__

- Bhavesh Heliconia

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

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.

.. |maintainer-Bhavesh Heliconia| image:: https://github.com/Bhavesh Heliconia.png?size=40px
:target: https://github.com/Bhavesh Heliconia
:alt: Bhavesh Heliconia

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-Bhavesh Heliconia|

This module is part of the `OCA/bank-statement-import <https://github.com/OCA/bank-statement-import/tree/18.0/account_statement_import_required_fields>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
4 changes: 4 additions & 0 deletions account_statement_import_required_fields/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright 2026 Heliconia Solutions Pvt. Ltd.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import models
23 changes: 23 additions & 0 deletions account_statement_import_required_fields/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2026 Heliconia Solutions Pvt. Ltd.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Account Statement Import Required Fields",
"summary": "Enforce mandatory columns during bank statement import (TXT/CSV/XLSX)",
"version": "18.0.1.0.0",
"category": "Accounting",
"author": "Heliconia Solutions Pvt. Ltd., Odoo Community Association (OCA)",
"website": "https://github.com/OCA/bank-statement-import",
"license": "AGPL-3",
"depends": [
"account",
"account_statement_import_sheet_file",
],
"data": [
"views/account_statement_import_sheet_mapping_view.xml",
],
"maintainers": ["Bhavesh Heliconia"],
"installable": True,
"application": False,
"auto_install": False,
}
5 changes: 5 additions & 0 deletions account_statement_import_required_fields/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright 2026 Heliconia Solutions Pvt. Ltd.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import account_statement_import_sheet_mapping
from . import account_statement_import_sheet_parser
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Copyright 2026 Heliconia Solutions Pvt. Ltd.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, fields, models


class AccountStatementImportRequiredField(models.Model):
_inherit = "account.statement.import.sheet.mapping"

available_field_ids = fields.Many2many(
comodel_name="ir.model.fields",
compute="_compute_available_field_ids",
)

required_field_ids = fields.Many2many(
comodel_name="ir.model.fields",
relation="account_stmt_mapping_req_fld_rel",
column1="mapping_id",
column2="field_id",
string="Mandatory Mapping Columns",
help="Select which mapped columns must have a value in every row of "
"the imported file. "
"\nSpecial case: If you select both Debit and Credit columns, "
"at least one of them must be present in a given row.",
domain="[('id', 'in', available_field_ids)]",
)

@api.depends(
"timestamp_column",
"currency_column",
"amount_column",
"amount_debit_column",
"amount_credit_column",
"balance_column",
"original_currency_column",
"original_amount_column",
"debit_credit_column",
"transaction_id_column",
"description_column",
"notes_column",
"reference_column",
"partner_name_column",
"bank_name_column",
"bank_account_column",
)
def _compute_available_field_ids(self):
column_fields = [
"timestamp_column",
"currency_column",
"amount_column",
"amount_debit_column",
"amount_credit_column",
"balance_column",
"original_currency_column",
"original_amount_column",
"debit_credit_column",
"transaction_id_column",
"description_column",
"notes_column",
"reference_column",
"partner_name_column",
"bank_name_column",
"bank_account_column",
]
for record in self:
valid_field_names = []
for field_name in column_fields:
if getattr(record, field_name):
valid_field_names.append(field_name)

if valid_field_names:
fields_records = (
self.env["ir.model.fields"]
.sudo()
.search(
[
("model", "=", "account.statement.import.sheet.mapping"),
("name", "in", valid_field_names),
]
)
)
record.available_field_ids = fields_records
else:
record.available_field_ids = False

@api.onchange("available_field_ids")
def _onchange_available_field_ids(self):
for record in self:
if record.required_field_ids:
record.required_field_ids = (
record.required_field_ids & record.available_field_ids
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Copyright 2026 Heliconia Solutions Pvt. Ltd.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import models
from odoo.exceptions import ValidationError


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

def _parse_rows(self, mapping, currency_code, data, columns):
missing_cols = set()
parser = self
if mapping.required_field_ids:
required_field_names = mapping.required_field_ids.mapped("name")
parser = self.with_context(
mapping_required_fields=required_field_names,
missing_required_columns=missing_cols,
)

rows = super(AccountStatementImportSheetParser, parser)._parse_rows(
mapping, currency_code, data, columns
)

if not rows:
return rows

if missing_cols:
missing_field_labels = [
f"• {getattr(mapping, col)}" for col in missing_cols
]
raise ValidationError(
self.env._(
"The bank statement could not be imported. "
"The following required columns are missing data in one or "
"more rows:\n\n"
"%(missing_field_names)s\n\n"
"Please verify your file and ensure these columns have values "
"where expected.",
missing_field_names="\n".join(missing_field_labels),
)
)
return rows

def _get_values_from_column(self, values, columns, column_name):
res = super()._get_values_from_column(values, columns, column_name)

required_fields = self.env.context.get("mapping_required_fields", [])
if required_fields and column_name == "timestamp_column":
# 'timestamp_column' is an anchor, evaluated once per row.
row_missing = []
for req_col in required_fields:
if not columns.get(req_col):
continue
val = super()._get_values_from_column(values, columns, req_col)
if val is None or (isinstance(val, str) and str(val).strip() == ""):
row_missing.append(req_col)

# Special case for debit and credit amount columns
if (
"amount_debit_column" in required_fields
and "amount_credit_column" in required_fields
):
has_debit = "amount_debit_column" not in row_missing
has_credit = "amount_credit_column" not in row_missing
if has_debit or has_credit:
# At least one has a value, so remove both from the missing list
if "amount_debit_column" in row_missing:
row_missing.remove("amount_debit_column")
if "amount_credit_column" in row_missing:
row_missing.remove("amount_credit_column")

if row_missing:
missing_set = self.env.context.get("missing_required_columns")
if missing_set is not None:
for col in row_missing:
missing_set.add(col)

return res
3 changes: 3 additions & 0 deletions account_statement_import_required_fields/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
Loading
Loading