fix(tax-article): clean curly quotes at the source instead of every migrate
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>
This commit is contained in:
parent
b963422bca
commit
ae162adc0c
|
|
@ -1,159 +0,0 @@
|
||||||
# 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.master_data.sync.sync_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. Запустите скрипт вручную для диагностики
|
|
||||||
|
|
@ -1661,13 +1661,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.24-cü maddəsinə əsasən \"Məşğulluq haqqında” Azərbaycan Respublikasının Qanununa uyğun olaraq Azərbaycan Respublikasının Prezidentinin müəyyən etdiyi orqanın (qurumun) haqqı ödənilən ictimai işlərin təşkili ilə bağlı fəaliyyətdən əldə etdiyi gəlir",
|
"article_name": "Vergi Məcəlləsinin 106.1.24-cü maddəsinə əsasən \"Məşğulluq haqqında\" Azərbaycan Respublikasının Qanununa uyğun olaraq Azərbaycan Respublikasının Prezidentinin müəyyən etdiyi orqanın (qurumun) haqqı ödənilən ictimai işlərin təşkili ilə bağlı fəaliyyətdən əldə etdiyi gəlir",
|
||||||
"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.703000",
|
"modified": "2025-09-19 16:35:19.703000",
|
||||||
"name": "Vergi Məcəlləsinin 106.1.24-cü maddəsinə əsasən \"Məşğulluq haqqında” Azərbaycan Respublikasının Qanununa uyğun olaraq Azərbaycan Respubli...",
|
"name": "Vergi Məcəlləsinin 106.1.24-cü maddəsinə əsasən \"Məşğulluq haqqında\" Azərbaycan Respublikasının Qanununa uyğun olaraq Azərbaycan Respubli...",
|
||||||
"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"
|
||||||
},
|
},
|
||||||
|
|
@ -1848,7 +1848,7 @@
|
||||||
"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.23-cü maddəsinə əsasən mikro və ya kiçik sahibkarlıq subyekti olan və hüquqi şəxs kimi fəaliyyət göstərən startapların “Startap” şəhadətnaməsi aldığı tarixdən innovasiya fəaliyyətindən əldə etdikləri mənfəəti",
|
"article_name": "Vergi Məcəlləsinin 106.1.23-cü maddəsinə əsasən mikro və ya kiçik sahibkarlıq subyekti olan və hüquqi şəxs kimi fəaliyyət göstərən startapların \"Startap\" şəhadətnaməsi aldığı tarixdən innovasiya fəaliyyətindən əldə etdikləri mənfəəti",
|
||||||
"declaration": "Income tax return",
|
"declaration": "Income tax return",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Tax Article",
|
"doctype": "Tax Article",
|
||||||
|
|
@ -1903,13 +1903,13 @@
|
||||||
"parent_tax_article": "Verginin hesablanması"
|
"parent_tax_article": "Verginin hesablanması"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"article_name": "Vergi ödəyicisi kimi vergi orqanında uçotda olmayan fiziki şəxslərin “Nağdsız hesablaşmalar haqqında” Azərbaycan Respublikasının Qanununun 3.5-ci maddəsində göstərilən malların təqdim edilməsindən əldə edilən gəlirləri (vergidən azad olunan gəlirlər istisna olmaqla) xərclər çıxılmadan",
|
"article_name": "Vergi ödəyicisi kimi vergi orqanında uçotda olmayan fiziki şəxslərin \"Nağdsız hesablaşmalar haqqında\" Azərbaycan Respublikasının Qanununun 3.5-ci maddəsində göstərilən malların təqdim edilməsindən əldə edilən gəlirləri (vergidən azad olunan gəlirlər istisna olmaqla) xərclər çıxılmadan",
|
||||||
"declaration": "Tax return withheld at source of payment",
|
"declaration": "Tax return withheld at source of payment",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Tax Article",
|
"doctype": "Tax Article",
|
||||||
"is_group": 0,
|
"is_group": 0,
|
||||||
"modified": "2025-09-19 16:35:19.707000",
|
"modified": "2025-09-19 16:35:19.707000",
|
||||||
"name": "Vergi ödəyicisi kimi vergi orqanında uçotda olmayan fiziki şəxslərin “Nağdsız hesablaşmalar haqqında” Azərbaycan Respublikasının Qanununu...",
|
"name": "Vergi ödəyicisi kimi vergi orqanında uçotda olmayan fiziki şəxslərin \"Nağdsız hesablaşmalar haqqında\" Azərbaycan Respublikasının Qanununu...",
|
||||||
"old_parent": null,
|
"old_parent": null,
|
||||||
"parent_tax_article": "Verginin hesablanması"
|
"parent_tax_article": "Verginin hesablanması"
|
||||||
},
|
},
|
||||||
|
|
@ -2156,13 +2156,13 @@
|
||||||
"parent_tax_article": "Verginin hesablanması"
|
"parent_tax_article": "Verginin hesablanması"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"article_name": "Vergi Məcəlləsinin 207.7-ci maddəsinə əsasən \"Aqrarkredit” Qapalı Səhmdar Cəmiyyəti Bank Olmayan Kredit Təşkilatı və “Azərbaycan Sənaye Korporasiyası” Açıq Səhmdar Cəmiyyəti ödəmə qabiliyyətini itirmiş bankların rezolyusiyası və sağlamlaşdırma tədbirləri çərçivəsində Azərbaycan Respublikasının Prezidentinin qərarına əsasən aldığı problemli aktivlərin (borcların) əvəzində əldə etdiyi, onun mülkiyyətində (balansında) olan və siyahısını Azərbaycan Respublikasının İqtisadiyyat Nazirliyi ilə razılaşdırdığı torpağa görə",
|
"article_name": "Vergi Məcəlləsinin 207.7-ci maddəsinə əsasən \"Aqrarkredit\" Qapalı Səhmdar Cəmiyyəti Bank Olmayan Kredit Təşkilatı və \"Azərbaycan Sənaye Korporasiyası\" Açıq Səhmdar Cəmiyyəti ödəmə qabiliyyətini itirmiş bankların rezolyusiyası və sağlamlaşdırma tədbirləri çərçivəsində Azərbaycan Respublikasının Prezidentinin qərarına əsasən aldığı problemli aktivlərin (borcların) əvəzində əldə etdiyi, onun mülkiyyətində (balansında) olan və siyahısını Azərbaycan Respublikasının İqtisadiyyat Nazirliyi ilə razılaşdırdığı torpağa görə",
|
||||||
"declaration": "Land tax declaration",
|
"declaration": "Land tax declaration",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Tax Article",
|
"doctype": "Tax Article",
|
||||||
"is_group": 0,
|
"is_group": 0,
|
||||||
"modified": "2025-09-19 16:35:19.714000",
|
"modified": "2025-09-19 16:35:19.714000",
|
||||||
"name": "Vergi Məcəlləsinin 207.7-ci maddəsinə əsasən \"Aqrarkredit” Qapalı Səhmdar Cəmiyyəti Bank Olmayan Kredit Təşkilatı və “Azərbaycan Sənaye K...",
|
"name": "Vergi Məcəlləsinin 207.7-ci maddəsinə əsasən \"Aqrarkredit\" Qapalı Səhmdar Cəmiyyəti Bank Olmayan Kredit Təşkilatı və \"Azərbaycan Sənaye K...",
|
||||||
"old_parent": null,
|
"old_parent": null,
|
||||||
"parent_tax_article": "Verginin hesablanması"
|
"parent_tax_article": "Verginin hesablanması"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ fixtures = [
|
||||||
after_migrate = [
|
after_migrate = [
|
||||||
"taxes_az.master_data.sync.sync_main_type_of_activity",
|
"taxes_az.master_data.sync.sync_main_type_of_activity",
|
||||||
"taxes_az.master_data.sync.sync_item_groups",
|
"taxes_az.master_data.sync.sync_item_groups",
|
||||||
"taxes_az.normalize_tax_articles.normalize_on_migrate",
|
|
||||||
"taxes_az.setup_accounts.check_and_create_accounts"
|
"taxes_az.setup_accounts.check_and_create_accounts"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Normalize Tax Articles - fix Unicode normalization, curly quotes, and NBSP
|
|
||||||
|
|
||||||
This script:
|
|
||||||
1. Replaces non-breaking spaces (NBSP U+00A0) with regular spaces
|
|
||||||
2. Replaces typographic (curly) quotes with straight quotes
|
|
||||||
3. Normalizes Unicode to NFC form (composed characters)
|
|
||||||
4. Fixes both `name` and `article_name` fields
|
|
||||||
|
|
||||||
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 name and article_name values:
|
|
||||||
- Replace non-breaking spaces (U+00A0) with regular spaces
|
|
||||||
- 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 non-breaking spaces (NBSP) with regular spaces
|
|
||||||
# C2A0 = UTF-8 for U+00A0 (NBSP)
|
|
||||||
# 20 = regular space
|
|
||||||
|
|
||||||
frappe.db.sql("""
|
|
||||||
UPDATE `tabTax Article`
|
|
||||||
SET article_name = REPLACE(article_name, UNHEX('C2A0'), ' ')
|
|
||||||
WHERE HEX(article_name) LIKE '%C2A0%'
|
|
||||||
""")
|
|
||||||
nbsp_article_fixed = frappe.db.sql("SELECT ROW_COUNT()")[0][0]
|
|
||||||
|
|
||||||
frappe.db.sql("""
|
|
||||||
UPDATE `tabTax Article`
|
|
||||||
SET name = REPLACE(name, UNHEX('C2A0'), ' ')
|
|
||||||
WHERE HEX(name) LIKE '%C2A0%'
|
|
||||||
""")
|
|
||||||
nbsp_name_fixed = frappe.db.sql("SELECT ROW_COUNT()")[0][0]
|
|
||||||
|
|
||||||
# Also fix NBSP in Sales Invoice Item tax_article references
|
|
||||||
if frappe.db.table_exists("Sales Invoice Item"):
|
|
||||||
frappe.db.sql("""
|
|
||||||
UPDATE `tabSales Invoice Item`
|
|
||||||
SET tax_article = REPLACE(tax_article, UNHEX('C2A0'), ' ')
|
|
||||||
WHERE HEX(tax_article) LIKE '%C2A0%'
|
|
||||||
""")
|
|
||||||
nbsp_ref_fixed = frappe.db.sql("SELECT ROW_COUNT()")[0][0]
|
|
||||||
else:
|
|
||||||
nbsp_ref_fixed = 0
|
|
||||||
|
|
||||||
# Step 2: Replace curly quotes with straight quotes using HEX
|
|
||||||
# E2809C = left curly quote \u201c
|
|
||||||
# E2809D = right curly quote \u201d
|
|
||||||
# 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 3: 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
|
|
||||||
encoding_errors = 0
|
|
||||||
|
|
||||||
for article in articles:
|
|
||||||
try:
|
|
||||||
original = article['article_name']
|
|
||||||
|
|
||||||
# Handle potential encoding issues
|
|
||||||
if isinstance(original, bytes):
|
|
||||||
original = original.decode('utf-8', errors='replace')
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
except (UnicodeDecodeError, UnicodeEncodeError, TypeError) as e:
|
|
||||||
encoding_errors += 1
|
|
||||||
frappe.log_error(
|
|
||||||
f"Encoding error in Tax Article {article['name']}: {str(e)}",
|
|
||||||
"Tax Article Encoding Error"
|
|
||||||
)
|
|
||||||
|
|
||||||
frappe.db.commit()
|
|
||||||
|
|
||||||
total_fixed = nbsp_article_fixed + nbsp_name_fixed + nbsp_ref_fixed + quotes_fixed + nfc_fixed
|
|
||||||
if total_fixed > 0:
|
|
||||||
parts = []
|
|
||||||
if nbsp_article_fixed > 0 or nbsp_name_fixed > 0:
|
|
||||||
parts.append(f"{nbsp_article_fixed + nbsp_name_fixed} NBSP fixed")
|
|
||||||
if nbsp_ref_fixed > 0:
|
|
||||||
parts.append(f"{nbsp_ref_fixed} NBSP in references fixed")
|
|
||||||
if quotes_fixed > 0:
|
|
||||||
parts.append(f"{quotes_fixed} curly quotes fixed")
|
|
||||||
if nfc_fixed > 0:
|
|
||||||
parts.append(f"{nfc_fixed} NFC normalized")
|
|
||||||
if encoding_errors > 0:
|
|
||||||
parts.append(f"{encoding_errors} encoding errors")
|
|
||||||
message = f"Tax Articles normalized: {', '.join(parts)}"
|
|
||||||
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)}")
|
|
||||||
Loading…
Reference in New Issue