arc/docs/CURRENCY_MIGRATION_ANALYSIS.md
2026-04-14 19:34:55 +03:30

336 lines
11 KiB
Markdown
Executable file

# تحلیل انتقال default_currency_id از hesabixOld به hesabixpy
## خلاصه اجرایی
این سند تحلیل نحوه انتقال `default_currency_id` از دیتابیس قدیمی (`hesabixOld`) به دیتابیس جدید (`hesabixpy`) را ارائه می‌دهد.
## وضعیت فعلی
### دیتابیس قدیمی (hesabixOld)
#### جدول `money`
- **تعداد ارزها**: 4 ارز
- **ساختار**:
- `id`: شناسه یکتا
- `name`: نام کد ارز (مثل IRR, USD, AFN, IQD)
- `label`: نام کامل فارسی
- `symbol`: نماد ارز
- `short_name`: نام کوتاه
**داده‌های موجود**:
```
id | name | label | symbol | short_name
1 | IRR | ریال ایران | ریال | ریال
2 | USD | دلار آمریکا | $ | دلار
3 | AFN | افغانی افغانستان | ؋ | افغانی
4 | IQD | دینار عراق | ع.د | دینار
```
#### جدول `business`
- **فیلد**: `money_id` (ارجاع به `money.id`)
- **توزیع استفاده**:
- `money_id = 1` (IRR): 4,420 کسب و کار (99.1%)
- `money_id = 2` (USD): 40 کسب و کار (0.9%)
- `money_id = 3` (AFN): 10 کسب و کار (0.2%)
- `money_id = 4` (IQD): 3 کسب و کار (0.07%)
- **کسب و کارهای بدون money_id**: 0 (همه کسب و کارها money_id دارند)
### دیتابیس جدید (hesabixpy)
#### جدول `currencies`
- **تعداد ارزها**: 82 ارز
- **ساختار**:
- `id`: شناسه یکتا
- `name`: نام کامل انگلیسی
- `title`: نام فارسی
- `symbol`: نماد ارز
- `code`: کد ارز (unique) - معادل `name` در جدول قدیمی
**ارزهای مربوط به دیتابیس قدیمی**:
```
id | name | title | symbol | code
1 | Iranian Rial | ریال ایران | ﷼ | IRR
2 | United States Dollar| US Dollar | $ | USD
20 | Afghan Afghani | Afghani | ؋ | AFN
19 | Iraqi Dinar | Iraqi Dinar | ع.د | IQD
```
#### جدول `businesses`
- **فیلد**: `default_currency_id` (ارجاع به `currencies.id`)
- **نوع**: `Integer, ForeignKey, nullable=True`
## استراتژی انتقال
### روش 1: Mapping بر اساس Code (پیشنهادی)
**الگوریتم**:
1. ایجاد جدول mapping بین `money.id` (قدیمی) و `currencies.id` (جدید)
2. Mapping بر اساس `money.name` (قدیمی) = `currencies.code` (جدید)
3. استفاده از mapping برای تبدیل `business.money_id` → `businesses.default_currency_id`
**مزایا**:
- دقیق و قابل اعتماد
- بر اساس استاندارد ISO currency codes
- ساده و قابل پیاده‌سازی
**معایب**:
- نیاز به ایجاد mapping table
### روش 2: جستجوی مستقیم در هر انتقال
**الگوریتم**:
1. برای هر کسب و کار، `money_id` را از دیتابیس قدیمی بخوان
2. `money.name` را از جدول `money` بخوان
3. در جدول `currencies` جستجو کن: `WHERE code = money.name`
4. اگر پیدا شد: استفاده از `currencies.id`
5. اگر پیدا نشد: استفاده از پیش‌فرض (IRR)
**مزایا**:
- نیاز به mapping table ندارد
- ساده برای پیاده‌سازی
**معایب**:
- کندتر (query برای هر کسب و کار)
- نیاز به cache کردن نتایج
### روش 3: استفاده از پیش‌فرض برای همه
**الگوریتم**:
- همه کسب و کارها را با `default_currency_id = 1` (IRR) ایجاد کن
**مزایا**:
- بسیار ساده
- سریع
**معایب**:
- از دست رفتن اطلاعات (40 کسب و کار USD، 10 کسب و کار AFN، 3 کسب و کار IQD)
- نادرست برای کسب و کارهای غیر-IRR
## راه‌حل پیشنهادی: روش 1 (Mapping Table)
### پیاده‌سازی
#### 1. ایجاد Currency Mapping Table
```sql
-- ایجاد جدول موقت برای mapping
CREATE TEMPORARY TABLE currency_id_mapping AS
SELECT
old_money.id as old_money_id,
new_currency.id as new_currency_id,
old_money.name as code
FROM hesabixOld.money old_money
INNER JOIN hesabixpy.currencies new_currency
ON old_money.name = new_currency.code;
```
**نتیجه**:
```
old_money_id | new_currency_id | code
1 | 1 | IRR
2 | 2 | USD
3 | 20 | AFN
4 | 19 | IQD
```
#### 2. استفاده در انتقال کسب و کار
```python
def get_default_currency_id(old_money_id: int, currency_mapping: Dict[int, int]) -> int | None:
"""
تبدیل money_id قدیمی به default_currency_id جدید
Args:
old_money_id: شناسه money در دیتابیس قدیمی
currency_mapping: دیکشنری mapping {old_money_id: new_currency_id}
Returns:
شناسه currency در دیتابیس جدید یا None
"""
if not old_money_id:
return None
new_currency_id = currency_mapping.get(old_money_id)
if new_currency_id:
return new_currency_id
# اگر پیدا نشد، از پیش‌فرض IRR استفاده کن
# جستجو برای IRR در دیتابیس جدید
return get_irr_currency_id() # معمولاً 1
```
#### 3. مدیریت موارد خاص
**سناریو 1: money_id پیدا نشد در mapping**
- عمل: استفاده از پیش‌فرض IRR (currency_id = 1)
- لاگ: ثبت در لاگ برای بررسی
**سناریو 2: currency در دیتابیس جدید وجود ندارد**
- عمل: استفاده از پیش‌فرض IRR
- لاگ: ثبت در لاگ با هشدار
**سناریو 3: money_id = NULL**
- عمل: `default_currency_id = NULL` یا پیش‌فرض IRR
- توجه: در دیتابیس قدیمی همه کسب و کارها money_id دارند، پس این حالت نادر است
### کد نمونه
```python
def create_currency_mapping(db_old, db_new) -> Dict[int, int]:
"""
ایجاد mapping بین money قدیمی و currencies جدید
"""
# Query برای ایجاد mapping
query = """
SELECT
old_money.id as old_money_id,
new_currency.id as new_currency_id
FROM hesabixOld.money old_money
INNER JOIN hesabixpy.currencies new_currency
ON old_money.name = new_currency.code
"""
results = db_old.execute(text(query)).fetchall()
mapping = {}
for row in results:
mapping[row.old_money_id] = row.new_currency_id
return mapping
def get_default_currency_id_for_business(
old_money_id: int | None,
currency_mapping: Dict[int, int],
default_irr_id: int = 1
) -> int | None:
"""
تبدیل money_id قدیمی به default_currency_id جدید
"""
if not old_money_id:
return default_irr_id # یا None بسته به نیاز
return currency_mapping.get(old_money_id, default_irr_id)
```
## آمار و ارقام
### توزیع استفاده از ارزها
| ارز | تعداد کسب و کار | درصد |
|-----|----------------|------|
| IRR | 4,420 | 99.1% |
| USD | 40 | 0.9% |
| AFN | 10 | 0.2% |
| IQD | 3 | 0.07% |
| **کل** | **4,473** | **100%** |
### Mapping Table
| old_money_id | old_code | new_currency_id | new_code | تطابق |
|--------------|----------|-----------------|----------|--------|
| 1 | IRR | 1 | IRR | ✅ |
| 2 | USD | 2 | USD | ✅ |
| 3 | AFN | 20 | AFN | ✅ |
| 4 | IQD | 19 | IQD | ✅ |
**نتیجه**: همه 4 ارز قدیمی در دیتابیس جدید وجود دارند و می‌توانند به درستی mapping شوند.
**نکته مهم**: به دلیل تفاوت collation بین دو دیتابیس، باید از `BINARY` یا `COLLATE` در query استفاده کرد:
```sql
-- روش صحیح برای mapping
SELECT old_money.id, new_currency.id
FROM hesabixOld.money old_money
INNER JOIN hesabixpy.currencies new_currency
ON BINARY old_money.name = BINARY new_currency.code;
```
## مراحل اجرا
### مرحله 1: ایجاد Currency Mapping
```python
# در اسکریپت انتقال کسب و کارها
currency_mapping = create_currency_mapping(old_db, new_db)
# نتیجه: {1: 1, 2: 2, 3: 20, 4: 19}
```
### مرحله 2: استفاده در انتقال
```python
# برای هر کسب و کار
old_money_id = old_business.get('money_id')
default_currency_id = get_default_currency_id_for_business(
old_money_id,
currency_mapping,
default_irr_id=1
)
# درج در دیتابیس جدید
business_data = {
# ... سایر فیلدها ...
'default_currency_id': default_currency_id
}
```
### مرحله 3: اعتبارسنجی
```sql
-- بررسی تعداد کسب و کارها با هر ارز
SELECT
c.code,
c.name,
COUNT(b.id) as business_count
FROM hesabixpy.businesses b
INNER JOIN hesabixpy.currencies c ON b.default_currency_id = c.id
GROUP BY c.id, c.code, c.name
ORDER BY business_count DESC;
```
**انتظار**:
- IRR: ~4,420 کسب و کار
- USD: ~40 کسب و کار
- AFN: ~10 کسب و کار
- IQD: ~3 کسب و کار
## نکات مهم
1. **همه کسب و کارها money_id دارند**: هیچ کسب و کاری بدون money_id نیست
2. **همه ارزهای قدیمی در جدید وجود دارند**: همه 4 ارز می‌توانند mapping شوند
3. **IRR پیش‌فرض است**: در صورت عدم تطابق، از IRR استفاده می‌شود
4. **Mapping بر اساس Code**: استفاده از `money.name` = `currencies.code` برای mapping
## ریسک‌ها و راه‌حل‌ها
### ریسک 1: Currency در دیتابیس جدید وجود ندارد
**احتمال**: بسیار کم (همه 4 ارز موجود هستند)
**راه‌حل**: استفاده از پیش‌فرض IRR و ثبت در لاگ
### ریسک 2: Mapping نادرست
**احتمال**: بسیار کم (بر اساس ISO code)
**راه‌حل**: اعتبارسنجی بعد از انتقال
### ریسک 3: Performance
**احتمال**: کم (فقط 4 ارز)
**راه‌حل**: استفاده از mapping table در memory
## نتیجه‌گیری
**روش پیشنهادی**: استفاده از Mapping Table بر اساس Code
**دلایل**:
1. ✅ دقیق و قابل اعتماد
2. ✅ همه ارزهای قدیمی در جدید وجود دارند
3. ✅ ساده برای پیاده‌سازی
4. ✅ قابل اعتبارسنجی
**پیاده‌سازی**:
- ایجاد `currency_id_mapping` در ابتدای اسکریپت انتقال
- استفاده از mapping در انتقال هر کسب و کار
- استفاده از IRR به عنوان پیش‌فرض در صورت عدم تطابق
**انتظار**:
- 4,420 کسب و کار با IRR
- 40 کسب و کار با USD
- 10 کسب و کار با AFN
- 3 کسب و کار با IQD