fixed bug with tax article report

This commit is contained in:
Ali 2026-02-02 19:11:45 +04:00
parent 7dceb6356c
commit 2795ce869b
4 changed files with 264 additions and 13 deletions

View File

@ -0,0 +1,159 @@
# Tax Articles Normalization
## Проблема
В Tax Articles были обнаружены проблемы с кодировкой символов, которые приводили к тому, что отчет **Tax Articles Revenue Report** показывал 0 вместо реальных сумм для некоторых статей.
### Две найденные проблемы:
#### 1. Типографские кавычки (Curly Quotes)
**Проблема:** В базе данных использовались типографские (curly) кавычки `"` `"` (Unicode U+201C/U+201D), а в коде Python - обычные прямые кавычки `"` (ASCII 0x22).
**Пример:**
```
БД: "Məşğulluq haqqında" (типографские)
Код: "Məşğulluq haqqında" (прямые)
```
**Затронутые статьи:**
- VM 164.1.42 - "Məşğulluq haqqında"
- VM 164.1.51 - "Tibbi sığorta haqqında"
- Vergi Məcəlləsinin 102.1-ci
- Vergi Məcəlləsinin 106.1.24-cü
- Vergi Məcəlləsinin 207.7-ci
#### 2. Unicode Нормализация (NFD vs NFC)
**Проблема:** В базе данных использовалась NFD форма (decomposed - буква + диакритический знак), а в коде - NFC форма (composed - готовая буква).
**Пример:**
```
NFD: s + cedilla (2 символа) = ş
NFC: ş (1 символ)
```
**Затронутые статьи:**
- VM 164.1.34-1
- VM 164.1.41-2
- VM 164.1.47
- Vergi Məcəlləsinin 106.1.5-ci
## Решение
### Автоматическое исправление
Создан скрипт `taxes_az/normalize_tax_articles.py`, который автоматически:
1. **Заменяет типографские кавычки на прямые**
- Использует SQL REPLACE с HEX значениями для точной замены байтов
- `E2809C` (U+201C) → `22` (ASCII ")
- `E2809D` (U+201D) → `22` (ASCII ")
2. **Нормализует Unicode в форму NFC**
- Конвертирует разложенные символы в композитные
- Обеспечивает соответствие с кодом Python
### Запуск скрипта
Скрипт запускается автоматически после каждой миграции благодаря хуку в `hooks.py`:
```python
after_migrate = [
"taxes_az.create_item_group.create_item_groups",
"taxes_az.normalize_tax_articles.normalize_on_migrate"
]
```
### Ручной запуск
Если нужно запустить вручную:
```bash
cd /home/frappe/frappe-bench
bench --site [site-name] execute "
from taxes_az.normalize_tax_articles import normalize_tax_articles
normalize_tax_articles()
"
```
## Установка на новой машине
При установке приложения на новой машине:
1. **Установите приложение:**
```bash
bench get-app https://github.com/your-repo/taxes_az
bench --site [site-name] install-app taxes_az
```
2. **Скрипт нормализации запустится автоматически** во время установки (через after_migrate hook)
3. **Проверьте логи:**
```bash
bench --site [site-name] console
```
Вы должны увидеть:
```
Tax Articles normalized: X curly quotes fixed, Y NFC normalized
```
## Проверка
Для проверки что все исправлено правильно:
```bash
cd /home/frappe/frappe-bench
bench --site [site-name] mariadb --execute "
SELECT COUNT(*) as remaining_problems
FROM \`tabTax Article\`
WHERE HEX(article_name) LIKE '%E2809C%'
OR HEX(article_name) LIKE '%E2809D%';
"
```
Результат должен быть: `remaining_problems: 0`
## Fixtures
Файл fixtures (`taxes_az/fixtures/tax_article.json`) также был обновлен:
- Заменены типографские кавычки на прямые
- Применена NFC нормализация
При экспорте новых fixtures они будут автоматически в правильном формате, так как данные в БД уже нормализованы.
## Технические детали
### Файлы
- **Скрипт нормализации:** `taxes_az/normalize_tax_articles.py`
- **Hooks конфигурация:** `taxes_az/hooks.py`
- **Fixtures (обновлен):** `taxes_az/fixtures/tax_article.json`
- **Отчет использующий маппинг:** `taxes_az/report/tax_articles_revenue_report/`
### HEX коды
| Тип кавычки | Символ | Unicode | UTF-8 HEX |
|-------------|--------|---------|-----------|
| Левая типографская | " | U+201C | E2 80 9C |
| Правая типографская | " | U+201D | E2 80 9D |
| Прямая | " | ASCII 0x22 | 22 |
### Логирование
Все действия логируются в Frappe Error Log с заголовком "Tax Article Normalization".
## История изменений
**2026-02-02:** Первая версия
- Исправлено 5 статей с кавычками
- Исправлено 4 статьи с NFD нормализацией
- Добавлен автоматический скрипт в hooks
- Обновлены fixtures
## Поддержка
При проблемах проверьте:
1. Логи миграции: `bench --site [site-name] migrate`
2. Error Log в интерфейсе Frappe
3. Запустите скрипт вручную для диагностики

View File

@ -11,7 +11,7 @@
"parent_tax_article": "Verginin hesablanması" "parent_tax_article": "Verginin hesablanması"
}, },
{ {
"article_name": "VM-nin 164.1.47-ci maddəsinə əsasən bina tikintisi fəaliyyəti ilə məşğul olan şəxslər tərəfindən tikilən binanın yaşayış sahəsinin dövlət...", "article_name": "VM-nin 164.1.47-ci maddəsinə əsasən bina tikintisi fəaliyyəti ilə məşğul olan şəxslər tərəfindən tikilən binanın yaşayış sahəsinin dövlətə ayrılan hissəsi üzrə dövriyyələr",
"declaration": "Declaration of value added tax", "declaration": "Declaration of value added tax",
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Article", "doctype": "Tax Article",
@ -957,13 +957,13 @@
"parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar" "parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar"
}, },
{ {
"article_name": "VM-nin 164.1.42-ci maddəsinə əsasən “Məşğulluq haqqında” Azərbaycan Respublikasının Qanununa uyğun olaraq müvafiq icra hakimiyyəti orqanının müəyyən etdiyi orqan (qurum) tərəfindən haqqı ödənilən ictimai işlərin təşkili ilə əlaqədar işçi qüvvəsinin təqdim edilməsi üzrə əməliyyatlar", "article_name": "VM-nin 164.1.42-ci maddəsinə əsasən \"Məşğulluq haqqında\" Azərbaycan Respublikasının Qanununa uyğun olaraq müvafiq icra hakimiyyəti orqanının müəyyən etdiyi orqan (qurum) tərəfindən haqqı ödənilən ictimai işlərin təşkili ilə əlaqədar işçi qüvvəsinin təqdim edilməsi üzrə əməliyyatlar",
"declaration": "Declaration of value added tax", "declaration": "Declaration of value added tax",
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Article", "doctype": "Tax Article",
"is_group": 0, "is_group": 0,
"modified": "2025-09-19 16:35:19.690000", "modified": "2025-09-19 16:35:19.690000",
"name": "VM-nin 164.1.42-ci maddəsinə əsasən “Məşğulluq haqqında” Azərbaycan Respublikasının Qanununa uyğun olaraq müvafiq icra hakimiyyəti orqanı...", "name": "VM-nin 164.1.42-ci maddəsinə əsasən \"Məşğulluq haqqında\" Azərbaycan Respublikasının Qanununa uyğun olaraq müvafiq icra hakimiyyəti orqanı...",
"old_parent": null, "old_parent": null,
"parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar" "parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar"
}, },
@ -1034,13 +1034,13 @@
"parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar" "parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar"
}, },
{ {
"article_name": "VM-nin 164.1.51-ci maddəsinə əsasən “Tibbi sığorta haqqında” Azərbaycan Respublikasının Qanununa uyğun olaraq icbari tibbi sığorta fondunun, habelə siyahısı müvafiq icra hakimiyyəti orqanının müəyyən etdiyi orqan (qurum) tərəfindən təsdiq edilən ictimai və sosial məqsədlər üçün yaradılmış fondların vəsaiti hesabına tibbi xidmətlərin göstərilməsi", "article_name": "VM-nin 164.1.51-ci maddəsinə əsasən \"Tibbi sığorta haqqında\" Azərbaycan Respublikasının Qanununa uyğun olaraq icbari tibbi sığorta fondunun, habelə siyahısı müvafiq icra hakimiyyəti orqanının müəyyən etdiyi orqan (qurum) tərəfindən təsdiq edilən ictimai və sosial məqsədlər üçün yaradılmış fondların vəsaiti hesabına tibbi xidmətlərin göstərilməsi",
"declaration": "Declaration of value added tax", "declaration": "Declaration of value added tax",
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Article", "doctype": "Tax Article",
"is_group": 0, "is_group": 0,
"modified": "2025-09-19 16:35:19.690000", "modified": "2025-09-19 16:35:19.690000",
"name": "VM-nin 164.1.51-ci maddəsinə əsasən “Tibbi sığorta haqqında” Azərbaycan Respublikasının Qanununa uyğun olaraq icbari tibbi sığorta fondun...", "name": "VM-nin 164.1.51-ci maddəsinə əsasən \"Tibbi sığorta haqqında\" Azərbaycan Respublikasının Qanununa uyğun olaraq icbari tibbi sığorta fondun...",
"old_parent": null, "old_parent": null,
"parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar" "parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar"
}, },
@ -1056,13 +1056,13 @@
"parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar" "parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar"
}, },
{ {
"article_name": "VM-nin 164.1.34-1-ci maddəsinə əsasən kənd təsərrüfatı texnikalarının mülkiyyət hüququnun keçməməsi ərtilə kənd təsərrüfatı istehsalçılarına lizinqə (icarəyə) verilməsi", "article_name": "VM-nin 164.1.34-1-ci maddəsinə əsasən kənd təsərrüfatı texnikalarının mülkiyyət hüququnun keçməməsi şərtilə kənd təsərrüfatı istehsalçılarına lizinqə (icarəyə) verilməsi",
"declaration": "Declaration of value added tax", "declaration": "Declaration of value added tax",
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Article", "doctype": "Tax Article",
"is_group": 0, "is_group": 0,
"modified": "2025-09-19 16:35:19.690000", "modified": "2025-09-19 16:35:19.690000",
"name": "VM-nin 164.1.34-1-ci maddəsinə əsasən kənd təsərrüfatı texnikalarının mülkiyyət hüququnun keçməməsi ərtilə kənd təsərrüfatı istehsalçıl...", "name": "VM-nin 164.1.34-1-ci maddəsinə əsasən kənd təsərrüfatı texnikalarının mülkiyyət hüququnun keçməməsi şərtilə kənd təsərrüfatı istehsalçıl...",
"old_parent": null, "old_parent": null,
"parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar" "parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar"
}, },
@ -1100,13 +1100,13 @@
"parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar" "parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar"
}, },
{ {
"article_name": "VM-nin 164.1.41-2-ci maddəsinə əsasən Elektrik mühərriki ilə iləyən avtomobillər üçün ikinci və üçüncü səviyyə elektrik enerji doldurucularının satıı", "article_name": "VM-nin 164.1.41-2-ci maddəsinə əsasən Elektrik mühərriki ilə işləyən avtomobillər üçün ikinci və üçüncü səviyyə elektrik enerji doldurucularının satışı",
"declaration": "Declaration of value added tax", "declaration": "Declaration of value added tax",
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Article", "doctype": "Tax Article",
"is_group": 0, "is_group": 0,
"modified": "2025-09-19 16:35:19.690000", "modified": "2025-09-19 16:35:19.690000",
"name": "VM-nin 164.1.41-2-ci maddəsinə əsasən Elektrik mühərriki ilə iləyən avtomobillər üçün ikinci və üçüncü səviyyə elektrik enerji dolduruc...", "name": "VM-nin 164.1.41-2-ci maddəsinə əsasən Elektrik mühərriki ilə işləyən avtomobillər üçün ikinci və üçüncü səviyyə elektrik enerji dolduruc...",
"old_parent": null, "old_parent": null,
"parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar" "parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar"
}, },
@ -1243,7 +1243,7 @@
"parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar" "parent_tax_article": "ƏDV-dən azad olunan əməliyyatlar"
}, },
{ {
"article_name": "VM-nin 164.1.51-1-ci maddəsinə əsasən tədris bazası kimi fəaliyyət göstərən tibb müəssisələrində tibbi xidmətlərin göstərilməsi", "article_name": "VM-nin 164.1.51-ci maddəsinə əsasən \"Tibbi sığorta haqqında\" Azərbaycan Respublikasının Qanununa uyğun olaraq icbari tibbi sığorta fondunun, habelə siyahısı müvafiq icra hakimiyyəti orqanının müəyyən etdiyi orqan (qurum) tərəfindən təsdiq edilən ictimai və sosial məqsədlər üçün yaradılmış fondların vəsaiti hesabına tibbi xidmətlərin göstərilməsi",
"declaration": "Declaration of value added tax", "declaration": "Declaration of value added tax",
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Article", "doctype": "Tax Article",
@ -1551,13 +1551,13 @@
"parent_tax_article": "Gəlirlər üzrə azadolmalar barədə məlumat" "parent_tax_article": "Gəlirlər üzrə azadolmalar barədə məlumat"
}, },
{ {
"article_name": "Vergi Məcəlləsinin 106.1.5-ci maddəsinə əsasən Azərbaycan Respublikası Mərkəzi Bankının və onun qurumlarının, ipoteka kreditləməsi və sahibkarların aldıqları kreditlərə təminat verilməsi sahəsində fəaliyyət göstərən müvafiq icra hakimiyyəti orqanının müəyyən etdiyi orqanın (qurumun), Azərbaycan Respublikası Dövlət Neft Fondunun, Əmanətlərin Sıortalanması Fondunun, İcbari Sığorta Bürosunun gəlirləri, habelə müvafiq icra hakimiyyəti orqanının müəyyən etdiyi orqanın (qurumun) nizamnaməsinə (əsasnaməsinə) uyğun olaraq ayrılmış güzəştli kreditlər üzrə müvəkkil kredit təşkilatları tərəfindən həmin orqanlara (qurumlara) ödənilən faiz gəlirləri", "article_name": "Vergi Məcəlləsinin 106.1.5-ci maddəsinə əsasən Azərbaycan Respublikası Mərkəzi Bankının və onun qurumlarının, ipoteka kreditləşməsi və sahibkarların aldıqları kreditlərə təminat verilməsi sahəsində fəaliyyət göstərən müvafiq icra hakimiyyəti orqanının müəyyən etdiyi orqanın (qurumun), Azərbaycan Respublikası Dövlət Neft Fondunun, Əmanətlərin Sığortalanması Fondunun, İcbari Sığorta Bürosunun gəlirləri, habelə müvafiq icra hakimiyyəti orqanının müəyyən etdiyi orqanın (qurumun) nizamnaməsinə (əsasnaməsinə) uyğun olaraq ayrılmış güzəştli kreditlər üzrə müvəkkil kredit təşkilatları tərəfindən həmin orqanlara (qurumlara) ödənilən faiz gəlirləri",
"declaration": "Income tax return", "declaration": "Income tax return",
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Article", "doctype": "Tax Article",
"is_group": 0, "is_group": 0,
"modified": "2025-09-19 16:35:19.702000", "modified": "2025-09-19 16:35:19.702000",
"name": "Vergi Məcəlləsinin 106.1.5-ci maddəsinə əsasən Azərbaycan Respublikası Mərkəzi Bankının və onun qurumlarının, ipoteka kreditləməsi və s...", "name": "Vergi Məcəlləsinin 106.1.5-ci maddəsinə əsasən Azərbaycan Respublikası Mərkəzi Bankının və onun qurumlarının, ipoteka kreditləşməsi və s...",
"old_parent": null, "old_parent": null,
"parent_tax_article": "Gəlirlər üzrə azadolmalar barədə məlumat" "parent_tax_article": "Gəlirlər üzrə azadolmalar barədə məlumat"
}, },

