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

20 KiB
Executable file

سناریوی ایمپورت فاکتورها از اکسل

📋 خلاصه

این سند سناریوی پیاده‌سازی قابلیت ایمپورت فاکتورها از فایل Excel را بررسی می‌کند. این قابلیت به کاربران امکان می‌دهد تا فاکتورهای متعدد را به صورت دسته‌ای از طریق فایل Excel وارد سیستم کنند.


🔍 بررسی ساختار فعلی

ساختار Backend

1. مدل‌های دیتابیس

  • Document: سند اصلی فاکتور

    • id, code, business_id, fiscal_year_id, currency_id
    • document_date, document_type, is_proforma
    • description, extra_info (JSON)
  • InvoiceItemLine: ردیف‌های فاکتور

    • id, document_id, product_id, quantity
    • description, extra_info (JSON)

2. سرویس‌های موجود

  • create_invoice در app/services/invoice_service.py:
    • دریافت payload شامل:
      • invoice_type: نوع فاکتور (sales, purchase, etc.)
      • document_date: تاریخ فاکتور
      • currency_id: شناسه ارز
      • is_proforma: پیش‌فاکتور یا قطعی
      • description: توضیحات
      • extra_info: اطلاعات اضافی (person_id, totals, etc.)
      • lines: لیست ردیف‌های فاکتور
      • payments: تراکنش‌های پرداخت (اختیاری)

3. انواع فاکتورهای پشتیبانی شده

INVOICE_SALES = "invoice_sales"
INVOICE_SALES_RETURN = "invoice_sales_return"
INVOICE_PURCHASE = "invoice_purchase"
INVOICE_PURCHASE_RETURN = "invoice_purchase_return"
INVOICE_DIRECT_CONSUMPTION = "invoice_direct_consumption"
INVOICE_PRODUCTION = "invoice_production"
INVOICE_WASTE = "invoice_waste"

4. الگوی موجود برای ایمپورت

در پروژه قابلیت ایمپورت برای محصولات و اشخاص پیاده‌سازی شده است:

  • محصولات: /api/v1/products/business/{business_id}/import/excel
  • اشخاص: /api/v1/persons/businesses/{business_id}/persons/import/excel

هر دو دارای:

  • Endpoint دانلود تمپلیت: /import/template
  • Endpoint ایمپورت: /import/excel با پارامترهای:
    • file: فایل Excel
    • dry_run: اعتبارسنجی بدون ذخیره
    • match_by: روش تطبیق (code/name)
    • conflict_policy: سیاست برخورد با تکراری‌ها

ساختار Frontend

1. ویجت‌های ایمپورت موجود

  • ProductImportDialog: ویجت ایمپورت محصولات

    • انتخاب فایل Excel
    • دانلود تمپلیت
    • تنظیمات (dry_run, match_by, conflict_policy)
    • نمایش نتایج
  • PersonImportDialog: ویجت ایمپورت اشخاص

    • مشابه ProductImportDialog

2. صفحه لیست فاکتورها

  • InvoicesListPage: صفحه اصلی لیست فاکتورها
    • فیلترها (نوع فاکتور، تاریخ، پیش‌فاکتور)
    • دکمه افزودن فاکتور جدید
    • جدول فاکتورها

3. سرویس‌های Frontend

  • InvoiceService: سرویس ارتباط با API فاکتورها
    • createInvoice: ایجاد فاکتور جدید
    • updateInvoice: ویرایش فاکتور
    • getInvoice: دریافت فاکتور

📊 ساختار فایل Excel برای ایمپورت فاکتورها

ساختار پیشنهادی

Sheet 1: Header Information (اطلاعات هدر فاکتور)

هر ردیف = یک فاکتور

