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

14 KiB
Executable file

تحلیل انتقال بخش انبارها

1. بررسی ساختار جداول

1.1. دیتابیس قدیمی (hesabixOld)

جدول storeroom (انبارها)

  • id: شناسه انبار
  • bid_id: شناسه کسب و کار
  • name: نام انبار
  • manager: نام انباردار
  • adr: آدرس
  • tel: تلفن
  • active: فعال/غیرفعال

آمار:

  • تعداد کل انبارها: 1,185
  • تعداد کسب و کارهای دارای انبار: 924

جدول storeroom_ticket (اسناد انبار)

  • id: شناسه سند
  • bid_id: شناسه کسب و کار
  • submitter_id: شناسه کاربر ایجادکننده
  • person_id: شناسه شخص (مشتری/تامین‌کننده)
  • doc_id: شناسه فاکتور مرتبط
  • year_id: شناسه سال مالی
  • storeroom_id: شناسه انبار
  • transfer_type_id: شناسه نوع انتقال (ارجاع به storeroom_transfer_type)
  • date: تاریخ سند (فرمت: 1402/08/27)
  • date_submit: تاریخ ثبت (timestamp)
  • transfer: نام انتقال‌دهنده
  • receiver: نام دریافت‌کننده
  • code: کد سند
  • type: نوع سند (input یا output)
  • referral: ارجاع
  • type_string: نوع سند به فارسی (مثل "حواله ورود"، "حواله خروج")
  • des: توضیحات
  • sender_tel: تلفن فرستنده
  • can_share: قابل اشتراک‌گذاری
  • import_workflow_code: کد workflow واردات
  • activation_code: کد فعال‌سازی
  • is_preview: پیش‌نمایش
  • is_approved: تایید شده
  • completed: تکمیل شده
  • completed_at: تاریخ تکمیل
  • approved_by_id: شناسه تاییدکننده
  • completed_by_id: شناسه تکمیل‌کننده

آمار:

  • تعداد کل اسناد: 4,564
    • input: 1,511
    • output: 3,053

جدول storeroom_item (آیتم‌های سند انبار)

  • id: شناسه آیتم
  • ticket_id: شناسه سند انبار
  • commodity_id: شناسه کالا
  • bid_id: شناسه کسب و کار
  • storeroom_id: شناسه انبار
  • type: نوع (input یا output)
  • count: تعداد (varchar)
  • des: توضیحات
  • referal: ارجاع

آمار:

  • تعداد کل آیتم‌ها: 12,645
    • input: 5,359
    • output: 7,286

جدول storeroom_transfer_type (نوع انتقال)

  • id: شناسه
  • name: نام (مثل "تحویل درب انبار"، "پست عادی"، "پست پیشتاز"، "باربری"، "اتوبوس"، "تیپاکس"، "پیک")

1.2. دیتابیس جدید (hesabixpy)

جدول warehouses (انبارها)

  • id: شناسه انبار
  • business_id: شناسه کسب و کار
  • code: کد یکتا در هر کسب و کار (varchar(64))
  • name: نام انبار (varchar(255))
  • description: توضیحات (text)
  • warehouse_keeper: نام انباردار (varchar(255))
  • phone: تلفن (varchar(32))
  • address: آدرس (text)
  • postal_code: کد پستی (varchar(16))
  • is_default: انبار پیش‌فرض (boolean)
  • created_at: تاریخ ایجاد
  • updated_at: تاریخ به‌روزرسانی

محدودیت‌ها:

  • UNIQUE(business_id, code): کد باید در هر کسب و کار یکتا باشد

جدول warehouse_documents (اسناد انبار)

  • id: شناسه سند
  • business_id: شناسه کسب و کار
  • fiscal_year_id: شناسه سال مالی
  • code: کد سند (varchar(64), UNIQUE)
  • document_date: تاریخ سند (date)
  • status: وضعیت (draft|posted|cancelled)
  • doc_type: نوع سند (receipt|issue|transfer|production_in|production_out|adjustment)
  • warehouse_id_from: شناسه انبار مبدا (برای transfer)
  • warehouse_id_to: شناسه انبار مقصد (برای transfer)
  • source_type: نوع منبع (invoice|manual|api)
  • source_document_id: شناسه سند منبع
  • extra_info: اطلاعات اضافی (JSON)
  • created_by_user_id: شناسه کاربر ایجادکننده
  • created_at: تاریخ ایجاد
  • updated_at: تاریخ به‌روزرسانی

