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>
Rename/retire the property_tax_return_ai doctype family in favour of the
property_tax_gov pilot (page-based, no-auth calc), and update property_tax_return.py
accordingly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sales Invoice moved its single tax_article (Link) to a multi-select child table,
which inflated VAT Allocation: one line citing N articles produced N rows of the
full amount (e.g. 1500 -> 6000), so a 1500 payment only covered one article.
Group sales-invoice rows by (customer, item_tax_template) and union the cited
articles into a JSON tax_articles list, summing the amount once (the article
aggregation is computed separately so the JOIN can no longer multiply amounts).
The tax-articles report parses the list and replicates each row's amount/odenis
into every cited maddE column (same turnover under several articles, as on e-taxes).
The grid shows a translatable "N articles" cell that opens a read-only pill popup.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Revenue and VAT-allocation reports now resolve tax articles by JOINing the
Sales Invoice Tax Article child table (each article carries the full line
amount) instead of the single sii.tax_article; add 302.x/303.x mappings.
Remove the obsolete Tax Article Items Report doctype.
Also stop tracking .etaxes_notes/cabinet_internals.md (contains live
secrets) and broaden .gitignore to the whole .etaxes_notes/ folder.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds two parallel DocType families that don't touch the existing
`Property tax return` family:
- Property Tax Return AI (parent + 10 children): cabinet-verified port
of the property-tax formulas (501/503/504 mirrors, 506=1, 509.1/509.2,
511.x) with parity 3/3 vs. the original.
- Property Tax Return AI Fill (parent + 10 children): same formulas plus
auto-fill from ERPNext Asset records. On form open the seeded grid is
populated from the single Company's Assets, routed by
`taxable_asset_type` (→ elavə 2 rows 1.1–1.6, 2–5) and
`tax_exempt_tax_article` (→ elavə 1 rows by article-code regex).
Residuals at year boundaries come from Depreciation Schedule;
Company.date_of_establishment drives 507 ayla when il_erzinde=1.
Coverage: 16/16 smoke (seeding + formulas + 11 autofill scenarios) and
3/3 parity vs. original Property tax return pass.
Also adds .etaxes_notes/cabinet_internals.md (sanitised cabinet probe
notes) and gitignores .etaxes_notes/scratch/ which holds live JWTs and
HAR-like dumps.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add 8 articles under "ƏDV-yə 0% dərəcə ilə tutulan əməliyyatlar" (302.x)
and 6 under "ƏDV-dən azad olunan əməliyyatlar" (303.x), sourced from the
Declaration of value added tax client scripts (Elave 4 / Elave 5).
Keep declaration-code prefixes in article_name to avoid docname collisions
between the 0% and exempt sections (e.g. 302.12 vs 303.42 share identical text).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per-article column now shows invoice amount; a paired Ödəniş column shows
allocated_amount from VAT allocation sales invoice. Row filter widened to
include unpaid lines (amount > 0 OR allocated_amount > 0).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The tax_article.json fixture itself contained typographic curly quotes (“ ”),
so every migrate the fixture import wrote dirty data and the after_migrate
hook normalize_on_migrate() cleaned it back — a permanent ping-pong ("4 curly
quotes fixed" in every migrate log).
Fix the source: straighten the quotes in the fixture file once. The
normalize_on_migrate hook, the normalize_tax_articles.py module and its doc
are now redundant — removed. Existing databases need a one-time cleanup of
already-imported dirty rows (rename_doc on the affected names).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The hash fast-path compared the JSON file hash to a stored value and exited
early when unchanged. But it only tracked the *file*, not the *table*: if rows
went missing from the DB (manual delete, restore from an older dump) while the
JSON stayed the same, the sync skipped entirely and never restored them — so
`bench migrate` silently failed to recreate missing master-data rows.
Always run the full diff now. It costs one SELECT plus an in-memory compare —
measured at ~0.1s for 13k rows — so there's no reason to skip it. The diff is
driven by DB state, making the sync self-healing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
create_item_groups() ran a 26-record exists-check loop on every migrate
(the "Группа X уже существует, пропускаем" spam in migrate logs). Replace
with the master_data hash-diff sync: fast-paths via stored file hash, applies
only the delta otherwise.
Item Group is a tree doctype shared with ERPNext/user groups, so the sync
gains two options: is_tree=True (insert via db_insert, rebuild lft/rgt once
at the end) and manage_deletes=False (never delete rows absent from the JSON,
since the JSON is only the AZ tax-specific subset).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This doctype is now populated exclusively from master_data JSON via the
hash-diff sync, so block create/write/delete in the UI and API by dropping
those permissions. The sync writes at the DB level so it is unaffected.
Also marks option_name read_only and drops allow_rename.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous commit moved Main type of activity data to master_data/ but left
the original fixture file in place — Frappe's sync_fixtures reads every .json
in app/fixtures/ regardless of the hooks.py fixtures list, so the file was
still being imported on every migrate (3499 DELETE+INSERTs). Delete it.
Also adds a bulk-insert fast path (db_insert chunks of 500) to sync_master_data
for the large first-run case on fresh sites, so the new sync is faster than
the legacy fixture import even when populating an empty table.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixture import does DELETE+INSERT for every row on every migrate (force=True
in frappe's import_doc), so 3499 rows of Main type of activity added minutes
to each migrate. Replace with a hash-diff sync that fast-paths via stored
file hash and otherwise applies only the delta (insert new, update changed,
smart-delete removed with disabled-fallback).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces "Azerbaijan" with "Azərbaycan" in app description, README, and
the descriptions of the Tax Period Closing Settings fields. Country
master Link values (e.g. country_of_origin: Azerbaijan in item fixtures)
are kept as-is to preserve references.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>