Commit Graph

261 Commits

Author SHA1 Message Date
Ali ded4946c75 refactor(synonyms): move synonym data to JSON, keep matcher in Python
The header → Standard Field dictionary is data, not code, so it now
lives in header_synonyms.json next to its loader. Editing a synonym list
no longer requires touching Python — open the JSON, append a string,
done.

header_synonyms.py is now a thin loader (reads the JSON once at import
time) plus the matcher and the Azərbaycan→Latin transliteration table,
which stay in Python because they're behaviour, not data.
2026-05-18 12:54:25 +00:00
Ali 357305a687 refactor(excel-parser): extract header synonyms to its own module
The _HEADER_SYNONYMS dict, the Azərbaycan-to-Latin translation table, and
the matcher helpers were sitting in the middle of excel_parser.py making
the file harder to scan. Moved them to a dedicated header_synonyms.py
with a docstring explaining how to extend it.

Public API of the new module is just guess_standard_field(header) and
HEADER_SYNONYMS — that's the surface excel_parser actually uses.
2026-05-18 12:51:55 +00:00
Ali 381aebdbdb fix(file-format): auto-detect columns on sample upload, quiet alerts
Two changes that drop the manual step from the column-mapping setup:

- Auto-detect now runs implicitly when the user attaches a Sample Excel
  File. The Auto-detect Columns Button field is removed (Preview Sample
  stays — it answers a different question). Auto-detect only fires when
  the Column Mappings table is empty; a user-edited table is never
  overwritten silently.
- Replaced the modal "Auto-detect Complete" / "Sample File Error" /
  "Empty Header Row" msgprints with small bottom-right show_alert
  toasts. The result is one line — "Auto-mapped 9 of 11 columns" —
  with green/orange/red indicator instead of a full-screen dialog.

The synonym dictionary used by detection lives in code at
jey_erp/bank_integration/excel_parser.py (_HEADER_SYNONYMS).
2026-05-18 12:50:31 +00:00
Ali 54ac6288fd fix(file-format): move Preview/Auto-detect buttons into the tab body
The two helpers used to live in the form toolbar under a 'File Format'
group, which added noise to the already busy header (Match/Create
buttons for accounts/customers/suppliers/purposes plus Load Data are
also there).

They are now native Button fields inside the File Format tab, sitting in
a two-column row directly under the Sample Excel File attach widget:

  [ Sample Excel File: ... ]
  [ Preview Sample ]   [ Auto-detect Columns ]
  [ Format / Column Mappings sections ]

Both buttons are gated by depends_on=sample_file, so they only appear
when there's actually a file to operate on — same gating as before, just
contextually placed next to the upload instead of in the global toolbar.
2026-05-18 12:45:35 +00:00
Ali 7f24bd6b2a fix(file-format): hide rarely-used 'Use Currency From File' toggle
Multi-currency Bank Accounts (one account holding several currencies in
parallel) exist with some western banks like Revolut/Wise, but in AZ the
convention is one Bank Account per currency. The toggle was extra noise
on the File Format tab for every user.

