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

320 lines
20 KiB
Markdown
Executable file
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# سناریو: تولید کالا از طریق فاکتور تولید با استفاده از فرمول تولید (BOM)
## اصل طراحی
**فرمول تولید (BOM) که منجر به تولید کالای جدید می‌شود، صرفاً از طریق فاکتور تولید انجام می‌شود.**
این یعنی:
- ✅ **اجباری**: هر فاکتور تولید باید از یک فرمول تولید استفاده کند
- ✅ **یکپارچه**: تمام فرآیند تولید از تعریف فرمول تا صدور فاکتور در یک جریان یکپارچه انجام می‌شود
- ✅ **قابل ردیابی**: هر فاکتور تولید به یک فرمول مشخص لینک می‌شود
## وضعیت فعلی
### 1. مدیریت فرمول تولید (BOM) در بخش کالا
- **مکان**: تب "فرمول تولید" در فرم ویرایش/ایجاد کالا (`product_form_dialog.dart`)
- **عملکرد فعلی**:
- کاربر می‌تواند فرمول‌های تولید را برای هر کالا تعریف کند
- فرمول‌ها در جدول `product_boms` ذخیره می‌شوند و به کالا لینک می‌شوند (`product_id`)
- هر فرمول می‌تواند چندین نسخه داشته باشد (`version`)
- یک فرمول می‌تواند به عنوان پیش‌فرض تنظیم شود (`is_default`)
- **حذف شده**: دکمه "انفجار فرمول" از این بخش حذف شده است
### 2. صدور فاکتور تولید
- **مکان**: صفحه `new_invoice_page.dart`
- **نوع فاکتور**: `InvoiceType.production` (مقدار API: `invoice_production`)
- **عملکرد فعلی**:
- کاربر می‌تواند فاکتور از نوع "تولید" را ایجاد کند
- ویجت `BomExplosionWidget` برای استفاده از فرمول‌های تولید موجود است
- اما استفاده از BOM **اجباری نیست** (مشکل)
## مشکل فعلی
1. ❌ **استفاده از BOM اجباری نیست**: کاربر می‌تواند فاکتور تولید بدون استفاده از فرمول تولید ایجاد کند
2. ❌ **movement تنظیم نمی‌شود**: در BOM Explosion، مقدار `movement` در `extra_info` ردیف‌ها ذخیره نمی‌شود
3. ❌ **عدم ردیابی**: فاکتور تولید به فرمول استفاده شده لینک نمی‌شود
4. ❌ **امکان خطا**: کاربر می‌تواند به صورت دستی ردیف‌های نادرست اضافه کند
## سناریو بازطراحی شده
### اصول طراحی
1. ✅ **اجبار استفاده از BOM**: هر فاکتور تولید باید حداقل از یک فرمول تولید استفاده کند
2. ✅ **مدیریت فرمول در بخش کالا**: فرمول‌ها همچنان در تب "فرمول تولید" کالا تعریف و مدیریت می‌شوند
3. ✅ **انفجار در فاکتور**: انفجار فرمول فقط در صفحه صدور فاکتور تولید انجام می‌شود
4. ✅ **ردیابی کامل**: هر فاکتور تولید به فرمول(های) استفاده شده لینک می‌شود
5. ✅ **حرکت خودکار**: مقدار `movement` در `extra_info` به صورت خودکار تنظیم می‌شود
### تغییرات پیشنهادی
#### 1. بخش کالا (Frontend - `product_bom_section.dart`)
**تغییرات**:
- ✅ **حفظ**: مدیریت فرمول‌ها (ایجاد، ویرایش، حذف) همچنان در این بخش باقی می‌ماند
- ❌ **حذف**: دکمه "انفجار فرمول" از این بخش حذف می‌شود
- ❌ **حذف**: دیالوگ نمایش نتایج انفجار و ایجاد پیش‌نویس سند حذف می‌شود
- ✅ **افزودن**: یک پیام راهنما یا لینک به کاربر که "برای استفاده از این فرمول، در فاکتور تولید از آن استفاده کنید"
#### 2. صفحه صدور فاکتور تولید (Frontend - `new_invoice_page.dart`)
**تغییرات**:
- ✅ **اجبار استفاده از BOM**:
- اگر نوع فاکتور "تولید" است، کاربر نمی‌تواند فاکتور را ذخیره کند مگر اینکه حداقل یک بار انفجار فرمول انجام داده باشد
- یک پیام هشدار نمایش داده می‌شود: "برای فاکتور تولید، باید حداقل یک فرمول تولید را منفجر کنید"
- ✅ **ویجت انفجار فرمول (بهبود یافته)**:
- انتخاب کالای تولیدی (از لیست کالاها)
- نمایش فرمول‌های موجود برای آن کالا (اگر فرمول پیش‌فرض وجود دارد، به صورت پیش‌فرض انتخاب شود)
- امکان انتخاب فرمول خاص (اگر چند فرمول وجود دارد)
- ورود مقدار تولید
- دکمه "انفجار و افزودن به فاکتور"
- **جدید**: پس از انفجار، ID فرمول در `extra_info` فاکتور ذخیره می‌شود
- ✅ **پس از انفجار (بهبود یافته)**:
- نتایج انفجار به صورت ردیف‌های فاکتور به `_lineItems` اضافه می‌شوند
- **اجباری**: مواد اولیه (inputs) با `movement: "out"` در `extra_info` تنظیم می‌شوند
- **اجباری**: خروجی‌ها (outputs) با `movement: "in"` در `extra_info` تنظیم می‌شوند
- **جدید**: ID فرمول استفاده شده در `extra_info` هر ردیف ذخیره می‌شود (برای ردیابی)
- کاربر می‌تواند قبل از ذخیره، ردیف‌ها را ویرایش کند (مقدار، قیمت، انبار)
- **جدید**: امکان انفجار چند فرمول در یک فاکتور (برای محصولات مختلف)
- ✅ **اعتبارسنجی قبل از ذخیره**:
- بررسی می‌شود که حداقل یک ردیف با `movement: "out"` وجود دارد (مواد اولیه)
- بررسی می‌شود که حداقل یک ردیف با `movement: "in"` وجود دارد (محصول نهایی)
- بررسی می‌شود که تمام ردیف‌های فاکتور `movement` مشخص داشته باشند
#### 3. Backend (تغییرات اعتبارسنجی)
- ✅ API `explode_bom` همچنان موجود است و استفاده می‌شود
- ✅ **جدید**: اعتبارسنجی در `create_invoice`:
- اگر نوع فاکتور `invoice_production` است:
- بررسی می‌شود که حداقل یک ردیف با `movement: "out"` وجود دارد
- بررسی می‌شود که حداقل یک ردیف با `movement: "in"` وجود دارد
- بررسی می‌شود که تمام ردیف‌ها `movement` مشخص داشته باشند
- در صورت عدم وجود، خطا: "فاکتور تولید باید از فرمول تولید استفاده کند"
- ✅ **جدید**: ذخیره اطلاعات فرمول:
- در `extra_info` فاکتور، لیست ID فرمول‌های استفاده شده ذخیره می‌شود: `{"bom_ids": [1, 2]}`
- در `extra_info` هر ردیف، ID فرمول استفاده شده ذخیره می‌شود: `{"bom_id": 1}`
- ✅ ساختار دیتابیس بدون تغییر باقی می‌ماند
### جریان کار طراحی شده
```
1. کاربر در بخش کالا (تعریف فرمول):
└─> کالای تولیدی را ایجاد/ویرایش می‌کند
└─> در تب "فرمول تولید"، فرمول‌های تولید را تعریف می‌کند:
├─ مواد اولیه (component products)
├─ خروجی‌ها (output products)
└─ عملیات (operations)
└─> فرمول را ذخیره می‌کند
└─> (اختیاری) فرمولی را به عنوان پیش‌فرض تنظیم می‌کند
2. کاربر برای صدور فاکتور تولید:
└─> به صفحه "صدور فاکتور جدید" می‌رود
└─> نوع فاکتور را "تولید" انتخاب می‌کند
└─> به تب "کالاها و خدمات" می‌رود
└─> ⚠️ سیستم بررسی می‌کند که آیا فرمولی منفجر شده است؟
├─ خیر: نمایش پیام هشدار "برای فاکتور تولید، باید حداقل یک فرمول تولید را منفجر کنید"
└─ بله: ادامه روند
└─> دکمه "انفجار فرمول" را می‌زند
└─> کالای تولیدی را انتخاب می‌کند
└─> سیستم فرمول‌های موجود را نمایش می‌دهد:
├─ اگر فرمول پیش‌فرض وجود دارد، به صورت پیش‌فرض انتخاب می‌شود
└─ در غیر این صورت، کاربر فرمول را انتخاب می‌کند
└─> مقدار تولید را وارد می‌کند
└─> دکمه "انفجار و افزودن" را می‌زند
└─> ✅ ردیف‌های فاکتور به صورت خودکار پر می‌شوند:
├─ مواد اولیه (movement: "out" در extra_info)
│ └─ extra_info.bom_id = [ID فرمول]
└─ محصولات تولید شده (movement: "in" در extra_info)
└─ extra_info.bom_id = [ID فرمول]
└─> (اختیاری) امکان انفجار فرمول دیگر برای کالای دیگر
└─> کاربر می‌تواند ردیف‌ها را ویرایش کند:
├─ مقدار (quantity)
├─ قیمت (unit_price)
├─ انبار (warehouse_id)
└─ ⚠️ نمی‌تواند movement را تغییر دهد
└─> هنگام ذخیره، اعتبارسنجی انجام می‌شود:
├─ ✅ حداقل یک ردیف با movement: "out" وجود دارد؟
├─ ✅ حداقل یک ردیف با movement: "in" وجود دارد؟
└─ ✅ تمام ردیف‌ها movement مشخص دارند؟
└─> ✅ فاکتور ذخیره می‌شود
└─> ✅ حواله‌های انبار به صورت خودکار ایجاد می‌شوند:
├─ حواله issue (برای مواد اولیه - movement: "out")
└─ حواله receipt (برای محصولات نهایی - movement: "in")
└─> ✅ اسناد حسابداری در پست حواله‌ها ایجاد می‌شوند
```
### جزئیات پیاده‌سازی
#### Frontend - ویجت انفجار فرمول در فاکتور (بهبود یافته)
**فایل**: `hesabixUI/hesabix_ui/lib/widgets/invoice/bom_explosion_widget.dart`
**تغییرات لازم**:
```dart
class BomExplosionWidget extends StatefulWidget {
final int businessId;
final Function(List<InvoiceLineItem>, int bomId) onExploded; // bomId اضافه شد
// ...
}
// در _explodeAndAdd():
// پس از انفجار موفق:
final bomId = _selectedBom?.id;
if (bomId != null) {
// اضافه کردن bom_id به extra_info هر ردیف
for (var item in lineItems) {
// باید InvoiceLineItem را گسترش دهیم یا از Map استفاده کنیم
}
widget.onExploded(lineItems, bomId);
}
```
**عملکرد بهبود یافته**:
1. ✅ نمایش یک کارت برجسته در تب "کالاها و خدمات" (فقط برای `InvoiceType.production`)
2. ✅ دیالوگ انتخاب کالا و فرمول
3. ✅ ورود مقدار تولید با اعتبارسنجی
4. ✅ فراخوانی API `explode_bom`
5. ✅ تبدیل نتایج به `InvoiceLineItem` با تنظیم خودکار:
- مواد اولیه: `extra_info.movement = "out"`
- خروجی‌ها: `extra_info.movement = "in"`
- همه: `extra_info.bom_id = [bomId]`
6. ✅ افزودن به لیست و نمایش موفقیت
7. ✅ **جدید**: ذخیره لیست `bom_ids` در `extra_info` فاکتور
**تغییرات در `new_invoice_page.dart`**:
- ✅ در متد `_buildProductsTab()`:
- ویجت `BomExplosionWidget` را نمایش بده (همیشه در بالای جدول)
- پس از انفجار، ردیف‌ها را به `_lineItems` اضافه کن
- `bom_id` را در `extra_info` فاکتور ذخیره کن
- ✅ **جدید**: در متد `_serializeLineItem()`:
- بررسی کن که اگر نوع فاکتور `production` است:
- اگر ردیف `movement` ندارد، خطا بده
- ✅ **جدید**: در متد `_validateAndBuildPayload()`:
- اگر نوع فاکتور `production` است:
- بررسی کن که حداقل یک ردیف با `movement: "out"` وجود دارد
- بررسی کن که حداقل یک ردیف با `movement: "in"` وجود دارد
- بررسی کن که تمام ردیف‌ها `movement` مشخص دارند
- در صورت عدم وجود، خطا: "برای فاکتور تولید، باید حداقل یک فرمول تولید را منفجر کنید"
#### Frontend - حذف انفجار از بخش کالا
**تغییرات در `product_bom_section.dart`**:
- حذف متد `_explode()`
- حذف دکمه "انفجار فرمول" از `ListTile`
- (اختیاری) افزودن یک `InfoCard` که راهنمایی می‌دهد: "برای استفاده از این فرمول، در فاکتور تولید از آن استفاده کنید"
### مزایای این رویکرد
1. ✅ **یکپارچگی و کنترل**:
- تمام فرآیند تولید از یک مسیر انجام می‌شود
- کاهش خطاهای انسانی (عدم وارد کردن دستی مواد اولیه)
- تضمین صحت داده‌ها
2. ✅ **جداسازی مسئولیت‌ها**:
- مدیریت فرمول = بخش کالا (تعریف و نگهداری)
- استفاده از فرمول = فاکتور تولید (اجرای عملیات)
3. ✅ **ردیابی کامل**:
- هر فاکتور تولید به فرمول(های) استفاده شده لینک می‌شود
- امکان گزارش‌گیری دقیق از تولیدات بر اساس فرمول
- امکان بازگشت و بررسی فرمول استفاده شده
4. ✅ **تجربه کاربری بهتر**:
- کاربر مستقیماً در فاکتور می‌تواند از فرمول استفاده کند
- راهنمایی‌های واضح برای کاربر
- جلوگیری از خطا با اعتبارسنجی‌های مناسب
5. ✅ **انعطاف‌پذیری کنترل شده**:
- کاربر می‌تواند پس از انفجار، ردیف‌ها را ویرایش کند (مقدار، قیمت، انبار)
- کاربر نمی‌تواند `movement` را تغییر دهد (برای جلوگیری از خطا)
- امکان انفجار چند فرمول در یک فاکتور (برای تولید چند محصول)
6. ✅ **سازگاری با Backend**:
- از API‌های موجود استفاده می‌کند
- اعتبارسنجی‌های اضافی در Backend برای اطمینان از صحت داده‌ها
### تصمیمات طراحی
1. ✅ **امکان ویرایش ردیف‌های انفجار شده**: بله
- کاربر می‌تواند مقدار، قیمت و انبار را تغییر دهد
- کاربر **نمی‌تواند** `movement` را تغییر دهد (برای جلوگیری از خطا)
2. ✅ **امکان انفجار چندباره**: بله
- کاربر می‌تواند چند فرمول مختلف را در یک فاکتور منفجر کند
- هر فرمول می‌تواند برای کالای متفاوتی باشد
3. ✅ **اجبار استفاده از BOM**: بله
- کاربر نمی‌تواند فاکتور تولید را بدون انفجار حداقل یک فرمول ذخیره کند
- اعتبارسنجی در Frontend و Backend انجام می‌شود
4. ✅ **ذخیره اطلاعات فرمول**: بله
- `bom_id` در `extra_info` هر ردیف ذخیره می‌شود
- لیست `bom_ids` در `extra_info` فاکتور ذخیره می‌شود
5. ✅ **حذف گروهی ردیف‌های انفجار شده**: بله (در نسخه‌های بعدی)
- امکان حذف تمام ردیف‌های مربوط به یک فرمول خاص
6. ✅ **حرکت خودکار**: بله
- `movement: "out"` برای مواد اولیه به صورت خودکار تنظیم می‌شود
- `movement: "in"` برای محصولات نهایی به صورت خودکار تنظیم می‌شود
## خلاصه تغییرات فایل‌ها
### فایل‌های جدید
- ✅ `hesabixUI/hesabix_ui/lib/widgets/invoice/bom_explosion_widget.dart` - ویجت انفجار فرمول در فاکتور (موجود)
### فایل‌های تغییر یافته (نیاز به اصلاح)
#### Frontend:
1. **`hesabixUI/hesabix_ui/lib/widgets/invoice/bom_explosion_widget.dart`**:
- ✅ افزودن `movement` به `extra_info` هر ردیف
- ✅ افزودن `bom_id` به `extra_info` هر ردیف
- ✅ برگرداندن `bom_id` به callback
2. **`hesabixUI/hesabix_ui/lib/pages/business/new_invoice_page.dart`**:
- ✅ ذخیره `bom_ids` در `extra_info` فاکتور
- ✅ اعتبارسنجی قبل از ذخیره: بررسی وجود حداقل یک انفجار فرمول
- ✅ اعتبارسنجی `movement` برای تمام ردیف‌ها
3. **`hesabixUI/hesabix_ui/lib/models/invoice_line_item.dart`** (اختیاری):
- بررسی نیاز به اضافه کردن فیلد `bomId` یا استفاده از `extra_info`
#### Backend:
4. **`hesabixAPI/app/services/invoice_service.py`**:
- ✅ اعتبارسنجی در `create_invoice` برای فاکتور تولید:
- بررسی وجود حداقل یک ردیف با `movement: "out"`
- بررسی وجود حداقل یک ردیف با `movement: "in"`
- بررسی وجود `movement` در تمام ردیف‌ها
### فایل‌های بدون تغییر
- ✅ مدل‌های دیتابیس (نیاز به تغییر ندارند)
- ✅ API endpoints موجود (نیاز به تغییر ندارند)
- ✅ ساختار `product_boms` و جداول مرتبط
---
## چک‌لیست پیاده‌سازی
### مرحله 1: Frontend - ویجت انفجار
- [ ] تنظیم `movement: "out"` برای مواد اولیه در `BomExplosionWidget`
- [ ] تنظیم `movement: "in"` برای خروجی‌ها در `BomExplosionWidget`
- [ ] اضافه کردن `bom_id` به `extra_info` هر ردیف
- [ ] برگرداندن `bom_id` به callback
### مرحله 2: Frontend - صفحه فاکتور
- [ ] ذخیره لیست `bom_ids` در `extra_info` فاکتور
- [ ] اعتبارسنجی قبل از ذخیره: بررسی انفجار حداقل یک فرمول
- [ ] نمایش پیام هشدار در صورت عدم وجود انفجار فرمول
- [ ] اعتبارسنجی `movement` برای تمام ردیف‌ها
### مرحله 3: Backend - اعتبارسنجی
- [ ] اعتبارسنجی در `create_invoice` برای فاکتور تولید
- [ ] بررسی وجود `movement` در تمام ردیف‌ها
- [ ] بررسی وجود حداقل یک ردیف با `movement: "out"`
- [ ] بررسی وجود حداقل یک ردیف با `movement: "in"`
### مرحله 4: تست
- [ ] تست ایجاد فاکتور تولید با استفاده از BOM
- [ ] تست عدم امکان ذخیره فاکتور تولید بدون BOM
- [ ] تست انفجار چند فرمول در یک فاکتور
- [ ] تست ویرایش ردیف‌های انفجار شده
- [ ] تست ایجاد حواله‌های انبار
---
**آماده برای پیاده‌سازی**