diff --git a/kapital_bank/bank_api.py b/kapital_bank/bank_api.py
index 037122d..a895da1 100644
--- a/kapital_bank/bank_api.py
+++ b/kapital_bank/bank_api.py
@@ -743,6 +743,335 @@ def create_unmapped_suppliers(settings_name=None):
return {"success": False, "message": str(e)}
+# ═══════════════════════════════════════════════════════════════════════════════
+# ACCOUNT / CARD → BANK ACCOUNT MATCHING & CREATION
+# ═══════════════════════════════════════════════════════════════════════════════
+
+@frappe.whitelist()
+def match_similar_accounts():
+ """Auto-match account mappings to ERPNext Bank Accounts by IBAN."""
+ try:
+ doc = frappe.get_doc("Kapital Bank Settings")
+
+ erp_bank_accounts = frappe.get_all(
+ "Bank Account",
+ fields=["name", "bank_account_no", "iban"],
+ )
+
+ # Build lookup: iban/bank_account_no → (name, account)
+ iban_index = {}
+ for ba in erp_bank_accounts:
+ if ba.iban:
+ iban_index[ba.iban.strip()] = ba
+ if ba.bank_account_no:
+ iban_index[ba.bank_account_no.strip()] = ba
+
+ matched = 0
+ total_processed = 0
+
+ for row in doc.account_mappings:
+ if row.bank_account or not row.iban:
+ continue
+ total_processed += 1
+
+ match = iban_index.get(row.iban.strip())
+ if match:
+ row.bank_account = match.name
+ # Fetch GL account from Bank Account
+ gl_account = frappe.db.get_value("Bank Account", match.name, "account")
+ if gl_account:
+ row.gl_account = gl_account
+
+ frappe.db.set_value("Kapital Bank Account", row.iban, {
+ "status": "Mapped",
+ "bank_account": match.name,
+ }, update_modified=False)
+ matched += 1
+
+ if matched > 0:
+ doc.save(ignore_permissions=True)
+
+ frappe.db.commit()
+ return {"success": True, "matched_count": matched, "total_processed": total_processed}
+
+ except Exception as e:
+ frappe.log_error(f"match_similar_accounts: {e}\n{frappe.get_traceback()}", "Kapital Bank Account Matching")
+ return {"success": False, "message": str(e)}
+
+
+@frappe.whitelist()
+def create_unmapped_accounts(settings_name=None):
+ """Create Bank Account records for unmapped accounts in account_mappings."""
+ try:
+ doc = frappe.get_doc("Kapital Bank Settings")
+
+ unmapped = []
+ for row in doc.account_mappings:
+ if not row.bank_account and row.iban:
+ unmapped.append(row)
+
+ if not unmapped:
+ return {"success": True, "created_count": 0, "message": "No unmapped accounts to create"}
+
+ default_bank = doc.default_bank
+ default_company = doc.default_company
+
+ created = 0
+ for row in unmapped:
+ try:
+ # Check if Bank Account already exists for this IBAN
+ existing = frappe.db.get_value("Bank Account", {"bank_account_no": row.iban}, "name")
+ if existing:
+ row.bank_account = existing
+ gl_account = frappe.db.get_value("Bank Account", existing, "account")
+ if gl_account:
+ row.gl_account = gl_account
+ frappe.db.set_value("Kapital Bank Account", row.iban, {
+ "status": "Mapped",
+ "bank_account": existing,
+ }, update_modified=False)
+ created += 1
+ continue
+
+ account_name = row.account_label or row.iban
+
+ ba = frappe.new_doc("Bank Account")
+ ba.account_name = account_name
+ ba.bank_account_no = row.iban
+ if default_bank:
+ ba.bank = default_bank
+ if default_company:
+ ba.company = default_company
+ ba.insert(ignore_permissions=True)
+
+ row.bank_account = ba.name
+ gl_account = frappe.db.get_value("Bank Account", ba.name, "account")
+ if gl_account:
+ row.gl_account = gl_account
+
+ frappe.db.set_value("Kapital Bank Account", row.iban, {
+ "status": "Mapped",
+ "bank_account": ba.name,
+ }, update_modified=False)
+ created += 1
+
+ except Exception as e:
+ frappe.log_error(f"create_unmapped_accounts: error for {row.iban}: {e}", "Kapital Bank Account Creation")
+
+ if created > 0:
+ doc.save(ignore_permissions=True)
+
+ frappe.db.commit()
+ return {"success": True, "created_count": created}
+
+ except Exception as e:
+ frappe.log_error(f"create_unmapped_accounts: {e}\n{frappe.get_traceback()}", "Kapital Bank Account Creation")
+ return {"success": False, "message": str(e)}
+
+
+@frappe.whitelist()
+def match_similar_cards():
+ """Auto-match card mappings to ERPNext Bank Accounts by account_number."""
+ try:
+ doc = frappe.get_doc("Kapital Bank Settings")
+
+ erp_bank_accounts = frappe.get_all(
+ "Bank Account",
+ fields=["name", "bank_account_no"],
+ )
+
+ # Build lookup: bank_account_no → Bank Account
+ acno_index = {}
+ for ba in erp_bank_accounts:
+ if ba.bank_account_no:
+ acno_index[ba.bank_account_no.strip()] = ba
+
+ matched = 0
+ total_processed = 0
+
+ for row in doc.card_mappings:
+ if row.bank_account or not row.account_number:
+ continue
+ total_processed += 1
+
+ match = acno_index.get(row.account_number.strip())
+ if match:
+ row.bank_account = match.name
+ gl_account = frappe.db.get_value("Bank Account", match.name, "account")
+ if gl_account:
+ row.gl_account = gl_account
+
+ frappe.db.set_value("Kapital Bank Card", {"account_number": row.account_number}, {
+ "status": "Mapped",
+ "bank_account": match.name,
+ }, update_modified=False)
+ matched += 1
+
+ if matched > 0:
+ doc.save(ignore_permissions=True)
+
+ frappe.db.commit()
+ return {"success": True, "matched_count": matched, "total_processed": total_processed}
+
+ except Exception as e:
+ frappe.log_error(f"match_similar_cards: {e}\n{frappe.get_traceback()}", "Kapital Bank Card Matching")
+ return {"success": False, "message": str(e)}
+
+
+@frappe.whitelist()
+def create_unmapped_cards(settings_name=None):
+ """Create Bank Account records for unmapped cards in card_mappings."""
+ try:
+ doc = frappe.get_doc("Kapital Bank Settings")
+
+ unmapped = []
+ for row in doc.card_mappings:
+ if not row.bank_account and row.account_number:
+ unmapped.append(row)
+
+ if not unmapped:
+ return {"success": True, "created_count": 0, "message": "No unmapped cards to create"}
+
+ default_bank = doc.default_bank
+ default_company = doc.default_company
+
+ created = 0
+ for row in unmapped:
+ try:
+ # Check if Bank Account already exists for this account_number
+ existing = frappe.db.get_value("Bank Account", {"bank_account_no": row.account_number}, "name")
+ if existing:
+ row.bank_account = existing
+ gl_account = frappe.db.get_value("Bank Account", existing, "account")
+ if gl_account:
+ row.gl_account = gl_account
+ frappe.db.set_value("Kapital Bank Card", {"account_number": row.account_number}, {
+ "status": "Mapped",
+ "bank_account": existing,
+ }, update_modified=False)
+ created += 1
+ continue
+
+ account_name = row.card_label or row.account_number
+
+ ba = frappe.new_doc("Bank Account")
+ ba.account_name = account_name
+ ba.bank_account_no = row.account_number
+ if default_bank:
+ ba.bank = default_bank
+ if default_company:
+ ba.company = default_company
+ ba.insert(ignore_permissions=True)
+
+ row.bank_account = ba.name
+ gl_account = frappe.db.get_value("Bank Account", ba.name, "account")
+ if gl_account:
+ row.gl_account = gl_account
+
+ frappe.db.set_value("Kapital Bank Card", {"account_number": row.account_number}, {
+ "status": "Mapped",
+ "bank_account": ba.name,
+ }, update_modified=False)
+ created += 1
+
+ except Exception as e:
+ frappe.log_error(f"create_unmapped_cards: error for {row.account_number}: {e}", "Kapital Bank Card Creation")
+
+ if created > 0:
+ doc.save(ignore_permissions=True)
+
+ frappe.db.commit()
+ return {"success": True, "created_count": created}
+
+ except Exception as e:
+ frappe.log_error(f"create_unmapped_cards: {e}\n{frappe.get_traceback()}", "Kapital Bank Card Creation")
+ return {"success": False, "message": str(e)}
+
+
+# ═══════════════════════════════════════════════════════════════════════════════
+# PURPOSE LOADING FROM STATEMENTS
+# ═══════════════════════════════════════════════════════════════════════════════
+
+@frappe.whitelist()
+def load_purposes_from_statements(from_date, to_date, login_name=None):
+ """Load unique purposes from account statements and add new ones to purpose_mappings."""
+ try:
+ doc = frappe.get_doc("Kapital Bank Settings")
+
+ # Collect existing purpose keywords (lowercased for comparison)
+ existing_keywords = {
+ row.purpose_keyword.lower()
+ for row in doc.purpose_mappings
+ if row.purpose_keyword
+ }
+
+ # Get all registered accounts
+ accounts = frappe.get_all(
+ "Kapital Bank Account",
+ fields=["name", "cust_ac_no"],
+ )
+
+ if not accounts:
+ return {"success": True, "added_count": 0, "message": "No accounts in registry"}
+
+ client = BIRBankClient(login_name)
+ all_purposes = set()
+
+ for account in accounts:
+ if not account.cust_ac_no:
+ continue
+ try:
+ resp = client.get(
+ "/v2/statement/account",
+ params={
+ "accountNumber": account.cust_ac_no,
+ "fromDate": _fmt_date(from_date),
+ "toDate": _fmt_date(to_date),
+ }
+ )
+ if not resp.ok:
+ continue
+
+ data = resp.json()
+ resp_code = data.get("response", {}).get("code", "?")
+ if resp_code != "0":
+ continue
+
+ statement_list = (
+ data.get("responseData", {})
+ .get("operations", {})
+ .get("statementList", [])
+ )
+
+ for txn in statement_list:
+ purpose = (txn.get("purpose") or "").strip()
+ if purpose:
+ all_purposes.add(purpose)
+
+ except Exception:
+ continue
+
+ # Add new purposes not already in the mapping table
+ added = 0
+ for purpose in sorted(all_purposes):
+ if purpose.lower() not in existing_keywords:
+ doc.append("purpose_mappings", {
+ "purpose_keyword": purpose,
+ })
+ existing_keywords.add(purpose.lower())
+ added += 1
+
+ if added > 0:
+ doc.save(ignore_permissions=True)
+ frappe.db.commit()
+
+ return {"success": True, "added_count": added, "total_found": len(all_purposes)}
+
+ except Exception as e:
+ frappe.log_error(f"load_purposes_from_statements: {e}\n{frappe.get_traceback()}", "Kapital Bank Purpose Loading")
+ return {"success": False, "message": str(e)}
+
+
# ═══════════════════════════════════════════════════════════════════════════════
# RAW API ACCESSORS (for reports / manual use)
# ═══════════════════════════════════════════════════════════════════════════════
@@ -1032,7 +1361,7 @@ def _build_import_lookups(settings):
purpose_rules = []
for row in settings.purpose_mappings:
if row.purpose_keyword and row.paid_from and row.paid_to:
- purpose_rules.append((row.purpose_keyword.lower(), row.paid_from, row.paid_to))
+ purpose_rules.append((row.purpose_keyword.lower(), row.paid_from, row.paid_to, getattr(row, "payment_type", None) or ""))
voen_to_party = {}
name_to_party = {}
@@ -1080,7 +1409,9 @@ def _import_one_transaction(txn_data, settings, purpose_rules, purpose_threshold
paid_from = paid_to = None
best_purpose_score = 0
- for keyword, pf, pt in purpose_rules:
+ for keyword, pf, pt, rule_payment_type in purpose_rules:
+ if rule_payment_type and rule_payment_type != payment_type:
+ continue
if keyword in purpose_lower:
paid_from = pf
paid_to = pt
diff --git a/kapital_bank/client/bank_transaction.js b/kapital_bank/client/bank_transaction.js
index 1aa114f..3a8841b 100644
--- a/kapital_bank/client/bank_transaction.js
+++ b/kapital_bank/client/bank_transaction.js
@@ -512,7 +512,7 @@ KBBTImport.import = {
showTransactionSelection: function(txns) {
let txnTable = '
';
- txnTable += '' +
+ txnTable += '' +
' | ' +
'' + __('Ref No') + ' | ' +
'' + __('Date') + ' | ' +
diff --git a/kapital_bank/client/payment_entry.js b/kapital_bank/client/payment_entry.js
index b6f7a9a..0c84831 100644
--- a/kapital_bank/client/payment_entry.js
+++ b/kapital_bank/client/payment_entry.js
@@ -470,7 +470,7 @@ KBImport.import = {
showTransactionSelection: function(txns) {
let txnTable = '';
- txnTable += '' +
+ txnTable += '' +
' | ' +
'' + __('Ref No') + ' | ' +
'' + __('Date') + ' | ' +
diff --git a/kapital_bank/kapital_bank/doctype/kapital_bank_account_mapping/kapital_bank_account_mapping.json b/kapital_bank/kapital_bank/doctype/kapital_bank_account_mapping/kapital_bank_account_mapping.json
index d86920f..19a1fe0 100644
--- a/kapital_bank/kapital_bank/doctype/kapital_bank_account_mapping/kapital_bank_account_mapping.json
+++ b/kapital_bank/kapital_bank/doctype/kapital_bank_account_mapping/kapital_bank_account_mapping.json
@@ -13,9 +13,10 @@
"fields": [
{
"fieldname": "iban",
- "fieldtype": "Data",
+ "fieldtype": "Link",
"in_list_view": 1,
"label": "IBAN",
+ "options": "Kapital Bank Account",
"reqd": 1
},
{
diff --git a/kapital_bank/kapital_bank/doctype/kapital_bank_purpose_mapping/kapital_bank_purpose_mapping.json b/kapital_bank/kapital_bank/doctype/kapital_bank_purpose_mapping/kapital_bank_purpose_mapping.json
index e784a1c..ff620a3 100644
--- a/kapital_bank/kapital_bank/doctype/kapital_bank_purpose_mapping/kapital_bank_purpose_mapping.json
+++ b/kapital_bank/kapital_bank/doctype/kapital_bank_purpose_mapping/kapital_bank_purpose_mapping.json
@@ -5,6 +5,7 @@
"engine": "InnoDB",
"field_order": [
"purpose_keyword",
+ "payment_type",
"column_break_1",
"paid_from",
"paid_to",
@@ -18,6 +19,13 @@
"label": "Purpose Keyword",
"reqd": 1
},
+ {
+ "fieldname": "payment_type",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Payment Type",
+ "options": "\nPay\nReceive"
+ },
{
"fieldname": "column_break_1",
"fieldtype": "Column Break"
@@ -27,16 +35,14 @@
"fieldtype": "Link",
"in_list_view": 1,
"label": "Paid From (Account)",
- "options": "Account",
- "reqd": 1
+ "options": "Account"
},
{
"fieldname": "paid_to",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Paid To (Account)",
- "options": "Account",
- "reqd": 1
+ "options": "Account"
},
{
"fieldname": "notes",
diff --git a/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.js b/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.js
index d8c2c4d..6844807 100644
--- a/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.js
+++ b/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.js
@@ -6,6 +6,30 @@ frappe.ui.form.on('Kapital Bank Settings', {
}, __('Kapital Bank'));
// ── Accounts buttons ──────────────────────────────────────────────────
+ frm.add_custom_button(__('Match accounts to Bank Accounts'), () => {
+ if (frm.is_dirty()) {
+ frappe.confirm(
+ __('Document contains unsaved changes. Save before performing the operation?'),
+ () => frm.save().then(() => _match_accounts(frm)),
+ () => frappe.show_alert({ message: __('Operation canceled. Please save the document first.'), indicator: 'red' }, 5)
+ );
+ } else {
+ _match_accounts(frm);
+ }
+ }, __('Accounts'));
+
+ frm.add_custom_button(__('Create Bank Accounts'), () => {
+ if (frm.is_dirty()) {
+ frappe.confirm(
+ __('Document contains unsaved changes. Save before performing the operation?'),
+ () => frm.save().then(() => create_unmapped_bank_accounts(frm)),
+ () => frappe.show_alert({ message: __('Operation canceled. Please save the document first.'), indicator: 'red' }, 5)
+ );
+ } else {
+ create_unmapped_bank_accounts(frm);
+ }
+ }, __('Accounts'));
+
frm.add_custom_button(__('Add unmapped accounts'), () => {
if (frm.is_dirty()) {
frm.save().then(() => add_unmapped_accounts(frm));
@@ -15,6 +39,30 @@ frappe.ui.form.on('Kapital Bank Settings', {
}, __('Accounts'));
// ── Cards buttons ────────────────────────────────────────────────────
+ frm.add_custom_button(__('Match cards to Bank Accounts'), () => {
+ if (frm.is_dirty()) {
+ frappe.confirm(
+ __('Document contains unsaved changes. Save before performing the operation?'),
+ () => frm.save().then(() => _match_cards(frm)),
+ () => frappe.show_alert({ message: __('Operation canceled. Please save the document first.'), indicator: 'red' }, 5)
+ );
+ } else {
+ _match_cards(frm);
+ }
+ }, __('Cards'));
+
+ frm.add_custom_button(__('Create Bank Accounts for cards'), () => {
+ if (frm.is_dirty()) {
+ frappe.confirm(
+ __('Document contains unsaved changes. Save before performing the operation?'),
+ () => frm.save().then(() => create_unmapped_bank_accounts_for_cards(frm)),
+ () => frappe.show_alert({ message: __('Operation canceled. Please save the document first.'), indicator: 'red' }, 5)
+ );
+ } else {
+ create_unmapped_bank_accounts_for_cards(frm);
+ }
+ }, __('Cards'));
+
frm.add_custom_button(__('Add unmapped cards'), () => {
if (frm.is_dirty()) {
frm.save().then(() => add_unmapped_cards(frm));
@@ -89,6 +137,19 @@ frappe.ui.form.on('Kapital Bank Settings', {
}
}, __('Suppliers'));
+ // ── Purpose buttons ───────────────────────────────────────────────────
+ frm.add_custom_button(__('Load purposes from statements'), () => {
+ if (frm.is_dirty()) {
+ frappe.confirm(
+ __('Document contains unsaved changes. Save before performing the operation?'),
+ () => frm.save().then(() => _load_purposes(frm)),
+ () => frappe.show_alert({ message: __('Operation canceled. Please save the document first.'), indicator: 'red' }, 5)
+ );
+ } else {
+ _load_purposes(frm);
+ }
+ }, __('Purpose'));
+
// ── Populate data tabs ────────────────────────────────────────────────
load_data_tabs(frm);
}
@@ -144,6 +205,165 @@ function _match_suppliers(frm) {
);
}
+// ═══════════════════════════════════════════════════════════════════════════════
+// MATCH / CREATE ACCOUNTS & CARDS
+// ═══════════════════════════════════════════════════════════════════════════════
+
+function _match_accounts(frm) {
+ frappe.confirm(
+ __('This will automatically match unmapped accounts with existing Bank Accounts by IBAN. Continue?'),
+ () => {
+ frappe.show_alert({ message: __('Matching accounts...'), indicator: 'blue' });
+ frappe.call({
+ method: 'kapital_bank.bank_api.match_similar_accounts',
+ callback(r) {
+ if (r.message && r.message.success) {
+ frappe.show_alert({
+ message: __('Matched {0} of {1} accounts', [r.message.matched_count, r.message.total_processed]),
+ indicator: 'green'
+ }, 5);
+ frm.reload_doc();
+ } else {
+ frappe.msgprint({ title: __('Error'), indicator: 'red', message: r.message ? r.message.message : __('Error matching accounts') });
+ }
+ }
+ });
+ }
+ );
+}
+
+function create_unmapped_bank_accounts(frm) {
+ frappe.confirm(
+ __('WARNING! This will create new Bank Account records for all unmapped accounts!
This action is irreversible. Are you sure?
'),
+ function() {
+ frappe.confirm(
+ __('Are you really sure? This cannot be undone.'),
+ function() {
+ frappe.call({
+ method: 'kapital_bank.bank_api.create_unmapped_accounts',
+ args: { 'settings_name': frm.doc.name },
+ callback: function(r) {
+ if (r.message && r.message.success) {
+ frappe.show_alert({
+ message: __('Created {0} new Bank Accounts', [r.message.created_count]),
+ indicator: 'green'
+ }, 6);
+ frm.reload_doc();
+ } else {
+ frappe.msgprint({
+ title: __('Error'),
+ indicator: 'red',
+ message: r.message ? r.message.message : __('Error creating Bank Accounts')
+ });
+ }
+ }
+ });
+ }
+ );
+ }
+ );
+}
+
+function _match_cards(frm) {
+ frappe.confirm(
+ __('This will automatically match unmapped cards with existing Bank Accounts by account number. Continue?'),
+ () => {
+ frappe.show_alert({ message: __('Matching cards...'), indicator: 'blue' });
+ frappe.call({
+ method: 'kapital_bank.bank_api.match_similar_cards',
+ callback(r) {
+ if (r.message && r.message.success) {
+ frappe.show_alert({
+ message: __('Matched {0} of {1} cards', [r.message.matched_count, r.message.total_processed]),
+ indicator: 'green'
+ }, 5);
+ frm.reload_doc();
+ } else {
+ frappe.msgprint({ title: __('Error'), indicator: 'red', message: r.message ? r.message.message : __('Error matching cards') });
+ }
+ }
+ });
+ }
+ );
+}
+
+function create_unmapped_bank_accounts_for_cards(frm) {
+ frappe.confirm(
+ __('WARNING! This will create new Bank Account records for all unmapped cards!
This action is irreversible. Are you sure?
'),
+ function() {
+ frappe.confirm(
+ __('Are you really sure? This cannot be undone.'),
+ function() {
+ frappe.call({
+ method: 'kapital_bank.bank_api.create_unmapped_cards',
+ args: { 'settings_name': frm.doc.name },
+ callback: function(r) {
+ if (r.message && r.message.success) {
+ frappe.show_alert({
+ message: __('Created {0} new Bank Accounts for cards', [r.message.created_count]),
+ indicator: 'green'
+ }, 6);
+ frm.reload_doc();
+ } else {
+ frappe.msgprint({
+ title: __('Error'),
+ indicator: 'red',
+ message: r.message ? r.message.message : __('Error creating Bank Accounts for cards')
+ });
+ }
+ }
+ });
+ }
+ );
+ }
+ );
+}
+
+// ═══════════════════════════════════════════════════════════════════════════════
+// LOAD PURPOSES FROM STATEMENTS
+// ═══════════════════════════════════════════════════════════════════════════════
+
+function _load_purposes(frm) {
+ const today = frappe.datetime.get_today();
+ const first_day = frappe.datetime.month_start(today);
+
+ const d = new frappe.ui.Dialog({
+ title: __('Load Purposes from Statements'),
+ fields: [
+ { fieldname: 'date_from', fieldtype: 'Date', label: __('Date From'), reqd: 1, default: first_day },
+ { fieldname: 'col_break', fieldtype: 'Column Break' },
+ { fieldname: 'date_to', fieldtype: 'Date', label: __('Date To'), reqd: 1, default: today },
+ ],
+ primary_action_label: __('Load'),
+ primary_action(values) {
+ d.hide();
+ frappe.show_alert({ message: __('Loading purposes from statements...'), indicator: 'blue' });
+ frappe.call({
+ method: 'kapital_bank.bank_api.load_purposes_from_statements',
+ args: { from_date: values.date_from, to_date: values.date_to },
+ callback(r) {
+ if (r.message && r.message.success) {
+ frappe.show_alert({
+ message: __('Added {0} new purposes (found {1} total)', [r.message.added_count, r.message.total_found]),
+ indicator: 'green'
+ }, 6);
+ if (r.message.added_count > 0) {
+ frm.reload_doc();
+ }
+ } else {
+ frappe.msgprint({
+ title: __('Error'),
+ indicator: 'red',
+ message: r.message ? r.message.message : __('Error loading purposes')
+ });
+ }
+ }
+ });
+ }
+ });
+ d.show();
+}
+
// ═══════════════════════════════════════════════════════════════════════════════
// LOAD DATA DIALOG
// ═══════════════════════════════════════════════════════════════════════════════
diff --git a/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.json b/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.json
index 9890667..db8ce80 100644
--- a/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.json
+++ b/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.json
@@ -9,6 +9,7 @@
"default_login",
"default_company",
"company_code",
+ "default_bank",
"general_section",
"similarity_threshold_customers",
"similarity_threshold_suppliers",
@@ -77,6 +78,12 @@
"fieldtype": "Data",
"label": "Company Code"
},
+ {
+ "fieldname": "default_bank",
+ "fieldtype": "Link",
+ "label": "Default Bank",
+ "options": "Bank"
+ },
{
"fieldname": "general_section",
"fieldtype": "Section Break",
diff --git a/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.py b/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.py
index fc3902e..1721da4 100644
--- a/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.py
+++ b/kapital_bank/kapital_bank/doctype/kapital_bank_settings/kapital_bank_settings.py
@@ -4,10 +4,33 @@ from frappe.model.document import Document
class KapitalBankSettings(Document):
def on_update(self):
+ self._sync_account_statuses()
self._sync_customer_statuses()
self._sync_supplier_statuses()
self._sync_card_statuses()
+ def _sync_account_statuses(self):
+ """Reset Kapital Bank Account status to 'New' for accounts removed from account_mappings."""
+ mapped_ibans = {row.iban for row in self.account_mappings if row.iban}
+
+ orphaned = frappe.get_all(
+ "Kapital Bank Account",
+ filters={"status": "Mapped"},
+ fields=["name"],
+ )
+
+ for account in orphaned:
+ if account.name not in mapped_ibans:
+ frappe.db.set_value(
+ "Kapital Bank Account",
+ account.name,
+ {
+ "status": "New",
+ "bank_account": None,
+ },
+ update_modified=False,
+ )
+
def _sync_customer_statuses(self):
"""Reset Kapital Bank Customer status to 'New' for customers removed from customer_mappings."""
mapped_names = {row.kb_customer_name for row in self.customer_mappings if row.kb_customer_name}