Sales Order had a single visible `tax_article` link field while Sales Invoice had
long since moved to a multi-select: several articles per item row, held in a
parent-level table and tied to the row by a generated key. The two had to match,
so Sales Order now gets the same picker — and the same exclusions.
Rather than copy 250 lines of dialog code into a second file that would drift,
the behaviour moves to item_tax_articles.js, which takes the doctype, the item
doctype, the child doctype, the table field and the row-key field as config.
sales_invoice_tax_articles.js and sales_order_tax_articles.js are now shims that
call .init() with their own config. Same split server-side: item_tax_articles.py
holds the validate logic, the two custom/*_tax_articles.py are entry points.
Sales Order keeps its own child DocType rather than reusing the invoice's. The
downstream consumers (VAT Allocation, the tax reports) anchor their joins on
`tabSales Invoice`, so sharing one table would be safe today — but a single future
query written without that join would pull sales orders into a filed tax return,
and that is not a trap worth leaving armed.
Articles ride into the invoice on conversion: get_mapped_doc only walks the
standard field map, so make_sales_invoice is wrapped to carry them across, matching
order rows to invoice rows via `so_detail`. Row keys are regenerated, not copied —
they are no_copy and the two documents key their rows independently.
`Sales Order Item.tax_article` becomes hidden/read-only/deprecated, exactly as on
Sales Invoice. Nothing referenced it (the table is empty), and vat_calculator.js —
which drove it, and which still serves any doctype using the plain link field —
now skips Sales Order the same way it already skipped Sales Invoice, so it no
longer clears a hidden field and dirties the form on every refresh.
The 302.6 / 302.6.1 exclusion added for Sales Invoice was reachable around: the
dialog filtered them out, but the collapsible "Item Tax Articles" table on the
parent let you pick them directly. Both child DocTypes now carry the same
`not like 302.6%` in link_filters, which is what guards that path.
Verified end-to-end against the live site (rolled back): an order with two articles
converts to an invoice that keeps both, under a fresh row key, surviving validate.
Both forms checked headless — shared module loads, cells render, legacy field
hidden, no JS errors, and Sales Invoice is unregressed by the refactor.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a "Sertifikatın seriyə / nömrəsi" multi-select cell to Sales Invoice
Item, mirroring the existing tax-article cell:
- new child DocType "Sales Invoice Certificate" (parent_row + certificate
-> E-Taxes Presented Certificate), parent table custom_item_certificates,
per-row display field custom_certificates_display; reuses the existing
custom_si_row_key to link rows to certificates.
- sales_invoice_certificates.js: clicking an empty cell first asks
"0% dərəcəsi ilə ƏDV-yə cəlb olunma haqqında sertifikatlar var?" (yes ->
open the Table MultiSelect picker, no -> open nothing); rows that already
have certificates open the picker directly.
- server hook sync_item_certificates: stable row keys, drop orphans,
refresh the display label (not bound to the tax template).
- patch add_certificates_to_si_grid_views: inject the new column into saved
per-user grid layouts.
- lock the tax-article cell (read-only) for any line that has a certificate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per-user grid layouts (GridView in __UserSettings) saved before this feature
list the legacy tax_article column but not custom_tax_articles_display. Frappe
renders the saved list verbatim and ignores the new in_list_view field, so the
'Tax Articles' cell never appears for those users until added via the grid gear
-- which is why it showed on a fresh user (Administrator) but not on machines
where the user had a saved layout.
Patch replaces the legacy tax_article entry with custom_tax_articles_display in
every saved Sales Invoice Item layout that lacks it. Idempotent; runs on migrate
so every machine converges automatically.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bank Account is chosen explicitly in the Import dialog before the
parser runs, so the IBAN→Bank Account child table never carried weight
in this flow. Unlike kapital_bank — where the API hands over raw IBANs
and needs a lookup table — jey_erp's Excel import fixes one Bank
Account per file from the dialog and applies it to every row.
Removed surface:
- Bank Statement Importer JSON: Account Mappings subtab (Mappings group)
and Accounts subtab (Data group), plus their child table field
- Bank Integration Account Mapping DocType folder
- matching.py: match_similar_accounts
- creation.py: create_unmapped_accounts
- import_api.py: "accounts" branch of get_bi_reference_data_list
- bank_statement_importer.js: Match/Create buttons in the Accounts
group, _bi_render_accounts renderer, and its load_tab line
Added post_model_sync patch drop_bank_integration_account_mapping that
deletes the DocType and its `tab<Name>` table from existing databases.
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.
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>
Fields added as Small Text (TEXT in MySQL) to bypass tabEmployee's 65535-byte
row size limit. Added two pre_model_sync patches to fix row format and add
columns directly before Frappe schema sync runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rewrote won/lost checkbox logic with clean code approach
- Removed complex HTML synchronization in favor of native Frappe checkboxes
- Added CSS to display checkboxes inline (side by side) instead of stacked
- Implemented automatic enforcement: one checkbox must always be selected
- Added client-side validation that auto-corrects invalid states
- Added server-side validation for gambling companies only
- Removed old column break and section break fields
- Fields now only validate for gambling company main activities
Changes:
- custom_fields.py: Simplified won/lost field definitions, removed HTML field
- sales_invoice_vat.js: Clean logic + CSS for inline display + auto-selection
- sales_invoice.py: Server validation with auto-correction
- hooks.py: Added validate_won_lost_fields hook
- patches: Added cleanup patches for old fields
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>