جدول warehouse_document_lines (خطوط سند انبار)

  • id: شناسه خط
  • warehouse_document_id: شناسه سند انبار
  • product_id: شناسه کالا
  • warehouse_id: شناسه انبار
  • movement: نوع حرکت (in|out)
  • quantity: تعداد (decimal(18,6))
  • cost_price: قیمت تمام شده (decimal(18,6))
  • cogs_amount: مبلغ COGS (decimal(18,6))
  • extra_info: اطلاعات اضافی (JSON)
  • instance_ids: لیست ID کالاهای یونیک (JSON)

2. نگاشت فیلدها

2.1. انبارها (storeroom → warehouses)

فیلد قدیمی فیلد جدید توضیحات
id - استفاده نمی‌شود (ایجاد جدید)
bid_id business_id نیاز به mapping
name name مستقیم
manager warehouse_keeper مستقیم
adr address مستقیم
tel phone مستقیم
active - فقط انبارهای active منتقل می‌شوند
- code باید تولید شود (مثلاً از name یا id)
- description می‌تواند از name یا address استفاده کند
- postal_code NULL
- is_default اولین انبار هر کسب و کار = true

2.2. اسناد انبار (storeroom_ticket → warehouse_documents)

فیلد قدیمی فیلد جدید توضیحات
id - استفاده نمی‌شود
bid_id business_id نیاز به mapping
submitter_id created_by_user_id نیاز به mapping
person_id - در extra_info ذخیره می‌شود
doc_id source_document_id نیاز به mapping (اگر فاکتور مرتبط باشد)
year_id fiscal_year_id نیاز به mapping
storeroom_id warehouse_id_from یا warehouse_id_to بستگی به type دارد
transfer_type_id - در extra_info ذخیره می‌شود
date document_date تبدیل از شمسی به میلادی
date_submit created_at تبدیل از timestamp به datetime
code code مستقیم (با بررسی یکتایی)
type doc_type تبدیل: input → receipt, output → issue
type_string - در extra_info ذخیره می‌شود
des - در extra_info ذخیره می‌شود
transfer - در extra_info ذخیره می‌شود
receiver - در extra_info ذخیره می‌شود
sender_tel - در extra_info ذخیره می‌شود
referral - در extra_info ذخیره می‌شود
- status بر اساس completed و is_approved: completed=1 → posted, در غیر این صورت draft
- warehouse_id_from برای transfer (اگر type_string شامل "انتقال" باشد)
- warehouse_id_to برای transfer
- source_type اگر doc_id وجود داشته باشد → invoice, در غیر این صورت manual

2.3. آیتم‌های سند (storeroom_item → warehouse_document_lines)

فیلد قدیمی فیلد جدید توضیحات
id - استفاده نمی‌شود
ticket_id warehouse_document_id نیاز به mapping
commodity_id product_id نیاز به mapping
storeroom_id warehouse_id نیاز به mapping
type movement تبدیل: input → in, output → out
count quantity تبدیل از varchar به decimal
des - در extra_info ذخیره می‌شود
referal - در extra_info ذخیره می‌شود
- cost_price NULL (می‌تواند از قیمت کالا محاسبه شود)
- cogs_amount NULL
- instance_ids NULL

3. تبدیل‌های مورد نیاز

3.1. تبدیل نوع سند (type → doc_type)

def convert_doc_type(old_type: str, type_string: str | None) -> str:
    """
    تبدیل type قدیمی به doc_type جدید
    
    Args:
        old_type: 'input' یا 'output'
        type_string: نوع سند به فارسی (مثل "حواله ورود")
    
    Returns:
        doc_type جدید: 'receipt'|'issue'|'transfer'|'production_in'|'production_out'|'adjustment'
    """
    if old_type == "input":
        # بررسی type_string برای تشخیص نوع دقیق
        if type_string and "انتقال" in type_string:
            return "transfer"
        elif type_string and "تولید" in type_string:
            return "production_in"
        else:
            return "receipt"
    elif old_type == "output":
        if type_string and "انتقال" in type_string:
            return "transfer"
        elif type_string and "تولید" in type_string:
            return "production_out"
        else:
            return "issue"
    else:
        return "adjustment"  # پیش‌فرض

3.2. تبدیل وضعیت (completed/is_approved → status)