ستون نام نوع الزامی توضیحات
invoice_type نوع فاکتور String ✅ sales, purchase, sales_return, purchase_return, direct_consumption, production, waste
document_date تاریخ فاکتور Date ✅ YYYY-MM-DD یا YYYY/MM/DD
currency_code کد ارز String ✅ کد ارز (مثلاً IRR, USD)
is_proforma پیش‌فاکتور Boolean ❌ true/false (پیش‌فرض: false)
description توضیحات String ❌ توضیحات فاکتور
person_code کد مشتری/تامین‌کننده String ⚠️ برای فاکتورهای sales/purchase الزامی
seller_code کد فروشنده/بازاریاب String ❌ برای محاسبه کارمزد
due_date تاریخ سررسید Date ❌ برای فاکتورهای فروش
post_inventory ثبت انبار Boolean ❌ true/false (پیش‌فرض: true)

Sheet 2: Line Items (ردیف‌های فاکتور)

هر ردیف = یک ردیف فاکتور

ستون نام نوع الزامی توضیحات
invoice_number شماره فاکتور String ✅ برای ارتباط با Sheet 1
product_code کد کالا/خدمت String ✅ کد محصول
quantity تعداد Decimal ✅ مقدار
unit واحد String ❌ main/secondary (پیش‌فرض: main)
unit_price قیمت واحد Decimal ✅ قیمت به ازای واحد انتخابی
discount_type نوع تخفیف String ❌ percent/amount (پیش‌فرض: amount)
discount_value مقدار تخفیف Decimal ❌ درصد یا مبلغ
tax_rate نرخ مالیات Decimal ❌ درصد (پیش‌فرض: 0)
description توضیحات ردیف String ❌ توضیحات ردیف
movement جهت حرکت String ⚠️ in/out (برای فاکتور تولید الزامی)
warehouse_code کد انبار String ❌ کد انبار

Sheet 3: Payments (پرداخت‌ها) - اختیاری

هر ردیف = یک تراکنش پرداخت

ستون نام نوع الزامی توضیحات
invoice_number شماره فاکتور String ✅ برای ارتباط با Sheet 1
transaction_type نوع تراکنش String ✅ cash, bank, check, etc.
amount مبلغ Decimal ✅ مبلغ پرداخت
transaction_date تاریخ تراکنش Date ✅ تاریخ پرداخت
account_code کد حساب String ❌ کد حساب بانکی/صندوق
check_number شماره چک String ❌ برای تراکنش چک
description توضیحات String ❌ توضیحات پرداخت

ساختار جایگزین (ساده‌تر)

اگر ساختار چند Sheet پیچیده باشد، می‌توان از یک Sheet استفاده کرد:

Single Sheet Structure

هر ردیف = یک ردیف فاکتور (فاکتورهای متعدد با invoice_number گروه‌بندی می‌شوند)

ستون نام نوع الزامی توضیحات
invoice_number شماره فاکتور String ✅ شناسه یکتا برای هر فاکتور
invoice_type نوع فاکتور String ✅ sales, purchase, etc.
document_date تاریخ فاکتور Date ✅
currency_code کد ارز String ✅
is_proforma پیش‌فاکتور Boolean ❌
description توضیحات فاکتور String ❌
person_code کد مشتری/تامین‌کننده String ⚠️
seller_code کد فروشنده String ❌
due_date تاریخ سررسید Date ❌
product_code کد کالا/خدمت String ✅
quantity تعداد Decimal ✅
unit_price قیمت واحد Decimal ✅
discount_type نوع تخفیف String ❌
discount_value مقدار تخفیف Decimal ❌
tax_rate نرخ مالیات Decimal ❌
line_description توضیحات ردیف String ❌
movement جهت حرکت String ❌
warehouse_code کد انبار String ❌

نکته: در این ساختار، اطلاعات هدر فاکتور در هر ردیف تکرار می‌شود. سیستم باید ردیف‌های با invoice_number یکسان را گروه‌بندی کند.


🎯 سناریوی پیاده‌سازی

مرحله 1: Backend - Endpoint دانلود تمپلیت

