How to Import MT940 Bank Statements in ERPNext and Troubleshoot Parsing Errors
ERPNext imports MT940 bank statements by detecting the format via mandatory SWIFT tags, preprocessing statement numbers to fix 6-digit truncation issues, parsing with the mt940 library, converting to CSV, and running the generic Data Import engine—most errors stem from missing format flags or non-standard tags that can be debugged through the Bank Statement Import logs.
The MT940 bank statement import process in ERPNext (frappe/erpnext) allows organizations to automatically import SWIFT MT940 format files into the accounting system using the Bank Statement Import doctype. This standardized workflow transforms raw MT940 text files into structured Bank Transaction records through a series of validation, preprocessing, and conversion steps. Understanding the underlying Python implementation in the ERPNext source code is essential for troubleshooting parsing errors when dealing with non-standard bank exports.
How the MT940 Bank Statement Import Process Works in ERPNext
The import flow follows seven distinct stages orchestrated by erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py.
1. File Upload and Storage
Users attach a .txt file containing the MT940 data to a Bank Statement Import document. The system stores the file path in the import_file field during the validation phase. According to the source code at lines 74-76, the validate method ensures the file attachment exists before proceeding.
2. MT940 Format Detection
Before conversion, the helper function is_mt940_format() checks for mandatory MT940 tags including :20:, :25:, :28C:, and :61: (lines 36-40). This validation ensures the uploaded file conforms to SWIFT standards before processing begins.
3. Format Flag Verification
If the file contains valid MT940 tags but the import_mt940_fromat checkbox remains unchecked, ERPNext raises the error: "MT940 file detected. Please enable 'Import MT940 Format' to proceed." This safeguard prevents accidental processing of mixed file types (lines 55-57).
4. Statement Number Preprocessing
Some banks emit 6-digit (or longer) statement numbers in the :28C: tag, which the mt940 Python library rejects because it expects 5 digits or fewer. The preprocess_mt940_content() function (lines 14-42) automatically trims excess digits to the last five while preserving optional sequence parts, preventing parsing failures.
5. MT940 Parsing
The system calls mt940.parse() on the preprocessed content (lines 58-63). Any exception during this stage bubbles up as "Failed to parse MT940 format..." with the specific library error message attached.
6. CSV Generation for Data Import
Each parsed transaction normalizes into rows with columns for Date, Deposit, Withdrawal, Description, Reference Number, Bank Account, and Currency (lines 68-99). The generated CSV saves to the File Manager and attaches back to the import document for the generic Data Import engine.
7. Background Import Execution
ERPNext's start_import() background job (lines 55-71) processes the CSV file to create individual Bank Transaction records linked to the appropriate bank accounts.
Common MT940 Parsing Errors and Troubleshooting Fixes
When the MT940 bank statement import process fails, the error messages directly map to specific validation checks in the source code.
"The uploaded file does not appear to be in valid MT940 format."
This error triggers when is_mt940_format() cannot locate the mandatory tags (:20:, :25:, :28C:, :61:). Verify the raw .txt file in a text editor to ensure these SWIFT tags exist and the file is not corrupted or password-protected.
"MT940 file detected. Please enable 'Import MT940 Format' to proceed."
This indicates the file passed format detection but the import_mt940_fromat field on the Bank Statement Import document is unchecked. Enable this flag in the import form before retrying.
"Failed to parse MT940 format. Error: ..."
The mt940 library raised an exception, typically due to statement numbers exceeding 5 digits or unexpected custom tags. While preprocess_mt940_content() handles the digit length issue automatically, persistent errors require manual inspection for non-standard bank-specific tags. Contact the bank for a cleaner export or strip custom tags manually.
"Parsed file is not in valid MT940 format or contains no transactions."
The parser succeeded but returned an empty transaction list. Ensure the file contains transaction lines with :61: tags, as some banks provide balance-only statements without transaction details.
"Bank Account column missing"
The generated CSV requires a Bank Account column. The import wizard automatically injects this via add_bank_account(), but manual CSV edits must preserve this header.
Debugging Techniques for Failed Imports
When standard error messages insufficiently explain failures, use these diagnostic approaches based on the ERPNext implementation.
-
Inspect Raw File Content – Download the uploaded
.txtfile via the File Manager and locate the:28C:line to verify statement number formatting. -
Review Import Logs – Navigate to Bank Statement Import → Download Import Log to view server-side tracebacks and identify exactly where
convert_mt940_to_csv()failed. -
Enable Verbose Error Logging – Temporarily modify the exception handler in
bank_statement_import.pyto addfrappe.log_error(e, "MT940 Parse")for capturing full stack traces in the Error Log doctype. -
Manual Conversion Testing – Invoke the preprocessing function directly in a Python console to isolate parsing issues from the broader import workflow.
Practical Code Examples for MT940 Import
Client-Side Upload and Import Trigger
Use this JavaScript pattern to programmatically upload a statement and initiate the MT940 conversion.
frappe.call({
method: "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
args: { company: "My Company", bank_account: "1234-5678-90" },
callback: function(r) {
const docname = r.message.name;
// Enable MT940 import flag
frappe.db.set_value('Bank Statement Import', docname, 'import_mt940_fromat', 1);
// Attach the .txt file (assume `filedata` is a File object)
const fd = new FormData();
fd.append('file', filedata);
fd.append('docname', docname);
frappe.upload.upload_file(fd, {
doctype: 'Bank Statement Import',
docname: docname,
fieldname: 'import_file',
callback: function() {
// Convert to CSV & start background job
frappe.call({
method: "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.convert_mt940_to_csv",
args: { data_import: docname, mt940_file_path: filedata.file_url },
callback: function(res) {
// The CSV is now attached; kick off import
frappe.call('erpnext.accounts.doctype.bank_statement_import.bank_statement_import.form_start_import',
{ data_import: docname });
}
});
}
});
}
});
Server-Side Conversion Logic
The convert_mt940_to_csv whitelisted method executes the core transformation.
@frappe.whitelist()
def convert_mt940_to_csv(data_import: str, mt940_file_path: str):
# Load file content
_file_doc, content = get_file(mt940_file_path)
# Verify MT940 tags
if not is_mt940_format(content):
frappe.throw(_("The uploaded file does not appear to be in valid MT940 format."))
# Ensure user enabled the flag
if not frappe.get_doc("Bank Statement Import", data_import).import_mt940_fromat:
frappe.throw(_("MT940 file detected. Please enable 'Import MT940 Format' to proceed."))
# Pre-process statement numbers (truncates >5-digit numbers)
processed_content = preprocess_mt940_content(content)
# Parse using the mt940 library
transactions = mt940.parse(processed_content)
# Build CSV and save to File Manager
csv_buffer = io.StringIO()
writer = csv.writer(csv_buffer)
writer.writerow(["Date", "Deposit", "Withdrawal", "Description",
"Reference Number", "Bank Account", "Currency"])
for txn in transactions:
writer.writerow([...]) # Transaction data mapping
saved_file = save_file(
f"{frappe.utils.now_datetime():%Y%m%d%H%M%S}_converted_mt940.csv",
csv_buffer.getvalue().encode("utf-8"),
"Bank Statement Import",
data_import,
is_private=True,
df="import_file",
)
return saved_file.file_url
Manual Preprocessing for Debugging
Test the statement number fix outside the import workflow.
from erpnext.accounts.doctype.bank_statement_import.bank_statement_import import preprocess_mt940_content
with open("my_statement.txt") as f:
raw = f.read()
clean = preprocess_mt940_content(raw)
print(clean[:500]) # Verify :28C: line has ≤5 digits
Key Source Files in the ERPNext Repository
Understanding these files deepens debugging capabilities.
-
erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py– Contains the core implementation includingis_mt940_format(),preprocess_mt940_content(), andconvert_mt940_to_csv(). -
bank_statement_import_log.py– Stores per-row import logs accessible via the Download Import Log feature. -
bank_statement_import_list.js– Provides front-end UI actions for Convert MT940 and Download Log. -
bank_statement_import_log_column_map.py– Maps CSV columns to doctype fields when using custom import templates.
Summary
- ERPNext detects MT940 files by validating the presence of SWIFT tags
:20:,:25:,:28C:, and:61:before processing begins. - The
import_mt940_fromatflag must be enabled on the Bank Statement Import document to proceed with conversion. - The
preprocess_mt940_content()function automatically truncates statement numbers in:28C:tags to 5 digits to comply with the mt940 library requirements. - Failed imports generate CSV files for manual inspection and detailed logs accessible via the import document's Download Import Log button.
- The process ultimately leverages ERPNext's standard Data Import engine to create
Bank Transactionrecords from the converted CSV.
Frequently Asked Questions
Why does ERPNext require the "Import MT940 Format" checkbox when it already auto-detects the file type?
The import_mt940_fromat field acts as an explicit consent mechanism. While is_mt940_format() can identify MT940 files by their tag structure, the checkbox prevents accidental processing when users upload mixed-format files, ensuring intentional MT940 conversion before the system executes convert_mt940_to_csv().
How do I fix "Failed to parse MT940 format" errors caused by 6-digit statement numbers?
The ERPNext source code includes preprocess_mt940_content() in bank_statement_import.py (lines 14-42) which automatically truncates statement numbers in :28C: tags to the last 5 digits. If errors persist, manually inspect the file for non-standard tags or bank-specific proprietary extensions that the mt940 library cannot parse.
Where can I find detailed error logs when the Bank Statement Import fails?
Access detailed tracebacks through Bank Statement Import → Download Import Log. For deeper server-side debugging, check the Error Log doctype in the Frappe desk or temporarily add frappe.log_error(e, "MT940 Parse") to the exception handlers in bank_statement_import.py.
Can I import MT940 files with custom tag structures or extensions?
The standard implementation requires strict adherence to MT940 tag formats (:20:, :25:, :28C:, :61:). For non-standard bank exports, you must preprocess the file to remove proprietary tags or modify the preprocess_mt940_content() function in the ERPNext source code to handle your specific bank's variations before calling mt940.parse().
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →