forked from hesabix/arc
320 lines
20 KiB
Markdown
Executable file
320 lines
20 KiB
Markdown
Executable file
# سناریو: تولید کالا از طریق فاکتور تولید با استفاده از فرمول تولید (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
|
||
- [ ] تست انفجار چند فرمول در یک فاکتور
|
||
- [ ] تست ویرایش ردیفهای انفجار شده
|
||
- [ ] تست ایجاد حوالههای انبار
|
||
|
||
---
|
||
|
||
**آماده برای پیادهسازی**
|
||
|