Watch
1
0
Fork
You've already forked Seyyed_arc
0
forked from hesabix/arc
Seyyed_arc/hesabixAPI/docs/DOCUMENTS_MIGRATION_SCENARIO.md
2026-04-14 19:34:55 +03:30

945 lines
37 KiB
Markdown
Executable file

# سناریوی انتقال اسناد حسابداری از hesabixOld به hesabixpy
## خلاصه اجرایی
این سند سناریوی کامل انتقال اسناد حسابداری از دیتابیس قدیمی (`hesabixOld`) به دیتابیس جدید (`hesabixpy`) را ارائه می‌دهد. تمرکز اصلی بر روی نحوه استفاده از منطق و سرویس‌های سیستم جدید برای ثبت صحیح اسناد است.
## نگاشت انواع اسناد
### جدول نگاشت کامل
| نوع قدیمی (hesabdari_doc.type) | نوع جدید (documents.document_type) | سرویس مورد استفاده | توضیحات |
|--------------------------------|-------------------------------------|---------------------|---------|
| `sell` | `invoice_sales` | `invoice_service.create_invoice()` | فاکتور فروش |
| `rfsell` | `invoice_sales_return` | `invoice_service.create_invoice()` | برگشت از فروش |
| `buy` | `invoice_purchase` | `invoice_service.create_invoice()` | فاکتور خرید |
| `rfbuy` | `invoice_purchase_return` | `invoice_service.create_invoice()` | برگشت از خرید |
| `person_receive` | `receipt` | `receipt_payment_service.create_receipt_payment()` | دریافت از اشخاص |
| `person_send` | `payment` | `receipt_payment_service.create_receipt_payment()` | پرداخت به اشخاص |
| `sell_receive` | `receipt` | `receipt_payment_service.create_receipt_payment()` | دریافت از فروش |
| `buy_send` | `payment` | `receipt_payment_service.create_receipt_payment()` | پرداخت برای خرید |
| `cost` | `expense` | `expense_income_service.create_expense_income()` | هزینه |
| `income` | `income` | `expense_income_service.create_expense_income()` | درآمد |
| `transfer` | `transfer` | `transfer_service.create_transfer()` | انتقال بین حساب‌ها |
| `modify_cheque` | `check` | `check_service.create_check()` | ثبت/تغییر وضعیت چک |
| `modify_cheque_output` | `check` | `check_service.create_check()` | ثبت/تغییر وضعیت چک پرداختی |
| `pass_cheque` | `check_clear` | `check_service.clear_check()` | وصول چک |
| `transfer_cheque` | `check_endorse` | `check_service.endorse_check()` | واگذاری/پاسخگویی چک |
| `open_balance` | `opening_balance` | `document_service.create_manual_document()` | تراز افتتاحیه |
| سایر | `manual` | `document_service.create_manual_document()` | سند دستی |
## مهم: استفاده از حساب‌های سیستم جدید
⚠️ **نکته بسیار مهم**: در سیستم جدید، **نیازی به نگاشت جدول حساب‌ها نیست**. سیستم جدید از **حساب‌های ثابت با کد** استفاده می‌کند که در چارت حساب پیش‌فرض قرار دارند. این حساب‌ها از طریق کد خودشان پیدا می‌شوند (مثل 10401, 20201, 50002 و...).
✅ **تنها برای اسناد دستی (manual)** نیاز به نگاشت `ref_id` قدیمی به `account_id` جدید است.
## سناریوی انتقال برای هر نوع سند
### 1. فاکتور فروش (sell → invoice_sales)
#### منطق سیستم جدید:
```python
invoice_service.create_invoice(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"invoice_type": "invoice_sales",
"document_date": "2025-01-15",
"currency_id": new_currency_id,
"person_id": new_person_id,
"lines": [
{
"product_id": new_product_id,
"quantity": 10,
"unit_price": 100000,
"discount": 0,
"tax_percent": 9
}
],
"description": "توضیحات فاکتور",
"extra_info": {
"source": "migration",
"old_document_id": old_doc_id
}
}
)
```
#### ثبت‌های حسابداری خودکار:
- **بدهکار**: حساب شخص (10401) = مبلغ کل با مالیات
- **بستانکار**: حساب درآمد (50001) = مبلغ ناخالص
- **بدهکار**: حساب مالیات خروجی (20101) = مبلغ مالیات
- **بستانکار**: حساب تخفیفات فروش (50003) = مبلغ تخفیف (اگر وجود داشته باشد)
#### نکات انتقال:
1. ✅ استفاده از سرویس `create_invoice()` - ثبت‌های حسابداری خودکار انجام می‌شود
2. ✅ نیاز به mapping: `person_id`, `product_id`, `currency_id`, `business_id`
3. ✅ محاسبه `gross`, `discount`, `tax`, `net` از سطرهای قدیمی
4. ✅ اگر `is_preview = 1` باشد، `is_proforma = True` تنظیم شود
---
### 2. برگشت از فروش (rfsell → invoice_sales_return)
#### منطق سیستم جدید:
```python
invoice_service.create_invoice(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"invoice_type": "invoice_sales_return",
"document_date": "2025-01-15",
"currency_id": new_currency_id,
"person_id": new_person_id,
"lines": [...],
"description": "برگشت از فروش"
}
)
```
#### ثبت‌های حسابداری خودکار:
- **بستانکار**: حساب شخص (10401) = مبلغ کل با مالیات
- **بدهکار**: حساب برگشت از فروش (50002) = مبلغ ناخالص
- **بستانکار**: حساب تخفیفات فروش (50003) = مبلغ تخفیف (برگشت)
- **بدهکار**: حساب مالیات خروجی (20101) = مبلغ مالیات (تعدیل)
#### نکات انتقال:
1. ⚠️ **مهم**: در دیتابیس قدیمی ممکن است ساختار متفاوتی داشته باشد
2. ⚠️ **مهم**: ref_id=3 (حساب‌های دریافتی) باید به حساب دریافتنی (10401) نگاشت شود
3. ⚠️ **مهم**: ref_id=137 (موجودی کالا) در سیستم جدید در پست حواله انجام می‌شود
4. ✅ استفاده از سرویس `create_invoice()` - ثبت‌های حسابداری خودکار انجام می‌شود
5. ✅ **نباید** سطرهای قدیمی را مستقیماً کپی کنیم - باید از منطق جدید استفاده کنیم
---
### 3. فاکتور خرید (buy → invoice_purchase)
#### منطق سیستم جدید:
```python
invoice_service.create_invoice(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"invoice_type": "invoice_purchase",
"document_date": "2025-01-15",
"currency_id": new_currency_id,
"person_id": new_person_id,
"lines": [...],
"description": "فاکتور خرید"
}
)
```
#### ثبت‌های حسابداری خودکار:
- **بدهکار**: حساب GRNI (30101) = مبلغ ناخالص
- **بستانکار**: حساب تخفیفات خرید (40003) = مبلغ تخفیف
- **بدهکار**: حساب مالیات ورودی (10104) = مبلغ مالیات
- **بستانکار**: حساب شخص (20201) = مبلغ کل با مالیات
---
### 4. برگشت از خرید (rfbuy → invoice_purchase_return)
#### منطق سیستم جدید:
```python
invoice_service.create_invoice(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"invoice_type": "invoice_purchase_return",
"document_date": "2025-01-15",
"currency_id": new_currency_id,
"person_id": new_person_id,
"lines": [...],
"description": "برگشت از خرید"
}
)
```
#### ثبت‌های حسابداری خودکار:
- **بدهکار**: حساب شخص (20201) = مبلغ کل با مالیات
- **بستانکار**: حساب GRNI (30101) = مبلغ ناخالص
- **بدهکار**: حساب تخفیفات خرید (40003) = مبلغ تخفیف (برگشت)
- **بستانکار**: حساب مالیات ورودی (10104) = مبلغ مالیات (تعدیل)
#### نکات انتقال:
1. ⚠️ **مهم**: در دیتابیس قدیمی ref_id=3 (حساب‌های دریافتی) استفاده شده - باید به حساب پرداختنی (20201) نگاشت شود
2. ⚠️ **مهم**: ref_id=59 (فروش کالا) باید به GRNI (30101) نگاشت شود
3. ✅ استفاده از سرویس `create_invoice()` - ثبت‌های حسابداری خودکار انجام می‌شود
---
### 5. دریافت از اشخاص (person_receive → receipt)
#### منطق سیستم جدید:
```python
receipt_payment_service.create_receipt_payment(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"document_type": "receipt",
"document_date": "2025-01-15",
"currency_id": new_currency_id,
"person_lines": [
{
"person_id": new_person_id,
"amount": 1000000,
"description": "دریافت از مشتری"
}
],
"account_lines": [
{
"account_id": bank_account.account.id, # حساب بانک
"amount": 1000000,
"transaction_type": "bank",
"bank_id": bank_account.id
}
],
"description": "دریافت وجه"
}
)
```
#### ثبت‌های حسابداری:
- **بدهکار**: حساب بانک/صندوق/تنخواه = مبلغ
- **بستانکار**: حساب شخص (10401) = مبلغ
#### نکات انتقال:
1. ✅ نیاز به تشخیص نوع حساب (bank_id, cashdesk_id, salary_id)
2. ✅ نگاشت به `bank_account_id`, `cash_register_id`, `petty_cash_id`
3. ✅ مجموع `person_lines` باید برابر `account_lines` باشد
---
### 6. پرداخت به اشخاص (person_send → payment)
#### منطق سیستم جدید:
```python
receipt_payment_service.create_receipt_payment(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"document_type": "payment",
"document_date": "2025-01-15",
"currency_id": new_currency_id,
"person_lines": [
{
"person_id": new_person_id,
"amount": 1000000,
"description": "پرداخت به تامین‌کننده"
}
],
"account_lines": [
{
"account_id": bank_account.account.id,
"amount": 1000000,
"transaction_type": "bank",
"bank_id": bank_account.id
}
],
"description": "پرداخت وجه"
}
)
```
#### ثبت‌های حسابداری:
- **بدهکار**: حساب شخص (20201) = مبلغ
- **بستانکار**: حساب بانک/صندوق/تنخواه = مبلغ
---
### 7. هزینه (cost → expense)
#### منطق سیستم جدید:
```python
expense_income_service.create_expense_income(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"document_type": "expense",
"document_date": "2025-01-15",
"currency_id": new_currency_id,
"item_lines": [
{
"account_id": expense_account.id, # حساب هزینه
"amount": 500000,
"description": "هزینه حمل"
}
],
"counterparty_lines": [
{
"transaction_type": "bank",
"amount": 500000,
"bank_id": bank_account.id
}
],
"description": "هزینه حمل و نقل"
}
)
```
#### ثبت‌های حسابداری:
- **بدهکار**: حساب هزینه = مبلغ
- **بستانکار**: حساب بانک/صندوق/تنخواه/شخص = مبلغ
#### نکات انتقال:
1. ✅ `item_lines`: سطرهای حساب‌های هزینه
2. ✅ `counterparty_lines`: سطرهای طرف‌حساب (بانک/صندوق/شخص/چک)
3. ✅ مجموع `item_lines` باید برابر `counterparty_lines` باشد
---
### 8. درآمد (income → income)
#### منطق سیستم جدید:
```python
expense_income_service.create_expense_income(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"document_type": "income",
"document_date": "2025-01-15",
"currency_id": new_currency_id,
"item_lines": [
{
"account_id": income_account.id, # حساب درآمد
"amount": 2000000,
"description": "درآمد فروش"
}
],
"counterparty_lines": [
{
"transaction_type": "bank",
"amount": 2000000,
"bank_id": bank_account.id
}
],
"description": "دریافت درآمد"
}
)
```
#### ثبت‌های حسابداری:
- **بدهکار**: حساب بانک/صندوق/تنخواه/شخص = مبلغ
- **بستانکار**: حساب درآمد = مبلغ
---
### 9. انتقال (transfer → transfer)
#### منطق سیستم جدید:
```python
transfer_service.create_transfer(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"document_date": "2025-01-15",
"currency_id": new_currency_id,
"source": {
"type": "bank", # bank | cash_register | petty_cash
"id": source_bank_account.id
},
"destination": {
"type": "cash_register",
"id": destination_cash_register.id
},
"amount": 5000000,
"commission": 5000, # اختیاری
"description": "انتقال از بانک به صندوق"
}
)
```
#### ثبت‌های حسابداری خودکار:
- **بدهکار**: حساب مقصد (بانک/صندوق/تنخواه) = مبلغ
- **بستانکار**: حساب مبدأ (بانک/صندوق/تنخواه) = مبلغ
- **بدهکار**: حساب کارمزد (70902) = کارمزد (اگر وجود داشته باشد)
- **بستانکار**: حساب مبدأ = کارمزد (اگر وجود داشته باشد)
#### نکات انتقال:
1. ✅ نیاز به تشخیص نوع source و destination از ref_id
2. ✅ نگاشت bank_id → bank_account_id
3. ✅ نگاشت cashdesk_id → cash_register_id
4. ✅ نگاشت salary_id → petty_cash_id
---
### 10. چک‌ها (modify_cheque, modify_cheque_output, pass_cheque, transfer_cheque)
#### آمار در دیتابیس قدیمی:
- `modify_cheque`: 1,250 سند (ثبت/تغییر چک)
- `modify_cheque_output`: 535 سند (ثبت چک پرداختی)
- `pass_cheque`: 306 سند (وصول چک)
- `transfer_cheque`: 299 سند (انتقال/واگذاری چک)
#### منطق سیستم جدید:
چک‌ها در سیستم جدید به صورت جداگانه در جدول `checks` مدیریت می‌شوند. هر عملیات روی چک یک سند حسابداری ایجاد می‌کند که به صورت خودکار ثبت می‌شود.
##### 10.1 ثبت چک دریافتی (modify_cheque → create_check)
```python
check_service.create_check(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"type": "received", # چک دریافتی
"person_id": new_person_id,
"issue_date": "2025-01-15",
"due_date": "2025-02-15",
"check_number": "123456",
"amount": 10000000,
"currency_id": new_currency_id,
"bank_name": "بانک ملی",
"branch_name": "شعبه مرکزی",
"sayad_code": "1234567890123456", # اختیاری
"document_date": "2025-01-15",
"document_description": "ثبت چک دریافتی"
}
)
```
**ثبت‌های حسابداری خودکار**:
- **بدهکار**: حساب اسناد دریافتنی (10403) = مبلغ چک
- **بستانکار**: حساب شخص (10401) = مبلغ چک
##### 10.2 ثبت چک پرداختی (modify_cheque_output → create_check)
```python
check_service.create_check(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"type": "transferred", # چک پرداختی
"person_id": new_person_id,
"issue_date": "2025-01-15",
"due_date": "2025-02-15",
"check_number": "654321",
"amount": 5000000,
"currency_id": new_currency_id,
"bank_name": "بانک ملت",
"document_date": "2025-01-15",
"document_description": "ثبت چک پرداختی"
}
)
```
**ثبت‌های حسابداری خودکار**:
- **بدهکار**: حساب شخص (20201) = مبلغ چک
- **بستانکار**: حساب اسناد پرداختنی (20202) = مبلغ چک
##### 10.3 وصول چک (pass_cheque → clear_check)
```python
# ابتدا باید چک را پیدا کنیم یا ایجاد کنیم
check = find_or_create_check_from_old_document(old_doc)
# سپس وصول چک
check_service.clear_check(
db=db,
check_id=check.id,
user_id=new_user_id,
data={
"document_date": "2025-02-15",
"bank_account_id": bank_account.id,
"description": "وصول چک"
}
)
```
**ثبت‌های حسابداری خودکار**:
- **بدهکار**: حساب بانک (10203) = مبلغ چک
- **بستانکار**: حساب اسناد دریافتنی (10403) یا اسناد در جریان وصول (10404) = مبلغ چک
##### 10.4 واگذاری/پاسخگویی چک (transfer_cheque → endorse_check)
```python
# ابتدا باید چک را پیدا کنیم
check = find_or_create_check_from_old_document(old_doc)
# سپس واگذاری چک
check_service.endorse_check(
db=db,
check_id=check.id,
user_id=new_user_id,
data={
"document_date": "2025-01-20",
"to_person_id": new_person_id,
"description": "واگذاری چک"
}
)
```
**ثبت‌های حسابداری خودکار**:
- **بدهکار**: حساب شخص جدید (10401) = مبلغ چک
- **بستانکار**: حساب اسناد دریافتنی (10403) = مبلغ چک
#### نکات مهم انتقال چک‌ها:
1. ⚠️ **چک‌ها باید از جدول `cheque` در دیتابیس قدیمی منتقل شوند**
- جدول `cheque` شامل اطلاعات کامل چک‌ها است
- اسناد `hesabdari_doc` با نوع چک، فقط عملیات روی چک هستند
2. ⚠️ **ترتیب انتقال چک‌ها**:
- ابتدا باید چک‌ها از جدول `cheque` منتقل شوند
- سپس اسناد مربوط به چک‌ها (modify_cheque, pass_cheque و...) منتقل شوند
3. ✅ **استفاده از حساب‌های ثابت**:
- سیستم جدید از حساب‌های ثابت با کد استفاده می‌کند:
- 10403: اسناد دریافتنی
- 10404: اسناد در جریان وصول
- 20202: اسناد پرداختنی
- 10401: حساب‌های دریافتنی
- 20201: حساب‌های پرداختنی
- 10203: بانک
4. ✅ **استفاده از سرویس‌های سیستم جدید**:
- `create_check()`: برای ثبت چک
- `clear_check()`: برای وصول چک
- `deposit_check()`: برای سپرده چک
- `endorse_check()`: برای واگذاری چک
- `return_check()`: برای عودت چک
- `bounce_check()`: برای برگشت خوردن چک
5. ⚠️ **تحلیل اسناد چک در دیتابیس قدیمی**:
- `modify_cheque`: معمولاً ثبت چک جدید یا تغییر وضعیت
- `pass_cheque`: وصول چک (معمولاً cheque_id در hesabdari_row وجود دارد)
- `transfer_cheque`: واگذاری/پاسخگویی چک
- باید بررسی کنیم که آیا cheque_id در hesabdari_row وجود دارد یا نه
---
### 11. سند دستی (calc, سایر → manual)
#### منطق سیستم جدید:
```python
document_service.create_manual_document(
db=db,
business_id=new_business_id,
fiscal_year_id=new_fiscal_year_id,
user_id=new_user_id,
data={
"code": "MAN-20250115-0001",
"document_date": "2025-01-15",
"currency_id": new_currency_id,
"document_type": "manual",
"description": "سند دستی",
"lines": [
{
"account_id": account1.id,
"debit": 1000000,
"credit": 0,
"description": "بدهکار"
},
{
"account_id": account2.id,
"debit": 0,
"credit": 1000000,
"description": "بستانکار"
}
]
}
)
```
#### نکات انتقال:
1. ✅ برای اسنادی که منطق خاصی ندارند (مثلاً calc)
2. ✅ باید تعادل بدهکار/بستانکار بررسی شود
3. ⚠️ **فقط برای اسناد دستی نیاز به نگاشت ref_id → account_id است**
4. ✅ برای نگاشت حساب‌ها در اسناد دستی:
- باید mapping بین hesabdari_table و accounts ایجاد شود
- بر اساس business_id (اگر مخصوص کسب و کار باشد) یا NULL (حساب عمومی)
- بر اساس کد حساب (code)
- بر اساس نوع حساب (type → account_type)
---
## چالش‌ها و راهکارها
### چالش 1: استفاده از حساب‌های ثابت سیستم جدید
**مهم**: در سیستم جدید، **نیازی به نگاشت جدول حساب‌ها نیست**. سیستم جدید از **حساب‌های ثابت با کد** استفاده می‌کند که در چارت حساب پیش‌فرض قرار دارند.
**حساب‌های ثابت سیستم جدید** (با کد):
- **10201**: تنخواه گردان
- **10202**: صندوق
- **10203**: بانک
- **10401**: حساب‌های دریافتنی
- **10403**: اسناد دریافتنی
- **10404**: اسناد در جریان وصول
- **20201**: حساب‌های پرداختنی
- **20202**: اسناد پرداختنی
- **30101**: GRNI
- **50001**: درآمد
- **50002**: برگشت از فروش
- **50003**: تخفیفات فروش
- **40003**: تخفیفات خرید
- **20101**: مالیات بر ارزش افزوده خروجی
- **10104**: مالیات بر ارزش افزوده ورودی
- و سایر حساب‌های ثابت...
**راهکار**:
1. ✅ **برای فاکتورها، دریافت/پرداخت، هزینه/درآمد، انتقال و چک**: از سرویس‌های سیستم جدید استفاده می‌کنیم که حساب‌ها را خودکار از کد پیدا می‌کنند
2. ✅ **فقط برای اسناد دستی (manual)**: نیاز به نگاشت `ref_id` به `account_id` است
3. ✅ **سیستم جدید حساب‌ها را از کد پیدا می‌کند**: `_get_fixed_account_by_code(db, "10401")`
### چالش 2: ساختار متفاوت ثبت‌های حسابداری
**مشکل**: در دیتابیس قدیمی ممکن است ساختار متفاوتی داشته باشد (مثلاً در rfbuy از ref_id=3 استفاده شده که باید حساب پرداختنی باشد).
**راهکار**:
1. ✅ **استفاده از سرویس‌های سیستم جدید** - ثبت‌های حسابداری خودکار و صحیح انجام می‌شود
2. ✅ **نباید سطرهای قدیمی را مستقیماً کپی کنیم** - باید از منطق جدید استفاده کنیم
3. ✅ **سیستم جدید حساب‌ها را از کد پیدا می‌کند** - نیازی به نگاشت ref_id نیست
4. ✅ باید از منطق جدید استفاده کنیم:
- برای فاکتورها: `invoice_service.create_invoice()` - حساب‌ها خودکار
- برای دریافت/پرداخت: `receipt_payment_service.create_receipt_payment()` - حساب‌ها خودکار
- برای هزینه/درآمد: `expense_income_service.create_expense_income()` - حساب‌ها خودکار
- برای انتقال: `transfer_service.create_transfer()` - حساب‌ها خودکار
- برای چک: `check_service.*()` - حساب‌ها خودکار
- فقط برای سند دستی: نیاز به نگاشت account_id داریم
### چالش 3: تبدیل تاریخ
**مشکل**: در دیتابیس قدیمی تاریخ به صورت varchar (timestamp یا جلالی) ذخیره شده.
**راهکار**:
1. تبدیل timestamp به datetime
2. تبدیل تاریخ جلالی به میلادی
3. استفاده از `CalendarConverter` برای تبدیل تاریخ
### چالش 4: حجم زیاد داده
**مشکل**: 126,447 سند نیاز به انتقال دارد.
**راهکار**:
1. انتقال به صورت batch (مثلاً 100 سند در هر batch)
2. استفاده از transaction برای هر batch
3. فقط اسناد تایید شده (`is_approved = 1`) و غیر پیش‌نمایش (`is_preview = 0`) را منتقل کنیم
4. لاگ کامل از عملیات انتقال
### چالش 5: وابستگی‌ها
**مشکل**: قبل از انتقال اسناد، باید این جداول منتقل شده باشند.
**راهکار**:
1. ترتیب انتقال:
- ✅ users
- ✅ businesses
- ✅ fiscal_years
- ✅ currencies
- ✅ accounts (حساب‌های عمومی پیش‌فرض باید وجود داشته باشند - از migration)
- ✅ persons
- ✅ products (commodity → products)
- ✅ bank_accounts
- ✅ cash_registers
- ✅ petty_cash
- ✅ checks (از جدول cheque در دیتابیس قدیمی)
- ✅ **documents** (آخرین مرحله)
**نکته مهم**: حساب‌های عمومی (با business_id = NULL) از طریق migration ایجاد می‌شوند و نیازی به انتقال ندارند. فقط باید مطمئن شویم که حساب‌های مورد نیاز وجود دارند.
---
## الگوریتم پیشنهادی برای انتقال
### مرحله 1: آماده‌سازی
1. ایجاد mapping tables:
- `old_to_new_business_id`
- `old_to_new_user_id`
- `old_to_new_fiscal_year_id`
- `old_to_new_currency_id`
- `old_to_new_person_id`
- `old_to_new_product_id`
- `old_to_new_bank_account_id`
- `old_to_new_cash_register_id`
- `old_to_new_petty_cash_id`
- `old_to_new_check_id`
- `old_to_new_account_id` (فقط برای اسناد دستی - hesabdari_table → accounts)
2. بررسی وجود حساب‌های ثابت مورد نیاز:
- حساب‌های عمومی باید از طریق migration ایجاد شده باشند
- بررسی وجود حساب‌های با کد:
- 10401 (حساب‌های دریافتنی)
- 10403 (اسناد دریافتنی)
- 20201 (حساب‌های پرداختنی)
- 20202 (اسناد پرداختنی)
- 10203 (بانک)
- 10202 (صندوق)
- 10201 (تنخواه گردان)
- و سایر حساب‌های ثابت...
### مرحله 2: انتقال به ترتیب نوع
#### 2.1 فاکتورها (sell, rfsell, buy, rfbuy)
```python
for old_doc in old_invoices:
# تشخیص نوع فاکتور
invoice_type_map = {
"sell": "invoice_sales",
"rfsell": "invoice_sales_return",
"buy": "invoice_purchase",
"rfbuy": "invoice_purchase_return"
}
new_invoice_type = invoice_type_map[old_doc.type]
# استخراج اطلاعات از سطرهای قدیمی
lines = extract_lines_from_old_rows(old_doc.id)
# محاسبه totals
totals = calculate_totals(lines)
# ایجاد فاکتور با سرویس جدید
invoice_service.create_invoice(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"invoice_type": new_invoice_type,
"document_date": convert_date(old_doc.date),
"currency_id": new_currency_id,
"person_id": new_person_id,
"lines": convert_lines_to_new_format(lines),
"description": old_doc.des,
"is_proforma": old_doc.is_preview == 1,
"extra_info": {
"source": "migration",
"old_document_id": old_doc.id
}
}
)
```
#### 2.2 دریافت/پرداخت (person_receive, person_send, sell_receive, buy_send)
```python
for old_doc in old_receipts_payments:
# تشخیص نوع
doc_type_map = {
"person_receive": "receipt",
"sell_receive": "receipt",
"person_send": "payment",
"buy_send": "payment"
}
new_doc_type = doc_type_map[old_doc.type]
# استخراج person_lines و account_lines
person_lines, account_lines = extract_receipt_payment_lines(old_doc.id)
# ایجاد سند با سرویس جدید
receipt_payment_service.create_receipt_payment(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"document_type": new_doc_type,
"document_date": convert_date(old_doc.date),
"currency_id": new_currency_id,
"person_lines": person_lines,
"account_lines": account_lines,
"description": old_doc.des
}
)
```
#### 2.3 هزینه/درآمد (cost, income)
```python
for old_doc in old_expenses_incomes:
# تشخیص نوع
doc_type_map = {
"cost": "expense",
"income": "income"
}
new_doc_type = doc_type_map[old_doc.type]
# استخراج item_lines و counterparty_lines
item_lines, counterparty_lines = extract_expense_income_lines(old_doc.id)
# ایجاد سند با سرویس جدید
expense_income_service.create_expense_income(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"document_type": new_doc_type,
"document_date": convert_date(old_doc.date),
"currency_id": new_currency_id,
"item_lines": item_lines,
"counterparty_lines": counterparty_lines,
"description": old_doc.des
}
)
```
#### 2.4 انتقال (transfer)
```python
for old_doc in old_transfers:
# تشخیص source و destination از سطرها
source, destination = extract_transfer_source_destination(old_doc.id)
# ایجاد سند با سرویس جدید
transfer_service.create_transfer(
db=db,
business_id=new_business_id,
user_id=new_user_id,
data={
"document_date": convert_date(old_doc.date),
"currency_id": new_currency_id,
"source": source,
"destination": destination,
"amount": old_doc.amount,
"commission": old_doc.commission or 0,
"description": old_doc.des
}
)
```
#### 2.5 چک‌ها (modify_cheque, pass_cheque, transfer_cheque)
```python
# ابتدا چک‌ها از جدول cheque منتقل شده باشند
for old_doc in old_check_documents:
# پیدا کردن چک مربوطه
old_cheque = get_cheque_from_document(old_doc)
if old_doc.type == "modify_cheque":
# ثبت یا تغییر وضعیت چک
check = find_or_create_check(old_cheque)
elif old_doc.type == "pass_cheque":
# وصول چک
check = find_check_by_old_id(old_cheque.id)
if check:
check_service.clear_check(
db=db,
check_id=check.id,
user_id=new_user_id,
data={
"document_date": convert_date(old_doc.date),
"bank_account_id": get_bank_account_from_row(old_doc.id),
"description": old_doc.des
}
)
elif old_doc.type == "transfer_cheque":
# واگذاری چک
check = find_check_by_old_id(old_cheque.id)
if check:
check_service.endorse_check(
db=db,
check_id=check.id,
user_id=new_user_id,
data={
"document_date": convert_date(old_doc.date),
"to_person_id": get_person_from_row(old_doc.id),
"description": old_doc.des
}
)
```
#### 2.6 سایر (manual)
```python
for old_doc in old_other_documents:
# استخراج سطرها
lines = extract_lines_from_old_rows(old_doc.id)
# تبدیل به فرمت جدید - **فقط برای اسناد دستی نیاز به نگاشت account_id است**
new_lines = []
for line in lines:
# نگاشت ref_id به account_id (فقط برای اسناد دستی)
old_account_id = line.get("ref_id")
new_account_id = account_mapping.get(old_account_id) # از mapping استفاده می‌کنیم
if not new_account_id:
# اگر mapping پیدا نشد، لاگ می‌کنیم
log_warning(f"حساب با ref_id {old_account_id} برای سند {old_doc.id} پیدا نشد")
continue
new_lines.append({
"account_id": new_account_id,
"debit": line.get("bs", 0),
"credit": line.get("bd", 0),
"description": line.get("des"),
"person_id": person_mapping.get(line.get("person_id")),
"product_id": product_mapping.get(line.get("commodity_id")),
# سایر فیلدها...
})
# بررسی تعادل
if not is_balanced(new_lines):
log_warning(f"سند {old_doc.id} نامتعادل است")
continue
# ایجاد سند دستی
document_service.create_manual_document(
db=db,
business_id=new_business_id,
fiscal_year_id=new_fiscal_year_id,
user_id=new_user_id,
data={
"code": generate_code(old_doc.code),
"document_date": convert_date(old_doc.date),
"currency_id": new_currency_id,
"document_type": "manual",
"description": old_doc.des,
"lines": new_lines
}
)
```
### مرحله 3: Validation
1. بررسی تعادل همه اسناد منتقل شده
2. مقایسه مانده حساب‌ها قبل و بعد از انتقال
3. تست عملکرد اسناد در سیستم جدید
---
## نکات مهم
1. ✅ **همیشه از سرویس‌های سیستم جدید استفاده کنیم** - ثبت‌های حسابداری خودکار و صحیح انجام می‌شود
2. ✅ **نباید سطرهای قدیمی را مستقیماً کپی کنیم** - باید از منطق جدید استفاده کنیم
3. ✅ **استفاده از حساب‌های ثابت سیستم جدید** - نیازی به نگاشت حساب‌ها نیست، سیستم از کد حساب استفاده می‌کند (مثل 10401, 20201, 50002 و...)
4. ✅ **فقط برای اسناد دستی نیاز به نگاشت حساب** - باید ref_id را به account_id نگاشت کنیم
5. ✅ **چک‌ها ابتدا منتقل شوند** - قبل از انتقال اسناد مربوط به چک، باید چک‌ها از جدول cheque منتقل شده باشند
6. ✅ **بررسی تعادل** - قبل از انتقال، باید مطمئن شویم که سند متعادل است
7. ✅ **استفاده از transaction** - برای هر batch از transaction استفاده کنیم
8. ✅ **لاگ کامل** - تمام عملیات انتقال را لاگ کنیم
9. ✅ **Backup** - قبل از انتقال، backup کامل بگیریم
---
## نتیجه‌گیری
با استفاده از سرویس‌های سیستم جدید و منطق صحیح، می‌توانیم اسناد را به درستی منتقل کنیم.
**مهم‌ترین نکات**:
1. **نباید سطرهای قدیمی را مستقیماً کپی کنیم** - باید از منطق جدید سیستم استفاده کنیم که ثبت‌های حسابداری را به صورت خودکار و صحیح انجام می‌دهد
2. **سیستم جدید از حساب‌های ثابت با کد استفاده می‌کند** - نیازی به نگاشت جدول حساب‌ها نیست. سیستم از طریق کد حساب (مثل 10401, 20201, 50002) حساب‌ها را پیدا می‌کند. فقط برای اسناد دستی نیاز به نگاشت ref_id به account_id داریم.
3. **چک‌ها باید ابتدا منتقل شوند** - قبل از انتقال اسناد مربوط به چک (modify_cheque, pass_cheque و...)، باید چک‌ها از جدول cheque در دیتابیس قدیمی منتقل شده باشند.
4. **هر نوع سند از سرویس مخصوص خودش استفاده می‌کند** - فاکتورها، دریافت/پرداخت، هزینه/درآمد، انتقال و چک هر کدام سرویس و منطق خاص خود را دارند که ثبت‌های حسابداری را به صورت خودکار انجام می‌دهد.