Instead of waiting a fixed delay after selecting dövr, wrap frappe.call to track
the fill calls (populate_vat_tables + formula_editor run_reports_batch): suppress
the per-write recompute while any is in flight and run it exactly once the moment
the last one resolves (deferred a tick so the apply lands first). User edits
recompute instantly. Robust regardless of how long the reports take.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the 300ms debounce on manual edits (the formula_editor storm it guarded
against is already handled by fill mode), so dependent cells update immediately.
Shorten the post-report-fill settle from 3s to 400ms — the one delay that must
stay, since recomputing during the hundreds-of-cells fill would freeze the form.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Selecting dövr triggers populate_vat_tables + formula_editor, which fill leaf
cells from reports but do not roll parents up (e.g. 301.1 = Σ 301.1.1..4). The
per-write recompute is suppressed during that storm; previously nothing ran
afterwards, so 301.1 (and other roll-ups / appendix→turnover passthrough) stayed
stale. Now "fill mode" runs the recompute ONCE, 3s after the fill settles.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Revert the over-eager move: the default-row-creation scripts belong in Client
Scripts (restored to the fixture and DB). Only the column calculations
(VERIFIED CALC) stay inlined in declaration_of_value_added_tax.js. The
remove-client-script patch now targets just the calc script.
Result: rows are created once by the Client Scripts, calculations run from the
doctype JS; Formula Editor script unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
All computed-cell logic and default-row creation for "Declaration of value
added tax" now lives in declaration_of_value_added_tax.js (version-controlled)
instead of DB Client Scripts, so it no longer depends on fixture export/import
and can't be dropped by a stale-DB re-export.
- inline the VERIFIED CALC recompute + the 11 row-creating scripts (IIFE-wrapped)
- remove those 12 entries from the client_script.json fixture
- patch remove_vat_calc_client_scripts deletes them from existing DBs on migrate
(fixture removal alone is upsert-only and would otherwise leave duplicates)
The Formula Editor script stays a Client Script (it is tool-generated).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The other machine re-exported client_script.json from its own DB (which
lacked this DB-only script) and the pull dropped it from the fixture.
Re-add only that entry (latest fill-mode version); no other script touched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a Client Script (fixture) that computes all derived cells of
"Declaration of value added tax" on manual edit, matching the e-taxes
form (verified against the live site):
- appendix roll-ups (parent = Σ children) for Əlavə 3/4/5 and debitor 307
- passthrough appendices → turnover (301.1←Əlavə3, 302←Əlavə4,
303←Əlavə5, 301-2←agro sold); agro purchased = Σ Hissə 1
- rates (18/20%, Art.174.5 50% relief), %-share lines, clamps, 326/327 net
- Əlavə 1 / Əlavə 8 page totals
Mark every computed cell read-only (per-column, or per-row via
read_only_depends_on). Guard recompute against the formula_editor
report-fill storm ("fill mode") so selecting dövr no longer hangs.
Additive — existing scripts (formula_editor, row-creators) untouched.
Docs: edv_butun_dusturlar.md, edv_formulas.xlsx (formula reference).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The accountant reads the Tax Article picker side by side with the cabinet, so
the two have to read identically. Three of the nine Əlavə 3.2 articles drifted:
328 carried a double space and the grammatically-tidied "Respublikasının" where
the form writes "Respublikasınını", 329 a double space, 330 spelled out "Vergi
Məcəlləsinin" where the form abbreviates to "VM-nin".
331 and 336 are left alone — Əlavə 3.2 is the reference wording for those two
and the articles already match it.
The nine already existed and were already mapped in the VAT allocation report;
only the wording moved. Tax Article is autoname:Prompt, so name is a stored id
rather than a slug of article_name — re-wording article_name alone would leave
the old text showing in every link picker. The name has to move too, and a moved
name is a different document, hence the delete-and-reinsert patch rather than a
rename_doc walk across a NestedSet. Nothing referenced the three (all ten Tax
Article link fields checked, zero rows).
The report's mapping keys move in lockstep. They are matched by exact string
against article_name, so a key left behind would not raise — it would silently
zero its column on a filed return. All 115 keys verified to resolve against the
live table afterwards.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Picked up by bench migrate — fills in msgstr values that were previously
empty. Unrelated to the tax-article cleanup; committed separately to keep
that change reviewable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
301.1 (CƏMİ) and its two subtotals, the umbrella 165.1.3 article that sits
above the goods/services export ones, 308, 319.2 and 319.3 are ФНО summary
lines, not leaf articles an accountant should tag an invoice with. Tagging a
line with the umbrella 165.1.3 landed it in Əlavə 4 row 302.3 but not in the
302.3.1/302.3.2 breakdown underneath, so the total stopped reconciling with
its own parts.
Eight rows, not seven: 165.5 (NAĞD) shipped twice — Frappe truncates name at
140 chars, and the fixture carried both the full and the ellipsised spelling.
That was the only duplicate in the whole table.
Removing them from the fixture is not enough on its own: sync_fixtures imports
every json under fixtures/ regardless of the fixtures hook, and import_doc only
ever inserts or updates. Hence the patch as well — and it has to land before
sync_fixtures runs, which post_model_sync does.
No data referenced any of the eight (checked all ten Tax Article link fields,
including the ones jey_erp owns), so nothing on an invoice or asset moves.
Known follow-up, left deliberately: Əlavə 4 row 4 still resolves
report(VM-165.1.3) / report(VM-165.1.3-ödəniş), whose report columns are gone,
so it now yields 0 rather than 302.3.1 + 302.3.2. Handed to the accountant to
decide between summing the two survivors and filling the row by hand.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Selecting a period on a declaration fired one HTTP request per report reference
— 217 on a VAT return — and each one re-executed an entire Script Report just to
read a single column. Those 217 requests only ever ran 4 distinct reports: 120 of
VAT's 127 report fields read different columns out of the same report with the
same filters.
run_reports_batch groups the references by (report, filters) and executes each
report once, slicing every requested column out of the one result. The extraction
rule (take the report's total row, else sum every row) is ported verbatim — it
decides the figures that end up on a filed tax return.
The runtime lives here rather than in formula_editor because that app is a
design-time tool that gets removed from production machines; generated scripts no
longer reference it at all.
Errors are now surfaced instead of swallowed. A failed report used to yield 0 for
every field that asked for it, so a user without report permission got a
plausible-looking, fully-zeroed declaration and no hint anything was wrong.
Failing reports now contribute no values, the affected formulas are skipped rather
than zeroed, and the user is told which report failed and why.
Also fixes a Frappe core bug that threw on every declaration: Frappe writes the
active tab's fieldname into the URL hash, the URL API percent-encodes our
Azerbaijani fieldnames, and form.js then feeds that hash to jQuery as a CSS
selector.
Land tax and Property tax pointed js_parent_subtab at a parent tab that does not
exist in those doctypes, so their Əlavə tabs never nested.
VAT, one period change: 217 HTTP requests -> 1, 236 refresh_field -> 11, 4.5s -> 1.5s.
Verified identical output across all 426 formulas and all 337 report fields.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Data is varchar(140) and Frappe raises CharacterLengthExceededError rather
than truncating, so a long organisation name would block saving the
declaration. vöenadı is the tightest: it concatenates VÖEN + " / " + name,
leaving 127 characters for the name, and the longest supplier on record
already runs to 93.
Widen the four counterparty name fields to Small Text (TEXT column):
- vöenadı (Əlavə 1 yeni)
- sertifikattəqdimolunanşəxsinadı (Əlavə 8 Tablo 1 and Tablo 2)
- adı (Əlavə 2 Hissə 1)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fill the second Əlavə 1 tab (əlavə1_yeni) from the Purchase VAT offset
report (Cədvəl 22), whose columns map one-to-one onto the table and whose
vat_offset_line_code shares the 308/314/316 options of sətirkodu. The
report gained a posting_date key so the tarix column can be filled; its
visible columns are unchanged.
Also fill three summary targets that were left to manual entry:
- əlavə_1_2 totals over Əlavə 1
- əlavə1_2_yeni totals per line code
- sertifikat...səhifəsicəmi page total of Əlavə 8 Tablo 2
The two summary tables already carry rows created by a client script, so
they are filled in place — fill_first_row and fill_rows_by_code never add
or remove rows. Rows are matched on the leading line code because the
stored sətirkodu carries a long description after it.
Widen the Int money fields to Float: paid VAT is proportional and was
truncating (106.78 -> 107). Invoice number becomes Data, matching the
report where it is a free-form string.
Note: the generated Formula Editor client script still writes əlavə_1_2
and əlavə1_2_yeni row 0 from report() grand totals. Those 6 formula blocks
are to be removed by hand; the Formula Editor record itself no longer
defines them, so they will not be regenerated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
populate_vat_tables built Əlavə 8 Tablo 2 rows with keys that do not exist
on "Declaration of value added tax Elave 8 2ci" (şəxsinadı, şəxsinvöeni,
sertifikatınseriyası, nsi, xidmətindəyəri). set_table() assigned them onto
the child object without error, so rows were created empty.
Map to the actual fieldnames. The doctype stores series and number in a
single "Sertifikatın seriya / nömrəsi" field, so cert_no is written whole
and _split_cert_no is dropped.
Also widen the two Int fields: the value column truncated decimals, and
VÖEN is an identifier that must not lose leading zeros — both now match
Elave 8 1ci.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Picking a period fired every recalculation twice. `frm.set_value()` schedules the
field's handlers asynchronously (frappe.model.set_value -> run_serially), so by
the time the first trigger ran, both `ay`/`rüb` and `il` were already set in
frm.doc — and each field's handler then did a full pass. In VAT that also meant
`populate_vat_tables` was called twice per pick.
Five declarations were affected, all of them via Formula_Editor_Script_* which
bind a handler to every field a formula depends on: Declaration of value added
tax, Declaration of mining tax, Road tax 2, Tax return withheld at source of
payment, Single declaration related to salaried and non-salaried work.
Those scripts are generated, so they can't be patched — the fix has to live in
the picker. Instead of two set_value() calls, write the fields directly, do what
set_value would have done (refresh_field, refresh_dependency, dirty) and fire the
handlers exactly once.
Firing only `il` is safe: for every affected declaration the period handler and
the `il` handler have byte-identical bodies once the trigger-field argument is
normalized (VAT 2750 lines each, single declaration 1202, mining tax 484, road
tax 2 128, tax withheld 4 — differing only in a comment), and
calculate_table_row_field() never reads its triggerField argument. A runtime
fallback triggers the period field instead when no `il` handler is bound.
Note this cannot be done with set_value("il", year) alone: frappe.model.set_value
skips both the trigger and dirty() when the value is unchanged, so changing only
the month within the same year would silently recalculate nothing.
Verified headless: one call per pick on both VAT (month) and Single declaration
(quarter), including a second pick within the same year.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every declaration used to expose its reporting period as raw `ay` (month or
quarter Select), `il` (Int year) and/or `rüb` (quarter Select) fields, scattered
across the form. This adds a single `Dövr` field to all 23 declarations that
have a period, and hides the legacy fields.
Clicking `Dövr` opens an inline air-datepicker popup (the same widget Frappe
uses for Date fields, not a modal), restricted per declaration:
month -> month grid with Azerbaijani month names + year nav
quarter_* -> 2x2 grid of "1. Rüb".."4. Rüb"
annual -> year grid
The selection is written back into the hidden `ay`/`il`/`rüb` in their exact
original formats, so all downstream code (VAT appendix auto-fill, property tax
asset loading, single declaration, reports) keeps working untouched.
`dövr` deliberately carries no `read_only` docfield flag: with the site setting
`hide_empty_read_only_fields` enabled, Frappe hides empty read-only fields, which
made the picker invisible (and unopenable) on new documents. Keyboard input is
blocked from JS instead.
Also restores `ay` + `il` (hidden) on Declaration of value added tax: they were
deleted upstream while declaration_of_value_added_tax.{js,py} still read them,
which had silently broken the Əlavə auto-fill from reports.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Property Tax Gov family (parent + 10 child tables) was a duplicate of
Property tax return, used as a pilot for page-based declaration calc. It is
no longer needed; the original Property tax return is untouched and remains
the only property-tax declaration.
Both tables were empty (0 documents). `bench migrate` drops the orphaned
DocTypes; the empty `tabProperty Tax Gov*` tables were dropped manually since
migrate leaves them behind.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- New Script Report "Certificate VAT Report": one row per sales-invoice item
certificate (cert no, VÖEN, taxpayer, value), full item amount per certificate
with no splitting; filters company + date range, submitted invoices only.
- Auto-fill VAT declaration Əlavə 8 Table 2 (müəssisə_malın_dəyəri) from that
report inside populate_vat_tables; split cert_no into series (letters, max 3)
and number (digits).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lists submitted Purchase Invoices flagged for VAT offset that have a VAT
offset line code: line code, VOEN/Name, e-taxes invoice series/number,
total, VAT (only the 521.3 ƏDV tax account), paid total (Grand Total -
Outstanding, Purchase Register logic) and proportional paid VAT.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ygbnintarixi was a Float field, so the YGB date could not be stored.
Change it to Date and map posting_date from the YGB report.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On selecting İl + Ay, the Declaration of value added tax fills three
appendices from the source reports' get_data:
- Əlavə 1 <- Purchase YGB VAT report
- Əlavə 2 Hissə 1 <- Purchase invoice info report (act_type mapped to AZ)
- Əlavə 2 Hissə 2 <- Agricultural goods stock report
Adds a whitelisted populate_vat_tables method + form trigger, and a
company filter to the purchase invoice info report.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the temporary Source type / Source document columns and the per-voucher
detail rows used for tracing; the report returns the single summary row again.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sold in period (sale price) now sums Sales Invoice items only when the
sale produced an outgoing Stock Ledger entry, so it stays consistent with
COGS/closing. A sale that does not update stock contributes 0 instead of
inflating the sale figure with no matching cost.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Append detail rows listing the Stock Ledger entries (with a clickable link
to each source voucher) and sale prices behind opening/purchased/COGS/
closing, for tracing. The summary stays the first row. Also decide stock
direction by stock_value_difference so opening-stock reconciliations
(actual_qty = 0) are counted correctly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the purchase_type/agricultural_goods gate with a YGB-presence
filter: a Purchase Invoice is included only when at least one of its YGB
fields (ygb / line_code) is filled.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebuild the report on Stock Ledger Entry instead of custom FIFO over
invoices. Opening/closing balances and COGS now come from ERPNext's stock
valuation (which already applies the company's method and includes opening
stock entered via Stock Reconciliation); sale price still comes from Sales
Invoices. Filtering is now item-level (Item.agricultural_goods) and all
amounts are net of VAT, so the trade-markup VAT is not double-counted.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Opening balance now subtracts sales before the period at their COGS
(via the company's FIFO/Moving Average method) instead of at sale price.
This keeps it consistent with the cost-based closing balance, so each
period's opening equals the previous period's closing and the value can
no longer go negative while stock remains.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closing balance is now cost-based (opening + purchased - COGS) so the
trade-markup identity holds. Adds two columns:
- Trade markup on goods sold = sold(sale) - (opening + purchased - closing)
- VAT on trade markup = trade markup * 18%
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per-document purchase report (Cədvəl 21 — ƏDV - YGB hesabat). A Purchase
Invoice qualifies when submitted and at least one of purchase_type /
agricultural_goods is set (items are not filtered). Columns: document,
date, line code, YGB, amount without VAT (net_total) and VAT amount.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Agricultural goods stock report now filters by the per-item agricultural
flag (only agro items inside agro documents count) and grosses up rows to
include VAT. Adds a single "Sold in period (purchase price)" column whose
costing method follows the company's Default Stock Valuation Method
(Moving Average -> weighted average, otherwise FIFO).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch the inclusion condition from purchase_type=1 to the newly added
agricultural_goods=1 flag on Purchase Invoice.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New Script Report grouping purchase-type Purchase Invoices by Act Type,
showing counterparty (ФИО/company/country), VOEN/FIN, and amount.
Includes a link column to the source Purchase Invoice and optional
date-range filters; formula-editor friendly (no total row).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove the now-unreachable profile-fetch and Asan inline-auth machinery
(checkAuthAndFetchProfile, showProfileDialog, startInlineAuthentication,
poll/complete/select cert, profile parsing/formatting helpers and the
dictionary cache) left orphaned after the E-Taxes buttons were removed.
Keeps gambling-activity handling, declaration defaults and tax wizards.
1797 -> 313 lines.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the "Get E-Taxes Profile" and "Set Main Activity" custom buttons
from the Company form. main_activity stays read-only (Custom Field
read_only=1) and can no longer be overridden via the dialog.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the 45 new tax articles to VAT Allocation Tax Articles Report, following
the report's hardcoded mapping standard:
- 36 from "ƏDV tutulan dövriyyə barədə məlumat" (301.x)
- 9 from "ƏDV tutulan əməliyyat sayılmayan dövriyyə" (328–336)
extending both get_tax_articles_mapping() and get_field_to_label_mapping().
Resolve the 301.1 vs 301-1 key collision (both -> vm_301_1 under the
separator->underscore convention): dash codes stay vm_301_N (consistent with
existing 302/303), the CƏMİ total line 301.1 -> vm_301_1_cemi.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the two Əlavə 3 declaration tables as prefixed Tax Article groups:
- "ƏDV tutulan dövriyyə barədə məlumat" (301.x, 36 articles)
- "ƏDV tutulan əməliyyat sayılmayan dövriyyə barədə məlumat" (328–336, 9)
mirroring the client scripts that populate Declaration of value added tax Əlavə 3.
Remove the legacy "ƏDV tutulan və tutulmayan əməliyyatlar" group (6 unused,
unprefixed articles) from the fixture, and add a post_model_sync patch to delete
it on other sites before fixtures import (fixture import never deletes).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>