forked from hesabix/arc
12 KiB
Executable file
12 KiB
Executable file
برنامه حذف Inventory Transfers و جایگزینی با Warehouse Documents
⚠️ نکته مهم: تفکیک حسابداری و انبارداری
موجودی حسابداری ≠ موجودی انبارداری
- کاردکس و محاسبات حسابداری: باید از اسناد حسابداری (
documentsوdocument_lines) استفاده شود - موجودی فیزیکی انبار: از
warehouse_documentsاستفاده میشود - این دو ممکن است متفاوت باشند (مثلاً کالاهای گمشده، ضایعات، خطاهای شمارش)
وضعیت فعلی
Inventory Transfers (سیستم قدیمی - باید حذف شود)
- جدول:
documentsباdocument_type = 'inventory_transfer' - خطوط:
document_linesباextra_info.inventory_tracked: True - استفاده در محاسبات موجودی حسابداری: ✅ بله (از طریق
_iter_product_movements) - استفاده در کاردکس: ✅ بله (از طریق
kardex_service) - نیاز به currency_id و fiscal_year_id: ✅ بله
- مشکل: یک سیستم جداگانه برای انتقال موجودی که باید حذف شود
Warehouse Documents (سیستم جدید)
- جدول:
warehouse_documentsوwarehouse_document_lines - نوع انتقال:
doc_type = 'transfer' - استفاده در محاسبات موجودی حسابداری: ❌ خیر (نباید مستقیماً استفاده شود)
- استفاده در کاردکس: ❌ خیر (نباید مستقیماً استفاده شود)
- نیاز به currency_id و fiscal_year_id: ❌ خیر
- هدف: مدیریت فیزیکی انبار (مستقل از حسابداری)
راهحل پیشنهادی
وقتی یک Warehouse Document با doc_type='transfer' پست میشود:
- ایجاد سند حسابداری: یک سند حسابداری (
Document) باdocument_type='inventory_transfer'ایجاد شود - ایجاد خطوط حسابداری: خطوط
DocumentLineباextra_info.inventory_tracked: Trueایجاد شود - لینک بین دو سیستم: سند حسابداری به Warehouse Document لینک شود (از طریق
extra_info) - استفاده در کاردکس: این سند حسابداری در کاردکس و محاسبات موجودی حسابداری استفاده شود
- مدیریت فیزیکی: Warehouse Document فقط برای مدیریت فیزیکی انبار باقی بماند
نتیجه:
- کاردکس و محاسبات حسابداری از اسناد حسابداری استفاده میکنند ✅
- موجودی فیزیکی انبار از Warehouse Documents استفاده میکند ✅
- این دو میتوانند متفاوت باشند ✅
فایلهای درگیر
Backend
فایلهای حذف شونده:
hesabixAPI/app/services/inventory_transfer_service.py- سرویس ایجاد انتقالhesabixAPI/adapters/api/v1/inventory_transfers.py- API endpointshesabixAPI/app/main.py- حذف router
فایلهای نیاز به تغییر:
-
hesabixAPI/app/services/warehouse_service.py- تغییر در
post_warehouse_document:- اگر
doc_type='transfer'باشد، یک سند حسابداری ایجاد شود - خطوط
DocumentLineباinventory_tracked: Trueایجاد شود - لینک بین سند حسابداری و Warehouse Document برقرار شود
- اگر
- تغییر در
-
hesabixAPI/app/services/kardex_service.py- حذف
inventory_transferاز mapping (اما همچنان ازdocument_type='inventory_transfer'استفاده میشود)
- حذف
-
hesabixAPI/adapters/db/repositories/document_repository.py- حذف
inventory_transferاز mapping (اما همچنان ازdocument_type='inventory_transfer'استفاده میشود)
- حذف
Frontend
فایلهای حذف شونده:
hesabixUI/hesabix_ui/lib/services/inventory_transfer_service.darthesabixUI/hesabix_ui/lib/widgets/transfer/inventory_transfer_form_dialog.darthesabixUI/hesabix_ui/lib/pages/business/inventory_transfers_page.dart
فایلهای نیاز به تغییر:
hesabixUI/hesabix_ui/lib/main.dart- حذف route/business/:business_id/inventory-transfershesabixUI/hesabix_ui/lib/pages/business/business_shell.dart- حذف منوی "حوالهها" (shipments)hesabixUI/hesabix_ui/lib/pages/business/kardex_page.dart- حذف فیلترinventory_transfer(اما همچنان ازdocument_type='inventory_transfer'استفاده میشود)hesabixUI/hesabix_ui/lib/l10n/app_fa.arbوapp_en.arb- حذف رشتههای مربوط بهshipmentsوinventory_transfer
تغییرات اصلی
1. تغییر post_warehouse_document برای ایجاد سند حسابداری
فایل: hesabixAPI/app/services/warehouse_service.py
تغییر در post_warehouse_document:
def post_warehouse_document(db: Session, wh_id: int) -> Dict[str, Any]:
"""پست حواله: کنترل کسری برای خروجها و بهروزرسانی موجودی انبار.
اگر doc_type='transfer' باشد، یک سند حسابداری هم ایجاد میشود.
"""
from app.services.invoice_service import _ensure_stock_sufficient, _get_current_fiscal_year
from adapters.db.models.document import Document
from adapters.db.models.document_line import DocumentLine
from adapters.db.models.currency import Currency
from app.services.document_monetization_service import ensure_document_policy_allows_creation
wh = db.query(WarehouseDocument).filter(WarehouseDocument.id == wh_id).first()
if not wh:
raise ApiError("NOT_FOUND", "Warehouse document not found", http_status=404)
if wh.status == "posted":
return {"id": wh.id, "status": wh.status}
lines = db.query(WarehouseDocumentLine).filter(WarehouseDocumentLine.warehouse_document_id == wh.id).all()
if not lines:
raise ApiError("NO_LINES", "حواله باید حداقل یک خط داشته باشد", http_status=400)
# کنترل کسری برای خروجها (کد موجود)
# ...
# تغییر وضعیت به posted
wh.status = "posted"
wh.touch()
db.flush()
# اگر doc_type='transfer' باشد، یک سند حسابداری ایجاد کن
if wh.doc_type == "transfer":
# دریافت currency_id (از business یا default)
currency = db.query(Currency).filter(Currency.business_id == wh.business_id).first()
if not currency:
# استفاده از currency پیشفرض
currency = db.query(Currency).filter(Currency.code == "IRR").first()
if not currency:
raise ApiError("CURRENCY_NOT_FOUND", "Currency not found", http_status=404)
fiscal_year = _get_current_fiscal_year(db, wh.business_id)
# ساخت کد سند
from datetime import datetime
today = datetime.now().date()
prefix = f"ITR-{today.strftime('%Y%m%d')}"
last_doc = db.query(Document).filter(
and_(
Document.business_id == wh.business_id,
Document.code.like(f"{prefix}-%"),
)
).order_by(Document.code.desc()).first()
if last_doc:
try:
last_num = int(last_doc.code.split("-")[-1])
next_num = last_num + 1
except Exception:
next_num = 1
else:
next_num = 1
doc_code = f"{prefix}-{next_num:04d}"
# بررسی policy
ensure_document_policy_allows_creation(
db,
wh.business_id,
document_type="inventory_transfer",
document_date=wh.document_date,
amount=Decimal(0),
)
# ایجاد سند حسابداری
accounting_doc = Document(
business_id=wh.business_id,
fiscal_year_id=fiscal_year.id,
code=doc_code,
document_type="inventory_transfer",
document_date=wh.document_date,
currency_id=currency.id,
created_by_user_id=wh.created_by_user_id,
registered_at=datetime.utcnow(),
is_proforma=False,
description=wh.extra_info.get("description") if wh.extra_info else None,
extra_info={
"source": "warehouse_document",
"warehouse_document_id": wh.id,
},
)
db.add(accounting_doc)
db.flush()
# ایجاد خطوط حسابداری از خطوط حواله
# برای transfer: یک خط out از مبدا و یک خط in به مقصد
for wline in lines:
if wline.movement == "out":
# خط خروج از انبار مبدا
db.add(DocumentLine(
document_id=accounting_doc.id,
product_id=wline.product_id,
quantity=wline.quantity,
debit=Decimal(0),
credit=Decimal(0),
description=None,
extra_info={
"movement": "out",
"warehouse_id": wline.warehouse_id,
"inventory_tracked": True,
},
))
elif wline.movement == "in":
# خط ورود به انبار مقصد
db.add(DocumentLine(
document_id=accounting_doc.id,
product_id=wline.product_id,
quantity=wline.quantity,
debit=Decimal(0),
credit=Decimal(0),
description=None,
extra_info={
"movement": "in",
"warehouse_id": wline.warehouse_id,
"inventory_tracked": True,
},
))
# لینک سند حسابداری به Warehouse Document
if not wh.extra_info:
wh.extra_info = {}
wh.extra_info["accounting_document_id"] = accounting_doc.id
db.flush()
db.commit()
return {"id": wh.id, "status": wh.status}
2. حذف فایلها و ارجاعات
مراحل:
- حذف فایلهای Backend
- حذف router از
main.py - حذف فایلهای Frontend
- حذف route از
main.dart - حذف منو از
business_shell.dart - حذف رشتههای ترجمه
- حذف از
kardex_page.dart(اما همچنان ازdocument_type='inventory_transfer'استفاده میشود) - حذف از
document_repository.py(اما همچنان ازdocument_type='inventory_transfer'استفاده میشود)
نکات مهم
-
Migration دادهها: اگر دادههای موجودی از Inventory Transfers وجود دارد، باید migration script نوشته شود که:
- Inventory Transfers موجود را به Warehouse Documents تبدیل کند
- سند حسابداری مرتبط را حفظ کند
-
Backward Compatibility: بررسی کنید که آیا APIهای خارجی از Inventory Transfers استفاده میکنند
-
Testing: بعد از تغییرات، تستهای کامل برای:
- ایجاد Warehouse Document با doc_type='transfer'
- پست کردن و ایجاد سند حسابداری
- نمایش در کاردکس
- محاسبات موجودی حسابداری
خلاصه تغییرات
Backend
- ✅ تغییر
post_warehouse_documentبرای ایجاد سند حسابداری در صورتdoc_type='transfer' - ❌ حذف
inventory_transfer_service.py - ❌ حذف
inventory_transfers.py(API) - ❌ حذف router از
main.py - ⚠️ حذف
inventory_transferاز mappingها (اما همچنان ازdocument_type='inventory_transfer'استفاده میشود)
Frontend
- ❌ حذف
inventory_transfer_service.dart - ❌ حذف
inventory_transfer_form_dialog.dart - ❌ حذف
inventory_transfers_page.dart - ❌ حذف route از
main.dart - ❌ حذف منو از
business_shell.dart - ⚠️ حذف فیلتر از
kardex_page.dart(اما همچنان ازdocument_type='inventory_transfer'استفاده میشود) - ❌ حذف رشتههای ترجمه