feat(backend): add FastAPI backend — procurement, inventory domain, Casdoor auth, PM arrival endpoints

This commit is contained in:
ali_mohaghegh
2026-08-25 19:08:14 +03:30
parent c8a9b66395
commit 170aed66ea
67 changed files with 9774 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
"""Pydantic v2 request schemas — inventory domain (fork's contract, unchanged)."""
from datetime import datetime
from decimal import Decimal
from typing import Literal
from uuid import UUID
from pydantic import Field, model_validator
from app.models.inventory import INVENTORY_TRANSACTION_TYPES
from app.schemas.common import APIModel
TransactionType = Literal[
"PURCHASE",
"WITHDRAWAL",
"RETURN",
"TRANSFER_IN",
"TRANSFER_OUT",
"DAMAGE",
"WASTE",
"LOSS",
"FOUND",
"STOCK_COUNT",
]
class WarehouseCreate(APIModel):
project_id: UUID
name: str = Field(min_length=1, max_length=255)
description: str | None = None
class WarehouseUpdate(APIModel):
name: str | None = Field(default=None, min_length=1, max_length=255)
description: str | None = None
project_id: UUID | None = None
class InventoryTransactionCreate(APIModel):
"""One inventory operation.
- For all types except STOCK_COUNT: `quantity` is the amount to add/remove.
- STOCK_COUNT uses `physical_qty` (the counted quantity); the difference
against the recorded quantity is computed and applied by the service.
- TRANSFER_OUT requires `dest_warehouse_id`; the service creates the linked
TRANSFER_IN row in the destination warehouse (transfer_pair_id).
- `specs` is a dict of technical specifications (dynamic multi-spec entries).
- `transaction_date` defaults to server now() when omitted.
"""
transaction_type: TransactionType
warehouse_id: UUID
dest_warehouse_id: UUID | None = None
material_id: UUID
brand_id: UUID | None = None
quantity: Decimal | None = Field(default=None, gt=0)
physical_qty: Decimal | None = Field(default=None, ge=0)
unit_id: int | None = None
specs: dict | None = None
note: str | None = None
transaction_date: datetime | None = None
@model_validator(mode="after")
def validate_type_rules(self):
t = self.transaction_type
if t == "STOCK_COUNT":
if self.physical_qty is None:
raise ValueError("برای شمارش موجودی، تعداد شمارش‌شده (physical_qty) الزامی است")
else:
if self.quantity is None:
raise ValueError("تعداد (quantity) الزامی است")
if t == "TRANSFER_OUT":
if self.dest_warehouse_id is None:
raise ValueError("برای انتقال خروجی، انبار مقصد (dest_warehouse_id) الزامی است")
if self.dest_warehouse_id == self.warehouse_id:
raise ValueError("انبار مبدأ و مقصد نمی‌توانند یکسان باشند")
if t not in INVENTORY_TRANSACTION_TYPES:
raise ValueError(f"نوع تراکنش نامعتبر است: {t}")
return self