Kept the field and the underlying parse/import logic intact (hidden=1
only). Removing the hidden flag re-exposes it if a real multi-currency
case ever shows up.
2026-05-18 12:42:57 +00:00
Ali 408102117c fix(workspace): insert importer link into ERPNext Invoicing Banking card
Replaces the standalone Bank Statements workspace (incorrect approach —
ERPNext doesn't expose top-level apps as tiles automatically) with a
runtime insertion of a single 'Bank Statement Importer' link into the
existing 'Banking' card on the ERPNext-owned Invoicing workspace.

Editing the workspace at runtime via after_migrate (not by overriding
its JSON) is intentional: ERPNext resyncs that JSON on every `bench
migrate`, so a file-level patch would be reverted. after_migrate runs
AFTER the resync, so the inserted link stays.

The insert is idempotent — it skips when the link is already present,
when the Banking card break isn't there, or when Bank Statement Importer
DocType doesn't exist yet.
2026-05-18 12:24:53 +00:00
Ali c6c1394af6 fix(workspace): nest Bank Statements under Accounting
User feedback: a standalone top-level workspace doesn't surface inside
the existing ERPNext module tiles. Putting it under Accounting (next to
Financial Reports) matches user mental model — bank statement work is
already a financial activity.

After migrate, Bank Statements shows up as a sub-page in the Accounting
sidebar group, alongside Financial Reports.
2026-05-18 12:05:27 +00:00
Ali 5e434c9113 feat(workspace): add Bank Statements workspace
A discoverable home for the import flow — until now Bank Statement
Importer, the registries, and the BRT had no workspace entry and were
only reachable by typing URLs.

The workspace groups everything into three cards:
  Setup       — Bank Statement Importer, Bank, Bank Account
  Daily Use   — Bank Transaction, Bank Reconciliation Tool
  Registry    — Counterparties (Customers/Suppliers), Purpose Keywords

Plus three coloured shortcut tiles for the most-used entry points.
2026-05-18 11:55:42 +00:00
Ali 6f8e9d40a2 refactor(bank-integration): rename DocType to Bank Statement Importer
The original "Bank Integration" name was too broad — this is specifically
a configurable Excel statement importer, not a generic bank API
integration. The new name lines up with the per-bank, per-format profile
the doctype actually represents.

Scope is narrow on purpose: only the main DocType is renamed. The
internal mapping/registry doctypes (Bank Integration Customer, Supplier,
Purpose, Customer Mapping, Supplier Mapping, Transaction Mapping,
Account Mapping, Standard Field, Excel Preset, Excel Column Mapping)
keep their tech names — they are not directly user-facing and renaming
them would multiply the migration surface for no UX gain.

Changes:
- Folder bank_integration/ moved to bank_statement_importer/, files
  renamed alongside; Python class is now BankStatementImporter
- All Python callers (excel_parser, import_api, create_reconcile,
  creation, matching, migrations) call frappe.get_doc("Bank Statement
  Importer", …); the parenttype string in child-table queries follows
- JS form handler, both list views (Bank Transaction, BRT) updated,
  including the "BiType::Name" mapping-source key used by BRT
- Registry doctypes (Customer, Supplier, Purpose) updated to link to
  Bank Statement Importer in their parent_bank_integration field
- Bank Account.bank_integration_type custom field options now offer
  "Bank Statement Importer"
- pre_model_sync patch renames the existing DocType, rewrites Bank
  Account.bank_integration_type stored values, and patches the Custom
  Field options in-place so the rest of the migrate run is consistent.
  Idempotent: skips when the new name already exists or the old one
  doesn't.

The Python module path jey_erp.bank_integration.* is intentionally
unchanged so existing imports (and any external integrations) keep
working without touching every from-import statement.
2026-05-18 11:54:41 +00:00
Ali 05ee98cc0e feat(bank-integration): inline preview and auto-detect for File Format
Two new helpers on the Bank Integration form, shown when a Sample File
is attached:

- "Preview Sample" opens a modal with the header row + first 20 data
  rows, with badges on each column showing the currently mapped Standard
  Field (or "unmapped"). Helps spot wrong Header Row / column drift
  before importing real statements.
- "Auto-detect Columns" reads the sample headers and replaces the
  Column Mappings table with a best-effort guess based on a multilingual
  synonym list (English, Azərbaycan, Russian). Reports how many of the
  total columns matched and lists unmapped headers so the user can fill
  the rest manually. Asks for confirmation before overwriting an
  existing table.

Both helpers are backed by new whitelisted endpoints
parse_sample_preview and auto_detect_column_mappings in excel_parser.py.
2026-05-18 11:47:49 +00:00
Ali 04b16a6eea refactor(bank-integration): merge Excel Preset into File Format tab
File format settings (header row, amount mode, column mappings, sample
file, direction values, custom date format, currency-from-file) now live
on the Bank Integration record itself under a new "File Format" tab,
removing the extra hop through Bank Integration Excel Preset.

- Bank Integration JSON gains the File Format tab and column_mappings child
- Excel parser and import API read format from Bank Integration directly,
  drop the preset_name argument from public endpoints
- Load Data dialog (on Bank Integration) and Load From Excel dialog (on
  Bank Transaction list) drop the preset Link field; the chosen Bank
  Integration provides everything needed
- after_migrate patch copies fields and column mappings from an existing
  preset into each Bank Integration whose File Format is still empty
- Bank Integration Excel Preset doctype kept around for rollback but
  marked deprecated (System Manager only, in-form warning), removed from
  the fixtures list so fresh installs don't ship the Generic preset
2026-05-18 11:45:56 +00:00
Ali 4f0c26208b Drop Obligation Pacts section from Company custom fields
The E-Taxes Obligation Pact doctype is being removed; the in-form
list section + HTML field that hosted it on Company are no longer
needed. Cleanup of leftover Custom Field rows on existing sites is
handled by jey_wizard's v0_1_27 patch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 16:07:09 +00:00
Ali e81f4bc8e8 chore(sales-invoice): remove Won/Lost checkboxes
Drop the conditional Won/Lost Custom Fields and their JS/Python plumbing —
they should never be shown again. Adds a post-migrate patch to delete the
existing Custom Field records (and any stray Property Setters) from the DB.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 15:06:08 +00:00
Ali 1a6bd5649b chore: use native Azərbaycan spelling in user-facing strings
Replaces "Azeri" / "Azerbaijani" / "Azerbaijan" with the native form
"Azərbaycan" in labels, descriptions, comments, app description, and
docs. Field names and Python identifiers are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 10:56:52 +00:00
Ali a3ae46b978 fix(bank-integration): symmetric Azeri translit + VOEN-first dedup
Two related correctness fixes:

1. Asymmetric Azerbaijani normalization.
   consider_azeri_chars was only honoured by match_similar_customers/suppliers
   when populating registries. At Create & Reconcile time both the party-name
   lookup and the fuzzy purpose matcher compared raw lowercased strings, so a
   party matched via "Şirkət" ↔ "Sirket" translit at registry time would not
   resolve when the BT arrived, and a purpose rule keyed on "Mədaxil" would
   fall short of fuzzy threshold against "Medaxil".

   - name_to_party now indexes each row under strict / lowercase / translit /
     translit+lowercase variants based on its effective Case Mode + Azeri
     Translit (new per-row Select fields that override the globals, same
     pattern as case sensitivity).
   - _find_mapping_for_txn takes an `az` flag and applies translit to both
     keyword cache and txn purpose/party before comparison.

2. Customer / Supplier dedup ignored VOEN.
   create_unmapped_customers / create_unmapped_suppliers checked only by
   customer_name / supplier_name, so a registry record sharing a VOEN with an
   existing party under a slightly different name would create a duplicate.
   Now: tax_id is checked first, name as fallback; when an existing party is
   found, the mapping row is linked to it (instead of silently `continue`ing
   without linking). Response includes linked_count alongside created_count.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 10:45:39 +00:00
Ali 70215e200c fix(bank-integration): preview XSS, header completeness, ref-number safeguards, per-file currency
- Escape t.ref_no / t.counterparty / t.purpose / error fields with
  escape_html when rendering the preview and error-detail dialogs. A
  malicious bank statement (or counterparty-supplied purpose text)
  containing HTML could otherwise execute in the importer's session.
- Parser now refuses to proceed if any standard field required by the
  selected Amount Mode is missing from the file headers — instead of
  silently dropping every row with reason "amount" / "date".
- Reference Number is now a required mapping for all Amount Modes,
  preventing a class of silent duplication on re-import. parse_excel_for_
  preview also returns empty_ref_count and the preview surfaces a warning
  about future duplicate risk when some rows have an empty Reference
  Number on disk.
- Add 'Use Currency From File' checkbox on Excel Preset (default off).
  When on, BT.currency uses the parsed Currency column with fallback to
  the Bank Account's currency. import_bulk_bt now takes preset_name and
  the background job reads the flag.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:52:34 +00:00
Ali 5dd1296221 refactor(bank-integration): cleanup + robust amount parsing
- Delete mapping_resolver.py: dead code (nothing imports it; create_reconcile
  builds the indexes it needs directly).
- Simplify _create_journal_entry_for_brt: the if is_pay / else branches were
  identical because the matching transaction mapping is scoped by payment_type,
  so paid_from / paid_to already encode direction (from = credit side, to =
  debit side for both Pay and Receive). Removed the dead conditional, kept a
  comment explaining why no swap is needed.
- Add _parse_amount helper in excel_parser: handles currency text in the cell
  ('AZN 250', '250 AZN'), comma decimals ('250,00'), mixed thousands+decimal
  ('1,200.50' / '1.200,50'), accounting-style brackets ('(45.50)' → -45.50),
  and trailing minus ('250-'). Used for amount/debit/credit cells.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:40:50 +00:00
Ali e8e9359cb5 feat(bank-integration): case-insensitive party matching, with per-row override
Party-name lookup at Create & Reconcile previously used a strict (strip-only)
key, so a Bank Transaction whose bank_party_name differed in casing from the
registry record would never resolve a party. Now:

- 'Ignore Case in Party Matching' Check on Bank Integration (default ON) sets
  the global behaviour: party names are indexed under both their strict and
  lowercase forms, and the lookup tries both.
- 'Case Mode' Select on each Customer / Supplier Mapping row overrides the
  global for that specific party — blank inherits, "Ignore Case" / "Case
  Sensitive" forces the mode regardless of the global setting.

Honours the established "row > global" priority pattern used elsewhere.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:25:46 +00:00
Ali 44c3281cb9 fix(bank-integration): validate required Excel columns per Amount Mode
Importing into Mode 3 with a preset that had no Amount mapping silently
dropped every row and showed a confusing "No transactions found" message.
Two fixes:

- Preset.validate() now requires the Standard Fields the parser needs for
  the chosen mode (Mode 1: Date+Amount; Mode 2: Date+Debit+Credit; Mode 3:
  Date+Amount+Direction). Clear error on save lists exactly what's missing.
- When all rows drop due to unreadable amount, the import dialog now opens
  a helper modal pointing at the preset, instead of the generic empty
  message.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:23:16 +00:00
Ali bd5d773daa feat(bank-integration): user-defined direction aliases in Mode 3
'Single column + direction column' previously guessed direction from a
hardcoded EN/AZ alias list, silently falling back to "D" on anything
unrecognised. Now the preset exposes two comma-separated fields — Debit
Values and Credit Values — that the user fills with whatever their bank
prints (e.g. "DR, Debit, Məxaric" / "CR, Credit, Mədaxil"). The fields
are only visible/required for this amount mode and validated on save.

Rows with a Direction value that doesn't match either list are now
dropped with reason="direction" and the offending values are surfaced
in the import / Load Data dialogs so the user can copy them straight
into the preset.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:04:28 +00:00
Ali 3b1b43a57c refactor(bank-integration): drop Skip Rows After Header from Excel Preset
Only well-formed files (data rows immediately after the header) are expected;
Header Row alone is enough to point the parser at the start of data.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:53:57 +00:00
Ali 0672441be3 feat(bank-integration): Excel Preset rework — sample-driven columns, auto date format
- Sample File on Preset: upload once, headers populate the Excel Column
  Autocomplete in the mapping table; Clear Sample button reverts to free text.
- Date format: hidden behind "Use Custom Date Format" checkbox; parser tries
  24 common formats (ISO/EU/US × -./\\\\ × with/without time) automatically.
  Surfaces a clear "open the preset and enable custom format" dialog on
  unparseable dates, instead of silently dropping rows.
- Standard Field options renamed from machine codes (date, reference_number…)
  to readable labels (Date, Reference Number…); parser maps labels → codes.
  Idempotent migration converts existing rows.
- Header matching is now case- and whitespace-insensitive.
- Direction-column aliases extended with Azerbaijani terms (Mədaxil/Məxaric/
  Daxil/Çıxıb/Gəlir/Xərc).
- cost_center in transaction_mappings filtered to is_group:0 in the form.
- Load Data result surfaces dropped_date / dropped_amount counts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:44:15 +00:00
Ali 751154efa8 feat(bank-integration): name Customer/Supplier registry records after their text name
Bank Integration Customer/Supplier used generic BIC-####/BIS-#### ids.
Switched to the Kapital Bank scheme: an autoname() controller method
sets the record name to customer_name / supplier_name (truncated to 140,
-N on collision since multiple Bank Integrations can hold the same
counterparty). The after_migrate pass renames any existing BIC-/BIS-/
BIP-#### records to their readable names.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:20:55 +00:00
Ali fcdfffbcf0 refactor(bank-integration): registries only via "Load Data"; resolve party at reconcile time
The Excel import dialog no longer has the "Also load counterparties &
purposes" checkbox — BT import now only creates Bank Transactions.
Registries (Bank Integration Customer/Supplier/Purpose) are populated
exclusively through the "Load Data" button on the Bank Integration form.

Since BTs are now always imported without a party, Create & Reconcile
resolves it: a BT's bank_party_name (the counterparty text from the
file) is looked up in the customer/supplier mappings, and if that
counterparty is mapped to an ERPNext party, the txn gets that party —
so counterparty-based transaction-mapping rules match and the created
PE/JE carries the party. A safety guard: a party is only attached to a
Payment Entry when one of the GL accounts is Receivable/Payable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:01:27 +00:00
Ali 42cac5a006 feat(bank-integration): fuzzy purpose matching in BRT Create & Reconcile
_find_mapping_for_txn now tries exact-substring purpose matches first
(unchanged), then falls back to a partial-ratio fuzzy match against
similarity_threshold_purpose — so a short/imprecise purpose keyword can
match somewhere inside a long bank-statement description. Order:
purpose+counterparty (exact) > purpose-only (exact) > purpose+
counterparty (fuzzy) > purpose-only (fuzzy) > counterparty-only >
fallback. Set the threshold to 100 to keep the old substring-only
behaviour. This makes similarity_threshold_purpose actually do something
in the BRT path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:22:40 +00:00
Ali cd162c1f89 feat(bank-integration): name purposes after the purpose text (like Kapital Bank)
Bank Integration Purpose used a generic BIP-#### id. Switched to the
Kapital Bank Purpose scheme: an autoname() controller method sets the
record name to the purpose_keyword text (truncated to 140, -N on
collision). A migration in after_migrate renames any existing BIP-####
records to their readable names (Link references update automatically).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 11:45:57 +00:00
Ali 8043b43875 fix(bank-integration): clearing a mapping row's ERP link resets the registry record
On save, a Bank Integration Customer/Supplier whose mapping row had its
erp_customer / erp_supplier cleared (not just whose row was deleted) is
now flipped back to status=New with mapped_customer/customer_group/
territory/payment_terms cleared — so a registry record never keeps a
phantom mapping.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 11:38:02 +00:00
Ali ac7be1021f fix(bank-integration): create party without a group when none is configured
The first-leaf fallback meant customers always landed in "Individual"
even when no Customer Group was chosen. Customer/Supplier/Territory
aren't actually mandatory in ERPNext, so now: a configured value is
validated (exists + leaf) and used; nothing configured -> the party is
created with an empty customer_group / supplier_group / territory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 11:05:04 +00:00
Ali 1abdedce20 feat(bank-integration): fall back to first leaf node when no group is configured + filter group fields
Creating customers/suppliers no longer requires a default Customer/
Supplier Group or Territory on the Bank Integration — when none is set,
the first leaf (non-group) node of the tree is used. A configured value
is still validated (must exist and be a leaf), with a clear message.
Also: the default-group / default-territory Link fields (and the
matching mapping child-table fields) now filter to is_group=0 so a group
node can't be selected in the first place.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 10:53:23 +00:00
Ali 94ce72e4fb fix(bank-integration): reject group-node Customer/Supplier Group / Territory upfront
Customer/Supplier require a leaf node for customer_group/supplier_group/
territory; selecting a group node makes ERPNext throw "Cannot select a
Group type ...". The upfront validation now also checks is_group and
returns a clear message before any record is touched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 10:45:21 +00:00
Ali 7e862e48e5 feat(bank-integration): populate the Data tab subtabs
The Accounts / Customers / Suppliers / Purposes subtabs under "Data"
were stuck on "Loading..." — nothing rendered into them. Added
get_bi_reference_data_list (paginated rows for each registry, scoped to
the Bank Integration record) and the client-side rendering in
bank_integration.js (tables with status pills, links to the underlying
records and their ERPNext mappings, plus a Refresh button per subtab) —
mirroring the kapital_bank_settings Data tab.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 10:40:23 +00:00
Ali 2dba6908b0 fix(bank-integration): clear validation message when default groups aren't set
Creating customers/suppliers/bank accounts from unmapped rows fell back
to "All Customer Groups" / "All Territories" / "All Supplier Groups",
which don't exist on localized sites — ERPNext then threw a cryptic
"Could not find Customer Group: All Customer Groups". Now creation
validates upfront and returns a clear message ("Set a Default Customer
Group on the Bank Integration ...") before touching any records. Same
for Territory, Supplier Group, and (for Bank Accounts) Company / Bank.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 10:31:34 +00:00
Ali 1d6bdc8424 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>
2026-05-11 13:49:10 +00:00
Ali e600a77806 fix(bank-integration): keep "use original description" hidden state across Action changes
The previous attempt double-set custom_purpose's hidden property
(visible in the forEach, then hidden in a follow-up if) and relied on
refresh_fields, which didn't re-render the hidden state. Now
_applyModeVisibility computes the final hidden value for each field in
one pass — folding the "Use original description" checkbox into the
custom_purpose decision — and calls refresh() on each field explicitly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:30:44 +00:00
Ali 6c395f9e3b fix(bank-integration): no checkbox flicker + keep "use original description" on mode change
Two BRT polish fixes:
- The checkbox column was added by destroying and rebuilding the
  DataTable after each "Get Unreconciled Entries", which visibly redrew
  the rows. Replaced with a wholesale override of DataTableManager's
  get_datatable that creates the table with checkboxColumn:true from the
  start and injects the toolbar there — no rebuild, no flicker. The
  render-event polling is gone.
- _applyModeVisibility unconditionally un-hid "Custom Purpose Keyword"
  when leaving "Documents & Reconcile" mode, overriding the effect of
  the "Use original description" checkbox. Now it re-applies the
  checkbox state after the mode-driven visibility pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:21:52 +00:00
Ali 71e2c06a35 fix(bank-integration): port kapital_bank's proven BRT dialog into jey_erp
The first jey_erp BRT implementation read row.name from dtm.transactions,
but those rows are arrays (per ERPNext's DataTableManager.format_row), so
the BT name was always undefined ("Could not resolve Bank Transaction
names"). Replaced it with a faithful port of kapital_bank's BRT
extension: the same checkbox column, "Create & Reconcile" toolbar, and
3-mode dialog (Both / Mappings Only / Documents & Reconcile), reading
the BT name from the row array the way kb did.

The dialog gains a "Mapping Source" select (defaulting from the Bank
Account's hidden bank_integration field, written back on submit). For a
Bank Integration source it calls the new
jey_erp.bank_integration.create_reconcile.create_purpose_mappings (a
port of kapital_bank/mapping.py adapted for the multi-record Bank
Integration doctype and Bank Integration Purpose); for a Kapital Bank
Settings source it calls kapital_bank.mapping.create_purpose_mappings
directly, leaving kb's proven backend untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:10:11 +00:00
Ali b4b1d1b9b6 fix(bank-integration): re-add BRT checkbox column on every "Get Unreconciled Entries"
The checkbox column + Create & Reconcile toolbar setup was on the form's
`refresh` event, which fires only once. On a second "Get Unreconciled
Entries" ERPNext rebuilds the DataTableManager without the checkbox
column and the polling interval never restarted. Moved the enhancement
to the `render` event (fires on every rebuild), matching the pattern
the kapital_bank BRT extension used. The prototype patch that adds the
"Bank Transaction" column stays on `refresh` since it is idempotent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:02:53 +00:00
Ali f1876b6088 reverse changes push 2026-05-11 12:45:28 +00:00
Ali 3c1dee9a72 feat(bank-integration): redesign Excel Preset UX with Detect & preview
Standard Field is now a Link to a new "Bank Integration Standard Field"
doctype (autoname=Prompt). Each entry holds a display name like "Date" /
"Reference Number" — the field that the user picks in Column Mappings —
and a `field_name` column with the technical key used by the parser.
Twelve standard fields ship as a fixture. The change replaces the old
Select with technical names (date, reference_number, debit, ...) which
were noisy in the UI.

Excel Preset gets two new fields:
  - sample_file (Attach) — user uploads a sample of their bank's xlsx
  - preview_html (HTML) — first 5 rows are rendered after detection

A "Detect Columns from Sample" custom button on the form calls a new
whitelisted endpoint detect_columns_from_sample() that reads the header
row + first 5 data rows. The Column Mappings table is repopulated with
one row per Excel header (Standard Field left empty for the user to
fill in), and the preview HTML shows what the parser actually sees so
the user can validate header_row / skip_rows. After detection the
sample File doc is removed via delete_sample_file().

excel_parser now resolves row.standard_field through Standard Field
to obtain the technical field_name. The Generic Bank Statement fixture
is updated to reference the new Link values.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 16:53:10 +00:00
Ali f35b19cf14 feat(bank-integration): universal Excel-based bank statement import
Adds 10 doctypes (Bank Integration + 4 child mapping tables + 3
registries + Excel Preset + Excel Column Mapping), 7 backend modules
(parser, import API, matching, creation, mapping resolver, create &
reconcile, migrations), 3 JS files, and a Generic Excel preset fixture.

Two hidden Dynamic Link custom fields on Bank Account
(bank_integration_type / bank_integration) point to either the new
Bank Integration record or the existing Kapital Bank Settings, letting
the universal Create & Reconcile in BRT read mappings from either source
through one resolver. Both apps now share an "Import From..." dropdown
on Bank Transaction list.

Plan and rationale: BANK_INTEGRATION_PLAN.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 16:23:44 +00:00
Ali cae1de7682 feat(landed-cost-voucher): item-driven taxes table with vendor invoice import
Replaces the free-text description in Landed Cost Taxes and Charges with a
service-item link, drives expense_account from a new Item.default_landed_cost_account,
adds an in-form "Get Items from Vendor Invoices" button, and validates totals
against vendor invoices on submit. Also fixes ERPNext's no-op items_remove
(its `this.trigger` call hits a non-existent method) so applicable_charges
recalculate correctly when rows are deleted from items or taxes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 16:01:41 +00:00
Ali 91268cc466 feat(default-data): auto-sync leave types and company defaults on migrate
Mirrors the salary-component pattern for two more pieces of master data:

- sync_leave_types(): full upsert from default_data/leave_types.json.
  Creates missing, updates existing, deletes those no longer in JSON.
  Leave Type has no `disabled` field, so when a deletion fails due to
  linked Leave Allocation/Application records, the sync logs and skips
  (vs. marking disabled like Salary Component does).

- sync_company_defaults(): for every Company, resolves each account_number
  in default_data/company_defaults.json to the company-scoped Account
  name and sets the corresponding Company field. Non-destructive: if
  the company has no account with that number, the field is left as-is.

Both hooked into after_migrate_combined after sync_salary_components,
since Leave Type's earning_component references a Salary Component.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 12:55:27 +00:00
Ali eef0c82032 chore(default-data): refresh salary components and leave types from accountant
Updated bookkeeper-supplied master data:
- leave_types.json: leave types restructured per AZ Labor Code articles
  (Maddə 95, 130.e, 143.1.4, …) — names now reference statute, flags
  adjusted accordingly.
- salary_components.json: "Keyfiim bonusu" renamed to "Premiya";
  obsolete disabled components removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 12:48:15 +00:00
Ali 408bacfc84 feat(salary-components): auto-sync from default_data on migrate
Adds sync_salary_components() — full upsert from default_data JSON:
creates missing components, updates existing ones (formula, condition,
flags, accounts), and deletes those no longer in the JSON. If a
component cannot be deleted due to linked records, it is marked
disabled instead. Idempotent and safe to re-run.

Hooked into after_migrate_combined so every "bench migrate" picks up
the latest formulas — single source of truth is the JSON file.

Replaced default_data/salary_components.json with the current
production export (14 components, formulas use the f(base) + g(LE)
pattern for separate ƏH/MK accounting).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 12:45:11 +00:00
Ali 3aa4e61a1d perf: idempotent bulk update for custom fields on migrate
Replace per-field delete+create loop (which forced ALTER TABLE on all 222
custom fields every migrate) with idempotent bulk update that only writes
when a field's definition actually changed. Saves with ignore_version=True
to bypass the Frappe Version diff bug that crashes on certain int values.

Also remove the top-level create_custom_fields() call so the function only
runs from after_migrate, not on every module import.

Optional JEY_ERP_FORCE_RECREATE_FIELDS=1 env var preserves the old
delete+create path for development.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 10:55:21 +00:00
Ali 511e91cbf1 new translations 2026-04-24 20:29:53 +00:00
Ali 5d1e2adee3 feat(company): add E-Taxes reference sections to Tax Policy tab
Adds six new collapsible sections at the very end of the Tax Policy
tab on Company form, each with an HTML field for a reference-data
table rendered by invoice_az/client/company.js:
- E-Taxes Objects
- Cash Registers
- POS Terminals
- Bank Accounts
- Obligation Pacts
- Presented Certificates

Tax Closing Wizards moved to the bottom (insert_after changed from
accounting_method to presented_certificates_list_html) so wizards
sit under the new sections.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 13:59:41 +00:00
Ali cf4d66c2d2 feat: add Tax Free tab to Salary Slip with Tax Free Indicator DocType
Adds a Tax Free tab to Salary Slip with 4 nested sub-tabs (Vergi, MDSS,
İşsizlik sığorta, İcbari Tibbi Sığorta) rendered via the custom_subtabs
app. Each sub-tab has a Göstəricilər (Link to Tax Free Indicator, filtered
by group) and an Amount (Currency) field arranged in two columns.

Introduces a new Tax Free Indicator tree DocType mirroring the tax_article
pattern from taxes_az, seeded via fixtures with 74 indicators scraped from
the e-taxes.gov.az unified declaration (32 Vergi + 30 MDSS + 5 İşsizlik
+ 7 İcbari Tibbi Sığorta) grouped under 4 roots.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 11:06:04 +00:00
Ali 72f9973000 feat: also rename default groups after setup wizard completion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:33:23 +04:00
Ali 5ea710d77c feat: rename default Item/Supplier Groups to Azerbaijani
Add rename_default_groups.py that uses frappe.rename_doc to rename
standard ERPNext groups (Products -> Məhsullar, etc.) instead of
modifying ERPNext source files directly. Runs on after_migrate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:30:00 +04:00