مسیر: POST /api/v1/invoices/business/{business_id}/import/template

عملکرد:

  1. ایجاد فایل Excel با ساختار تعریف شده
  2. پر کردن Header row با نام ستون‌ها
  3. اضافه کردن یک ردیف نمونه (اختیاری)
  4. بازگرداندن فایل به عنوان Response

کد نمونه (مشابه download_products_import_template):

@router.post("/business/{business_id}/import/template")
async def download_invoices_import_template(...):
    wb = Workbook()
    ws = wb.active
    ws.title = "Invoices"
    
    headers = [
        "invoice_number", "invoice_type", "document_date", "currency_code",
        "is_proforma", "description", "person_code", "seller_code",
        "due_date", "product_code", "quantity", "unit_price",
        "discount_type", "discount_value", "tax_rate", "line_description",
        "movement", "warehouse_code"
    ]
    
    # اضافه کردن header و نمونه
    # ...
    
    return Response(content=buf.getvalue(), ...)

مرحله 2: Backend - Endpoint ایمپورت

مسیر: POST /api/v1/invoices/business/{business_id}/import/excel

پارامترها:

  • file: فایل Excel (UploadFile)
  • dry_run: اعتبارسنجی بدون ذخیره (default: true)
  • create_mode: حالت ایجاد (single/multiple) - برای آینده

فرآیند پردازش:

  1. خواندن فایل Excel

    • اعتبارسنجی فرمت فایل (.xlsx)
    • بارگذاری workbook با openpyxl
  2. پارس کردن داده‌ها

    • خواندن Sheet اول
    • استخراج Header row
    • خواندن Data rows
    • گروه‌بندی ردیف‌ها بر اساس invoice_number
  3. اعتبارسنجی داده‌ها

    • بررسی فیلدهای الزامی
    • اعتبارسنجی نوع فاکتور
    • بررسی وجود محصولات (بر اساس product_code)
    • بررسی وجود اشخاص (بر اساس person_code)
    • بررسی وجود ارز (بر اساس currency_code)
    • محاسبه و اعتبارسنجی Totals
    • اعتبارسنجی قوانین کسب‌وکار (مثل اعتبار مشتری)
  4. تبدیل به Payload

    • تبدیل هر گروه به یک payload فاکتور
    • تبدیل ردیف‌ها به lines
    • ساخت extra_info از فیلدهای اضافی
  5. ایجاد فاکتورها (اگر dry_run=false)

    • فراخوانی create_invoice برای هر فاکتور
    • مدیریت خطاها و Rollback در صورت نیاز
  6. بازگرداندن نتیجه

    • خلاصه (total, valid, invalid, created, errors)
    • لیست خطاها با شماره ردیف

ساختار Response:

{
  "success": true,
  "data": {
    "summary": {
      "total": 10,
      "valid": 8,
      "invalid": 2,
      "created": 8,
      "dry_run": true
    },
    "errors": [
      {
        "row": 3,
        "invoice_number": "INV-001",
        "errors": ["product_code not found: P123"]
      }
    ],
    "warnings": [
      {
        "row": 5,
        "invoice_number": "INV-002",
        "message": "Credit limit exceeded but ignored"
      }
    ]
  }
}

مرحله 3: Frontend - ویجت ایمپورت

فایل جدید: lib/widgets/invoice/invoice_import_dialog.dart

عملکرد:

  • مشابه ProductImportDialog
  • انتخاب فایل Excel
  • دانلود تمپلیت
  • تنظیمات (dry_run)
  • نمایش نتایج

استفاده:

  • اضافه کردن دکمه "ایمپورت از اکسل" در InvoicesListPage
  • باز کردن Dialog با کلیک روی دکمه

مرحله 4: Frontend - سرویس ایمپورت

افزودن به InvoiceService:

Future<Map<String, dynamic>> importInvoicesFromExcel({
  required int businessId,
  required List<int> fileBytes,
  required String filename,
  required bool dryRun,
}) async {
  final form = FormData.fromMap({
    'file': MultipartFile.fromBytes(fileBytes, filename: filename),
    'dry_run': dryRun.toString(),
  });
  
  final res = await _api.post<Map<String, dynamic>>(
    '/api/v1/invoices/business/$businessId/import/excel',
    data: form,
    options: Options(contentType: 'multipart/form-data'),
  );
  
  return Map<String, dynamic>.from(res.data?['data'] ?? const {});
}

Future<void> downloadImportTemplate({
  required int businessId,
}) async {
  // مشابه محصولات
}

⚠️ نکات مهم و چالش‌ها

1. اعتبارسنجی داده‌ها

  • محصولات: باید بر اساس product_code پیدا شوند
  • اشخاص: باید بر اساس person_code پیدا شوند
  • ارز: باید بر اساس currency_code پیدا شود
  • انبار: باید بر اساس warehouse_code پیدا شود (اختیاری)

2. محاسبه Totals

  • سیستم باید Totals را از ردیف‌ها محاسبه کند
  • یا می‌توان از کاربر خواست Totals را در Excel وارد کند (برای اعتبارسنجی)

3. مدیریت خطاها

  • خطاهای اعتبارسنجی باید با شماره ردیف Excel مشخص شوند
  • در صورت خطا در یک فاکتور، سایر فاکتورها باید پردازش شوند (یا همه Rollback شوند؟)

4. فاکتور تولید

  • فاکتور تولید نیاز به ردیف‌های با movement: "in" و movement: "out" دارد
  • باید اعتبارسنجی شود که حداقل یک ردیف از هر نوع وجود دارد

5. اعتبار مشتری

  • برای فاکتورهای فروش، باید اعتبار مشتری بررسی شود
  • در صورت نیاز، می‌توان ignore_credit_check را در Excel اضافه کرد

6. تراکنش‌های پرداخت

  • پرداخت‌ها می‌توانند در Sheet جداگانه یا در همان Sheet باشند
  • باید با invoice_number مرتبط شوند

7. پیش‌فاکتور vs قطعی

  • پیش‌فاکتورها نیاز به پرداخت ندارند
  • فاکتورهای قطعی می‌توانند پرداخت داشته باشند

8. تاریخ‌ها

  • پشتیبانی از فرمت‌های مختلف تاریخ (YYYY-MM-DD, YYYY/MM/DD, Jalali)
  • تبدیل تاریخ شمسی به میلادی در صورت نیاز

9. واحدها

  • پشتیبانی از واحد اصلی و فرعی
  • تبدیل خودکار بر اساس unit_conversion_factor

10. Performance

  • برای فایل‌های بزرگ (مثلاً 1000+ فاکتور)، باید پردازش به صورت Batch انجام شود
  • نمایش Progress bar در Frontend

📝 فیلدهای مورد نیاز در Excel

فیلدهای هدر فاکتور (در هر ردیف یا Sheet جداگانه)

  • invoice_number: شناسه یکتا فاکتور (برای گروه‌بندی)
  • invoice_type: نوع فاکتور
  • document_date: تاریخ فاکتور
  • currency_code: کد ارز
  • is_proforma: پیش‌فاکتور (true/false)
  • description: توضیحات فاکتور
  • person_code: کد مشتری/تامین‌کننده (برای sales/purchase)
  • seller_code: کد فروشنده (اختیاری)
  • due_date: تاریخ سررسید (اختیاری)
  • post_inventory: ثبت انبار (true/false)

فیلدهای ردیف فاکتور

  • invoice_number: برای ارتباط با هدر
  • product_code: کد محصول
  • quantity: تعداد
  • unit: واحد (main/secondary)
  • unit_price: قیمت واحد
  • discount_type: نوع تخفیف (percent/amount)
  • discount_value: مقدار تخفیف
  • tax_rate: نرخ مالیات (درصد)
  • description: توضیحات ردیف
  • movement: جهت حرکت (in/out) - برای فاکتور تولید
  • warehouse_code: کد انبار (اختیاری)