def convert_status(completed: int | None, is_approved: int | None) -> str:
    """
    تبدیل وضعیت قدیمی به status جدید
    
    Returns:
        'draft'|'posted'|'cancelled'
    """
    if completed == 1:
        return "posted"
    elif is_approved == 0:
        return "cancelled"
    else:
        return "draft"

3.3. تبدیل تاریخ

  • date: از فرمت شمسی (1402/08/27) به date
  • date_submit: از timestamp به datetime

3.4. تبدیل تعداد (count → quantity)

def convert_quantity(count_str: str | None) -> Decimal | None:
    """
    تبدیل count از varchar به decimal
    """
    if not count_str or not count_str.strip():
        return None
    try:
        cleaned = count_str.strip().replace(',', '').replace(' ', '').replace('،', '')
        if not cleaned or cleaned == '0':
            return None
        return Decimal(cleaned)
    except (ValueError, InvalidOperation, TypeError):
        return None

3.5. تولید کد انبار (code)

def generate_warehouse_code(name: str, existing_codes: set) -> str:
    """
    تولید کد یکتا برای انبار
    """
    # استفاده از name (با محدودیت 64 کاراکتر)
    base_code = name[:64].strip()
    
    # اگر تکراری است، اضافه کردن شماره
    code = base_code
    counter = 1
    while code in existing_codes:
        suffix = f"_{counter}"
        code = (base_code[:64-len(suffix)] + suffix)
        counter += 1
    
    return code

4. روابط و وابستگی‌ها

4.1. وابستگی‌های انبارها

  • business_id: نیاز به mapping از bid_id قدیمی به business_id جدید
  • code: باید یکتا در هر کسب و کار باشد

4.2. وابستگی‌های اسناد انبار

  • business_id: نیاز به mapping
  • fiscal_year_id: نیاز به mapping از year_id
  • warehouse_id_from/warehouse_id_to: نیاز به mapping از storeroom_id
  • created_by_user_id: نیاز به mapping از submitter_id
  • source_document_id: نیاز به mapping از doc_id (اگر فاکتور مرتبط باشد)
  • code: باید یکتا باشد (UNIQUE)

4.3. وابستگی‌های خطوط سند

  • warehouse_document_id: نیاز به mapping از ticket_id
  • product_id: نیاز به mapping از commodity_id
  • warehouse_id: نیاز به mapping از storeroom_id

5. چالش‌ها و راه‌حل‌ها

5.1. یکتایی کد سند (code)

  • مشکل: code در warehouse_documents باید یکتا باشد
  • راه‌حل: اگر تکراری است، اضافه کردن suffix (مثلاً _1, _2)

5.2. تشخیص نوع سند (transfer vs receipt/issue)

  • مشکل: type فقط input/output است، اما باید transfer را تشخیص دهیم
  • راه‌حل: استفاده از type_string برای تشخیص

5.3. mapping فاکتورها (doc_id → source_document_id)

  • مشکل: doc_id در دیتابیس قدیمی به فاکتورها اشاره می‌کند که هنوز منتقل نشده‌اند
  • راه‌حل: اگر فاکتورها منتقل نشده‌اند، source_document_id را NULL بگذاریم و source_type را manual کنیم

5.4. انبار پیش‌فرض (is_default)

  • راه‌حل: اولین انبار هر کسب و کار را is_default = true کنیم

5.5. تبدیل تاریخ شمسی

  • راه‌حل: استفاده از کتابخانه jdatetime برای تبدیل تاریخ شمسی به میلادی

6. مراحل انتقال

  1. انتقال انبارها (warehouses)

    • ایجاد mapping بین storeroom_id قدیمی و warehouse_id جدید
    • فقط انبارهای active = 1 را منتقل کنیم
  2. انتقال اسناد انبار (warehouse_documents)

    • ایجاد mapping بین ticket_id قدیمی و warehouse_document_id جدید
    • تبدیل type به doc_type
    • تبدیل تاریخ‌ها
    • تبدیل وضعیت
  3. انتقال خطوط سند (warehouse_document_lines)

    • استفاده از mapping های ایجاد شده
    • تبدیل count به quantity
    • تبدیل type به movement

7. آمار کلی

  • انبارها: 1,185
  • اسناد انبار: 4,564
    • ورود: 1,511
    • خروج: 3,053
  • آیتم‌های سند: 12,645
    • ورود: 5,359
    • خروج: 7,286
  • کسب و کارهای دارای انبار: 924