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>
Bookkeeper clarified the contribution net-of-exemption columns should be
computed from Əsas əmək haqqı minus the exemption amount, not from
gross_pay. With no exemption the column equals the existing (ƏH) column.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
For each social contribution (DSMF, İcbari tibbi sığorta, İşsizlikdən
sığorta — işçi and şirkət, everything except Gəlir vergisi) add a column
per month that recomputes the amount on gross_pay minus that
contribution's "cəlb olunmayan gəlir" amount, using the Salary Component
formula. Sits next to the original column. 18 new columns total.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bookkeeper review confirmed the gəlir vergisi numbers are correct;
drop the tax_debug payload and the browser-console breakdown.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the console.group/console.table layout with one structured
text block per request: shows source fields (gross_pay, custom_vergi_amount),
formula, per-slip breakdown with bracket explanation, and per-month sum
arithmetic. Easy to select-all and forward to the bookkeeper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Server returns a tax_debug payload (per-slip, per (employee, month), and
monthly totals) and the client logs it via console.group + console.table
when payroll data is loaded. Temporary aid for the bookkeeper review —
will be removed once formulas are signed off.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply gəlir vergisi brackets to (gross_pay − custom_vergi_amount), not raw
gross. Slips marked azadolma drop to 0 contribution, matching how the
formula editor fills the tax-base column. Affects bölmə 3 hissə 1 and
əlavə 1 hissə 1 vergi məbləği.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per-employee quarterly income tax: brackets applied to each month's
gross pay, then summed for the quarter. Reuses the same per-month
gross helper as bölmə 3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply AZ progressive tax brackets (3%/10%/14%) per employee per month
on gross pay, then sum for monthly and quarterly tax amounts in
bölmə_3 hissə 1. Formula Editor stays responsible for the tax base
columns; this fills vergiməbləği* independently.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Group salary slips by tax-free indicator over the quarter and populate
vergi_, mdss_, issizlik, its child tables on il/rüb change. Convert the
göstəricilər field in those four child doctypes from Select to a Link
to Tax Free Indicator so values match what payroll already stores.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Duplicates the 7 deduction columns twice per month: once with the formula
evaluated at base=ƏH amount (LE=0), and once at base=0/LE=MK amount with
the formula(0,0) baseline subtracted. The sum of (ƏH-part + MK-part) for
each row equals the slip's actual deduction — serves as a built-in sanity
check. Baseline subtraction keeps Gəlir vergisi correct across the
threshold (200 AZN) without special-casing it.
Formulas are read from Salary Component at runtime via _safe_eval — no
hardcoded rates, so the report stays correct when tax law changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bring back salary_slip_id, start_date, end_date and the four tax-free
Göstərici Link fields that were dropped in the numeric-only pass, each
split into per-month columns so the formula editor can pick a specific
month's value.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add visible year/quarter selectors that sync hidden from_date/to_date
filters so the formula editor (which injects from_date via $AyIlSpecial)
keeps working without knowing about the quarter UI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace from/to date filters with year + quarter selectors and aggregate
one row per employee with numeric columns split per month (ay_1_, ay_2_,
ay_3_) so the formula editor can pick a specific month instead of
dividing the quarter total by 3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Event and checks referenced a non-existent hesabatdövrü field, so
selecting year + quarter never triggered populate_declaration_tables.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Row 4 sums Məzuniyyət Kompensasiyası per month from Salary Detail.
Row 1 Gəlirlər, cəmi now sums rows 2 + 4.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- bolme2_hisse2 row 2: sum of Əsas əmək haqqı per month from Salary Detail
- bolme2_hisse2 row 1: auto-sum of rows 2-5
- hissə_1 gəlirlərcəmi: switched from gross_pay to Əsas əmək haqqı
- New Monthly Present Report (Present + Half Day attendance)
- bolme2_hisse1 row 2: unique employees with Present/Half Day per month
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New report counts days present (Present + Half Day) per employee.
bolme2_hisse1 row 2 counts unique employees who showed up per month.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents migration failure when jey_erp custom fields are not yet
synced (Unknown column 'accounting_method' error).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Count employees per month using date_of_joining + relieving_date logic.
Safety net: status=Left without relieving_date excluded from count.
Updates existing pre-created rows by matching göstəricilər field.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>