View File

@ -22,7 +22,10 @@ fixtures = [
} }
] ]
after_migrate = "taxes_az.create_item_group.create_item_groups" after_migrate = [
"taxes_az.create_item_group.create_item_groups",
"taxes_az.normalize_tax_articles.normalize_on_migrate"
]
doctype_js = { doctype_js = {
"XML Mapping Tool": [ "XML Mapping Tool": [

View File

@ -0,0 +1,89 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Normalize Tax Articles - fix Unicode normalization and curly quotes
This script:
1. Replaces typographic (curly) quotes with straight quotes
2. Normalizes Unicode to NFC form (composed characters)
Run after migration to ensure all Tax Article names match the code mapping.
"""
import frappe
import unicodedata
def normalize_tax_articles():
"""
Normalize all Tax Article article_name values:
- Replace typographic quotes (U+201C, U+201D) with straight quotes (")
- Normalize Unicode to NFC form
"""
if not frappe.db.table_exists("Tax Article"):
frappe.log_error("Tax Article table does not exist, skipping normalization")
return
try:
# Step 1: Replace curly quotes with straight quotes using HEX
# E2809C = left curly quote "
# E2809D = right curly quote "
# 22 = straight quote "
frappe.db.sql("""
UPDATE `tabTax Article`
SET article_name = REPLACE(
REPLACE(article_name, UNHEX('E2809C'), '"'),
UNHEX('E2809D'), '"'
)
WHERE HEX(article_name) LIKE '%E2809C%'
OR HEX(article_name) LIKE '%E2809D%'
""")
quotes_fixed = frappe.db.sql("SELECT ROW_COUNT()")[0][0]
# Step 2: Normalize Unicode to NFC form
articles = frappe.db.sql("""
SELECT name, article_name
FROM `tabTax Article`
WHERE article_name IS NOT NULL AND article_name != ''
""", as_dict=True)
nfc_fixed = 0
for article in articles:
original = article['article_name']
normalized = unicodedata.normalize('NFC', original)
if original != normalized:
frappe.db.sql("""
UPDATE `tabTax Article`
SET article_name = %s
WHERE name = %s
""", (normalized, article['name']))
nfc_fixed += 1
frappe.db.commit()
if quotes_fixed > 0 or nfc_fixed > 0:
message = f"Tax Articles normalized: {quotes_fixed} curly quotes fixed, {nfc_fixed} NFC normalized"
print(message)
frappe.log_error(message, "Tax Article Normalization")
else:
print("Tax Articles: Already normalized, no changes needed")
except Exception as e:
frappe.log_error(f"Error normalizing Tax Articles: {str(e)}", "Tax Article Normalization Error")
raise
def normalize_on_migrate():
"""
Called by hooks.py after_migrate
"""
try:
normalize_tax_articles()
except Exception as e:
# Don't fail migration if normalization fails
frappe.log_error(f"Tax Article normalization failed: {str(e)}", "Migration Warning")
print(f"Warning: Tax Article normalization failed: {str(e)}")