فیلدهای پرداخت (اختیاری)

  • invoice_number: برای ارتباط با فاکتور
  • transaction_type: نوع تراکنش
  • amount: مبلغ
  • transaction_date: تاریخ تراکنش
  • account_code: کد حساب (اختیاری)
  • check_number: شماره چک (اختیاری)
  • description: توضیحات (اختیاری)

🔄 جریان کار (Workflow)

  1. کاربر روی دکمه "ایمپورت از اکسل" کلیک می‌کند
  2. سیستم Dialog ایمپورت را نمایش می‌دهد
  3. کاربر می‌تواند تمپلیت را دانلود کند
  4. کاربر فایل Excel را پر می‌کند
  5. کاربر فایل را انتخاب می‌کند
  6. کاربر روی "بررسی (Dry Run)" کلیک می‌کند
  7. سیستم فایل را پردازش و اعتبارسنجی می‌کند
  8. سیستم نتایج را نمایش می‌دهد (خطاها، هشدارها)
  9. کاربر خطاها را برطرف می‌کند
  10. کاربر روی "ایمپورت واقعی" کلیک می‌کند
  11. سیستم فاکتورها را ایجاد می‌کند
  12. سیستم نتایج نهایی را نمایش می‌دهد

🎨 UI/UX پیشنهادی

Dialog ایمپورت

  • تب 1: انتخاب فایل

    • دکمه انتخاب فایل
    • نمایش نام فایل انتخاب شده
    • دکمه دانلود تمپلیت
  • تب 2: تنظیمات

    • Checkbox: Dry Run (پیش‌فرض: فعال)
    • توضیحات درباره Dry Run
  • تب 3: نتایج

    • خلاصه (total, valid, invalid, created)
    • لیست خطاها با قابلیت فیلتر
    • دکمه Export خطاها به Excel

صفحه لیست فاکتورها

  • دکمه "ایمپورت از اکسل" در کنار دکمه "افزودن فاکتور"
  • آیکون: Icons.upload_file یا Icons.file_upload

📚 منابع و مراجع

  • کد موجود: hesabixAPI/adapters/api/v1/products.py (ایمپورت محصولات)
  • کد موجود: hesabixAPI/adapters/api/v1/persons.py (ایمپورت اشخاص)
  • ویجت موجود: hesabixUI/hesabix_ui/lib/widgets/product/product_import_dialog.dart
  • سرویس فاکتور: hesabixAPI/app/services/invoice_service.py
  • API فاکتور: hesabixAPI/adapters/api/v1/invoices.py

✅ چک‌لیست پیاده‌سازی

Backend

  • Endpoint دانلود تمپلیت
  • Endpoint ایمپورت
  • پارس کردن فایل Excel
  • اعتبارسنجی داده‌ها
  • تبدیل به Payload
  • ایجاد فاکتورها
  • مدیریت خطاها
  • تست‌های واحد

Frontend

  • ویجت InvoiceImportDialog
  • افزودن به InvoiceService
  • دکمه در InvoicesListPage
  • نمایش نتایج
  • مدیریت خطاها
  • تست UI

مستندات

  • مستندات API
  • راهنمای کاربر
  • نمونه فایل Excel

🚀 مراحل بعدی

  1. تایید سناریو: بررسی و تایید این سناریو توسط تیم
  2. تصمیم‌گیری ساختار Excel: انتخاب ساختار Single Sheet یا Multi Sheet
  3. پیاده‌سازی Backend: شروع با Endpoint تمپلیت
  4. پیاده‌سازی Frontend: ایجاد ویجت ایمپورت
  5. تست: تست با فایل‌های نمونه
  6. مستندسازی: نوشتن راهنمای کاربر

تاریخ ایجاد: 2024 آخرین به‌روزرسانی: 2024