feat(bank-integration): optional registry loading + "Load Data" button
The Excel import dialog on the Bank Transaction list gains an "Also load counterparties & purposes" checkbox (default on — preserves current behaviour). When unchecked, only Bank Transactions are created and the Bank Integration Customer/Supplier/Purpose registries are left alone. The Bank Integration form gains a "Load Data" button: pick an Excel preset + file and choose which registries to populate (Customers / Suppliers / Purposes checkboxes, like the standard data import). It runs the same upsert logic but creates no Bank Transactions — the file-based counterpart to kapital_bank's API-driven counterparty/purpose loaders. Backed by jey_erp.bank_integration.import_api.load_registries_from_excel. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e600a77806
commit
1d6bdc8424
|
|
@ -4,7 +4,7 @@ import json
|
|||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from frappe.utils import cint, flt
|
||||
|
||||
from jey_erp.bank_integration.excel_parser import parse_excel
|
||||
|
||||
|
|
@ -48,8 +48,14 @@ def parse_excel_for_preview(file_url, preset_name, bank_integration, bank_accoun
|
|||
|
||||
|
||||
@frappe.whitelist()
|
||||
def import_bulk_bt(txn_list, bank_integration, bank_account):
|
||||
"""Enqueue bulk Bank Transaction creation as a background job."""
|
||||
def import_bulk_bt(txn_list, bank_integration, bank_account, load_registries=1):
|
||||
"""Enqueue bulk Bank Transaction creation as a background job.
|
||||
|
||||
load_registries: when truthy, also upsert counterparties (→ Bank Integration
|
||||
Customer/Supplier by direction) and purpose keywords (→ Bank Integration
|
||||
Purpose) with status=New, exactly as before. When falsy, only Bank
|
||||
Transactions are created.
|
||||
"""
|
||||
if isinstance(txn_list, str):
|
||||
txn_list = json.loads(txn_list)
|
||||
|
||||
|
|
@ -61,6 +67,7 @@ def import_bulk_bt(txn_list, bank_integration, bank_account):
|
|||
bank_integration=bank_integration,
|
||||
bank_account=bank_account,
|
||||
user=user,
|
||||
load_registries=cint(load_registries),
|
||||
queue="default",
|
||||
timeout=600,
|
||||
)
|
||||
|
|
@ -68,8 +75,8 @@ def import_bulk_bt(txn_list, bank_integration, bank_account):
|
|||
return {"success": True, "enqueued": True, "total": len(txn_list)}
|
||||
|
||||
|
||||
def _process_bulk_bt_import(txn_list, bank_integration, bank_account, user):
|
||||
"""Background job: create BT + upsert registry rows for each transaction."""
|
||||
def _process_bulk_bt_import(txn_list, bank_integration, bank_account, user, load_registries=1):
|
||||
"""Background job: create BT (+ optionally upsert registry rows) for each transaction."""
|
||||
frappe.set_user(user)
|
||||
|
||||
bi = frappe.get_doc("Bank Integration", bank_integration)
|
||||
|
|
@ -102,6 +109,7 @@ def _process_bulk_bt_import(txn_list, bank_integration, bank_account, user):
|
|||
drcr = (txn.get("drcr") or "D").upper()
|
||||
purpose = (txn.get("purpose") or "").strip()
|
||||
|
||||
if load_registries:
|
||||
if counterparty:
|
||||
if drcr == "C":
|
||||
if _upsert_customer(counterparty, contr_voen, contr_iban, bank_integration):
|
||||
|
|
@ -258,3 +266,48 @@ def update_bank_account_bi_default(bank_account, bi_type, bi_name):
|
|||
}, update_modified=False)
|
||||
frappe.db.commit()
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def load_registries_from_excel(file_url, preset_name, bank_integration,
|
||||
load_customers=1, load_suppliers=1, load_purposes=1):
|
||||
"""Parse an Excel statement and upsert ONLY the selected reference registries
|
||||
(Bank Integration Customer / Supplier / Purpose) — no Bank Transactions.
|
||||
This is the "Load Data" action on the Bank Integration form, the file-based
|
||||
counterpart to kapital_bank's API-driven counterparty/purpose loaders."""
|
||||
load_customers = cint(load_customers)
|
||||
load_suppliers = cint(load_suppliers)
|
||||
load_purposes = cint(load_purposes)
|
||||
if not (load_customers or load_suppliers or load_purposes):
|
||||
return {"success": False, "message": _("Select at least one type to load.")}
|
||||
|
||||
transactions = parse_excel(file_url, preset_name)
|
||||
|
||||
new_c = new_s = new_p = 0
|
||||
for txn in transactions:
|
||||
counterparty = (txn.get("counterparty") or "").strip()
|
||||
contr_voen = (txn.get("contr_voen") or "").strip()
|
||||
contr_iban = (txn.get("contr_iban") or "").strip()
|
||||
drcr = (txn.get("drcr") or "D").upper()
|
||||
purpose = (txn.get("purpose") or "").strip()
|
||||
|
||||
if counterparty:
|
||||
if drcr == "C" and load_customers:
|
||||
if _upsert_customer(counterparty, contr_voen, contr_iban, bank_integration):
|
||||
new_c += 1
|
||||
elif drcr == "D" and load_suppliers:
|
||||
if _upsert_supplier(counterparty, contr_voen, contr_iban, bank_integration):
|
||||
new_s += 1
|
||||
|
||||
if purpose and load_purposes:
|
||||
if _upsert_purpose(purpose, drcr, bank_integration):
|
||||
new_p += 1
|
||||
|
||||
frappe.db.commit()
|
||||
return {
|
||||
"success": True,
|
||||
"total_rows": len(transactions),
|
||||
"new_customers": new_c,
|
||||
"new_suppliers": new_s,
|
||||
"new_purposes": new_p,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ frappe.ui.form.on('Bank Integration', {
|
|||
refresh(frm) {
|
||||
if (frm.is_new()) return;
|
||||
|
||||
// === Load Data (file-based, registry-only) ===
|
||||
frm.add_custom_button(__('Load Data'), () => {
|
||||
_show_load_data_dialog(frm);
|
||||
});
|
||||
|
||||
// === Accounts group ===
|
||||
frm.add_custom_button(__('Match accounts to Bank Accounts'), () => {
|
||||
_safe(frm, () => _call(frm, 'jey_erp.bank_integration.matching.match_similar_accounts',
|
||||
|
|
@ -133,3 +138,91 @@ function _call(frm, method, args, onSuccess) {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
function _show_load_data_dialog(frm) {
|
||||
const d = new frappe.ui.Dialog({
|
||||
title: __('Load Data from Excel'),
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'preset',
|
||||
fieldtype: 'Link',
|
||||
options: 'Bank Integration Excel Preset',
|
||||
label: __('Excel Preset'),
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'file_url',
|
||||
fieldtype: 'Attach',
|
||||
label: __('Excel File'),
|
||||
reqd: 1,
|
||||
},
|
||||
{ fieldname: 'sec_what', fieldtype: 'Section Break', label: __('What to load') },
|
||||
{
|
||||
fieldname: 'load_customers',
|
||||
fieldtype: 'Check',
|
||||
label: __('Customers (incoming counterparties)'),
|
||||
default: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'load_suppliers',
|
||||
fieldtype: 'Check',
|
||||
label: __('Suppliers (outgoing counterparties)'),
|
||||
default: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'load_purposes',
|
||||
fieldtype: 'Check',
|
||||
label: __('Purpose keywords'),
|
||||
default: 1,
|
||||
},
|
||||
],
|
||||
primary_action_label: __('Load'),
|
||||
primary_action(values) {
|
||||
if (!values.load_customers && !values.load_suppliers && !values.load_purposes) {
|
||||
frappe.msgprint({
|
||||
title: __('Nothing Selected'),
|
||||
indicator: 'orange',
|
||||
message: __('Pick at least one type to load.'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
d.disable_primary_action();
|
||||
frappe.call({
|
||||
method: 'jey_erp.bank_integration.import_api.load_registries_from_excel',
|
||||
args: {
|
||||
file_url: values.file_url,
|
||||
preset_name: values.preset,
|
||||
bank_integration: frm.doc.name,
|
||||
load_customers: values.load_customers ? 1 : 0,
|
||||
load_suppliers: values.load_suppliers ? 1 : 0,
|
||||
load_purposes: values.load_purposes ? 1 : 0,
|
||||
},
|
||||
callback(r) {
|
||||
d.enable_primary_action();
|
||||
if (!r.message || r.message.success === false) {
|
||||
frappe.msgprint({
|
||||
title: __('Error'),
|
||||
indicator: 'red',
|
||||
message: (r.message && r.message.message) || __('Unknown error'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
d.hide();
|
||||
const res = r.message;
|
||||
frappe.msgprint({
|
||||
title: __('Data Loaded'),
|
||||
indicator: 'green',
|
||||
message: __('From {0} rows — new customers: {1}, suppliers: {2}, purposes: {3}',
|
||||
[res.total_rows, res.new_customers, res.new_suppliers, res.new_purposes]),
|
||||
});
|
||||
frm.reload_doc();
|
||||
},
|
||||
error() {
|
||||
d.enable_primary_action();
|
||||
frappe.msgprint({ title: __('Error'), indicator: 'red', message: __('Network error') });
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
d.show();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,13 @@ const BIExcelImport = {
|
|||
label: __('Excel File'),
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'load_registries',
|
||||
fieldtype: 'Check',
|
||||
label: __('Also load counterparties & purposes'),
|
||||
default: 1,
|
||||
description: __('Extract unique counterparties and purpose keywords from the file into the Bank Integration registries (status: New).'),
|
||||
},
|
||||
],
|
||||
primary_action_label: __('Parse'),
|
||||
primary_action(values) {
|
||||
|
|
@ -251,6 +258,7 @@ const BIExcelImport = {
|
|||
txn_list: JSON.stringify(selected),
|
||||
bank_integration: values.bank_integration,
|
||||
bank_account: values.bank_account,
|
||||
load_registries: values.load_registries ? 1 : 0,
|
||||
},
|
||||
error() {
|
||||
frappe.realtime.off('bi_bt_import_progress');
|
||||
|
|
|
|||
Loading…
Reference in New Issue