Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
06ac632
feat: bank reconciliation rule
0xD0M1M0 Dec 16, 2025
3923fbc
fix: recall handle_excluded_fee
0xD0M1M0 Dec 16, 2025
2c5512a
refactor: refactor functions
0xD0M1M0 Dec 17, 2025
253ea0e
chore: remove comments
0xD0M1M0 Dec 17, 2025
c9c7997
fix: create_je_bank_fees only if included fee is set
0xD0M1M0 Dec 17, 2025
7b0037e
fix: bank_account
0xD0M1M0 Dec 18, 2025
1134e57
fix: deposit fees and rule credit value corrected
0xD0M1M0 Jan 6, 2026
fa85b90
feat: added test cases
0xD0M1M0 Jan 6, 2026
d05f9fd
fix(Bank Account): various fixes
barredterra Jan 14, 2026
2c8f0ce
feat: add rule to workspace
barredterra Jan 14, 2026
88d1873
fix(Bank Reconciliation Rule): filter_description, type hints
barredterra Jan 14, 2026
fa5339b
fix(Bank Reconciliation Rule): show only bank accounts with company a…
barredterra Jan 14, 2026
3577e7d
refactor(Bank Reconciliation Rule): set target account query
barredterra Jan 14, 2026
276ba76
refactor(Bank Reconciliation Rule): render_bt_filters
barredterra Jan 14, 2026
8242db8
feat(Bank Reconciliation Rule): make filters read-only when submitted
barredterra Jan 14, 2026
3d9a67e
fix: set clearance date after submit
0xD0M1M0 Jan 16, 2026
17ebb1c
refactor: test case for Bank Reconciliation Rule
barredterra Jan 27, 2026
3b096af
refactor: move exceptions and helpers to general utils
barredterra Jan 27, 2026
1639a8b
refactor: test_bank_account to make use of helpers
barredterra Jan 27, 2026
e18e89b
refactor: test_bank_transaction to make use of helpers
barredterra Jan 27, 2026
ad17236
refactor: don't check for exact error message
barredterra Jan 27, 2026
51a2487
refactor: move doc creation into helpers
barredterra Jan 27, 2026
2a267d1
Merge remote-tracking branch 'upstream/version-15-hotfix' into bank-r…
barredterra Jan 27, 2026
f9f00ce
fix: user-facing messages
barredterra Jan 27, 2026
6c53ba1
chore: update translations
barredterra Jan 27, 2026
f9f0984
fix: translate field labels
barredterra Jan 27, 2026
836c5e0
fix: mark label as translatable
barredterra Feb 2, 2026
287cd32
refactor(Bank Transaction): enforce positive values
barredterra Feb 2, 2026
0cf3e0c
refactor: pass cost center instead of company doc
barredterra Feb 2, 2026
1a3fd1e
fix: handle case when fee is not set
barredterra Feb 2, 2026
c43341a
test: don't convert None
barredterra Feb 2, 2026
d28975e
test: make sure we use the same company everywhere
barredterra Feb 2, 2026
ec51992
refactor: create_je_automatic_rules
barredterra Feb 2, 2026
c1e32f8
fix: avoid comparing with None
barredterra Feb 2, 2026
8cbcb4e
test: separate class for positive/negative unit test
barredterra Feb 2, 2026
ff920d9
test: centralize setup, rely on rollback for deletion
barredterra Feb 2, 2026
a60df17
test: centralize setup, rely on rollback for deletion (part 2)
barredterra Feb 2, 2026
557e96c
Merge branch 'version-15-hotfix' into bank-reconcilation-rule
barredterra Feb 6, 2026
3124e4c
refactor: create_automatic_journal_entry
barredterra Feb 17, 2026
5189c34
fix: make bank fee entry optional
barredterra Feb 17, 2026
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
35 changes: 35 additions & 0 deletions banking/custom/bank_account.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2025, ALYF GmbH and contributors
// For license information, please see license.txt

frappe.ui.form.on("Bank Account", {
refresh(frm) {
frm.trigger("set_fee_account_query");
},

account(frm) {
frm.trigger("set_fee_account_query");
},

async set_fee_account_query(frm) {
if (!frm.doc.account) {
return;
}

const {
message: { account_currency: currency, company: company },
} = await frappe.db.get_value("Account", frm.doc.account, [
"account_currency",
"company",
]);

frm.set_query("bank_fee_account", (doc) => {
return {
filters: {
root_type: "Expense",
account_currency: currency,
company: company,
},
};
});
},
});
10 changes: 10 additions & 0 deletions banking/custom_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,14 @@ def get_custom_fields():
insert_after="transaction_id",
),
],
"Bank Account": [
dict(
fieldname="bank_fee_account",
fieldtype="Link",
label=_("Bank Fee Account"),
options="Account",
depends_on="eval:doc.is_company_account && doc.account",
insert_after="account_subtype",
),
],
}
10 changes: 10 additions & 0 deletions banking/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2025, ALYF GmbH and Contributors
# See license.txt

from frappe.exceptions import ValidationError


class CurrencyMismatchError(ValidationError):
"""Raised when two accounts unexpectedly have different currencies."""

pass
5 changes: 5 additions & 0 deletions banking/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"Employee": "custom/employee.js",
"Supplier": "custom/supplier.js",
"Bank Reconciliation Tool": "custom/bank_reconciliation_tool.js",
"Bank Account": "custom/bank_account.js",
}
doctype_list_js = {"Purchase Invoice": "custom/purchase_invoice_list.js"}
# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"}
Expand Down Expand Up @@ -108,9 +109,13 @@
doc_events = {
"Bank Transaction": {
"on_update_after_submit": "banking.overrides.bank_transaction.on_update_after_submit",
"before_validate": "banking.overrides.bank_transaction.before_validate",
"before_submit": "banking.overrides.bank_transaction.before_submit",
"on_cancel": "banking.overrides.bank_transaction.on_cancel",
},
"Bank Account": {
"before_validate": "banking.overrides.bank_account.before_validate",
"validate": "banking.overrides.bank_account.validate",
},
"Employee": {
"validate": "banking.custom.employee.validate",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) 2025, ALYF GmbH and contributors
// For license information, please see license.txt

frappe.ui.form.on("Bank Reconciliation Rule", {
onload: function (frm) {
frm.trigger("render_bt_filters");
},
refresh(frm) {
frm.trigger("set_target_account_query");
},
bank_account(frm) {
frm.trigger("set_target_account_query");
},
async set_target_account_query(frm) {
if (!frm.doc.bank_account) {
return;
}

const {
message: { account },
} = await frappe.db.get_value(
"Bank Account",
frm.doc.bank_account,
"account"
);

const {
message: { account_currency: currency, company: company },
} = await frappe.db.get_value("Account", account, [
"account_currency",
"company",
]);

frm.set_query("target_account", () => ({
filters: {
account_currency: currency,
company: company,
},
}));
},
render_bt_filters(frm) {
const parent = frm.fields_dict.filter_area.$wrapper;
parent.empty();

const filters =
frm.doc.filters && frm.doc.filters !== "[]"
? JSON.parse(frm.doc.filters)
: [];

frappe.model.with_doctype("Bank Transaction", () => {
const filter_group = new frappe.ui.FilterGroup({
parent: parent,
doctype: "Bank Transaction",
on_change: () => {
frm.set_value("filters", JSON.stringify(filter_group.get_filters()));
},
});

filter_group.add_filters_to_filter_group(filters);

if (frm.doc.docstatus === 1) {
parent.find(".filter-action-buttons").remove();
parent.find(".divider").remove();
parent.find(".remove-filter").remove();
parent.find(".form-control").prop("disabled", true);
}
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
{
"actions": [],
"creation": "2025-10-23 13:00:49.226504",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"account_settings_section",
"bank_account",
"target_account",
"disabled",
"filters_section",
"filter_description",
"filters",
"filter_area",
"amended_from"
],
"fields": [
{
"fieldname": "bank_account",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Bank Account",
"link_filters": "[[\"Bank Account\",\"is_company_account\",\"=\",1],[\"Bank Account\",\"account\",\"is\",\"set\"]]",
"options": "Bank Account",
"reqd": 1
},
{
"fieldname": "account_settings_section",
"fieldtype": "Section Break",
"label": "Account Settings"
},
{
"fieldname": "target_account",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Target Account",
"options": "Account",
"reqd": 1
},
{
"fieldname": "filters_section",
"fieldtype": "Section Break",
"label": "Filters"
},
{
"fieldname": "filters",
"fieldtype": "Code",
"hidden": 1,
"label": "Filters",
"options": "JSON",
"read_only": 1
},
{
"fieldname": "filter_area",
"fieldtype": "HTML"
},
{
"fieldname": "filter_description",
"fieldtype": "HTML",
"options": "A <b>Journal Entry</b> is created automatically, when a submitted <b>Bank Transaction</b> matches these filters.<br>Please define set these filters as strictly as possible to avoid accounting errors.<br><br>"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Bank Reconciliation Rule",
"print_hide": 1,
"read_only": 1,
"search_index": 1
},
{
"allow_on_submit": 1,
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2026-01-27 21:55:21.191776",
"modified_by": "Administrator",
"module": "Klarna Kosma Integration",
"name": "Bank Reconciliation Rule",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"cancel": 1,
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1,
"write": 1
}
],
"row_format": "Dynamic",
"rows_threshold_for_grid_search": 20,
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright (c) 2025, ALYF GmbH and contributors
# For license information, please see license.txt

import frappe
from frappe import _
from frappe.exceptions import ValidationError
from frappe.model.document import Document

from banking.exceptions import CurrencyMismatchError


class NoFiltersError(ValidationError):
pass


class BankReconciliationRule(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from frappe.types import DF

amended_from: DF.Link | None
bank_account: DF.Link
disabled: DF.Check
filters: DF.Code | None
target_account: DF.Link

# end: auto-generated types
def validate(self):
self.validate_account_currencies()
self.validate_filters()

def validate_account_currencies(self):
bank_account = frappe.db.get_value("Bank Account", self.bank_account, "account")
bank_account_currency = frappe.db.get_value("Account", bank_account, "account_currency")
target_account_currency = frappe.db.get_value("Account", self.target_account, "account_currency")

if bank_account_currency != target_account_currency:
frappe.throw(
_("Bank Account and Target Account need to be in the same currency!"), CurrencyMismatchError
)

def validate_filters(self):
# self.filters is a code field with json, so it has "[]" when empty
if not self.filters or len(self.filters) <= 2:
frappe.throw(_("Please define at least one filter!"), NoFiltersError)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) 2025, ALYF GmbH and contributors
// For license information, please see license.txt

frappe.listview_settings["Bank Reconciliation Rule"] = {
add_fields: ["docstatus", "disabled"],
has_indicator_for_draft: true,

get_indicator: function (doc) {
// Submitted documents
if (doc.docstatus === 1) {
if (doc.disabled === 1) {
return [__("Disabled"), "orange", "disabled,=,1"];
} else {
return [__("Active"), "green", "docstatus,=,1"];
}
}

// Draft documents
if (doc.docstatus === 0) {
return [__("Draft"), "blue", "docstatus,=,0"];
}
},
};
Loading
Loading