revert: restore production to the state before 730d0bd (PM/RBAC work moved to 'pm')
Their three commits (730d0bd,5a9b543,387edfb) reached production by push and auto-deployed.387edfbexpects a users.username column that the live database does not have, so POST /api/v1/auth/login was returning HTTP 500. Their schema changes never applied either (projects still has only id, name, description, start_date, pm_user_id, created_at), so nothing depends on the reverted code. This commit sets the tree back to exactlya02b3ea— verified with git diff against it — while keeping history linear: the work itself is preserved on 'pm' at387edfb.
This commit is contained in:
@@ -77,21 +77,23 @@ async def inventory_scope(
|
||||
if user is not None:
|
||||
if user.role is None:
|
||||
raise ForbiddenError("نقش شما هنوز در سیستم تعیین نشده است")
|
||||
# مدیر تدارکات و مدیر پروژه: دسترسی کامل به پنل انبار، اما فقطخواندنی در تغییر موجودی
|
||||
if user.role in ("procurement_manager", "pm"):
|
||||
return Scope(role="manager", project_id=project_id, actor=user.display_name)
|
||||
if user.role == "supervisor":
|
||||
# v2.6: مدیر تدارکات = ادمین دفتر — دسترسی فقطخواندنی به پنل انبار.
|
||||
if user.role == "procurement_manager":
|
||||
return Scope(role="admin", project_id=project_id, actor=user.display_name)
|
||||
if user.role not in ("pm", "admin"):
|
||||
raise ValidationError("نقش نامعتبر است — فقط pm یا admin برای موجودی", field="role")
|
||||
if user.role == "pm":
|
||||
allowed = list(user.project_ids or [])
|
||||
if not allowed:
|
||||
raise ForbiddenError("پروژه تخصیص داده نشده است")
|
||||
if project_id is not None:
|
||||
if project_id not in allowed:
|
||||
raise ForbiddenError("این پروژه به شما تخصیص داده نشده است")
|
||||
return Scope(role="supervisor", project_id=project_id, actor=user.display_name)
|
||||
return Scope(role="supervisor", project_id=allowed[0], actor=user.display_name)
|
||||
if user.role == "admin":
|
||||
return Scope(role="admin", project_id=project_id, actor=user.display_name)
|
||||
raise ValidationError("نقش نامعتبر است", field="role")
|
||||
return Scope(role="pm", project_id=project_id, actor=user.display_name)
|
||||
if not allowed:
|
||||
raise ValidationError(
|
||||
"کاربر PM به هیچ پروژهای متصل نیست (projects.pm_user_id)", field="project_id"
|
||||
)
|
||||
return Scope(role="pm", project_id=allowed[0], actor=user.display_name)
|
||||
return Scope(role="admin", project_id=project_id, actor=user.display_name)
|
||||
|
||||
# Dev fallback: header-based identity (fork's placeholder mode) — only
|
||||
# when DEV_LOGIN_ENABLED is explicitly turned on (local testing without
|
||||
@@ -126,9 +128,9 @@ async def inventory_scope(
|
||||
return Scope(role=role, project_id=project_id, actor=actor)
|
||||
|
||||
|
||||
def _require_inventory_modifier(scope: Scope) -> None:
|
||||
if scope.role not in (ROLE_ADMIN, "admin", "supervisor", ROLE_PM):
|
||||
raise ForbiddenError("مدیر تدارکات و مدیر پروژه امکان تغییر موجودی انبار را ندارند — فقط سرپرست پروژه و ادمین مجاز هستند")
|
||||
def _require_pm(scope: Scope) -> None:
|
||||
if scope.role != ROLE_PM:
|
||||
raise ForbiddenError("دسترسی ادمین فقط خواندنی است — ثبت/تغییر موجودی مجاز نیست")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -213,8 +215,8 @@ async def list_warehouses(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
stmt = select(Warehouse)
|
||||
if scope.role in (ROLE_PM, "supervisor"):
|
||||
stmt = stmt.where(Warehouse.project_id == scope.project_id) # force PM / supervisor scope
|
||||
if scope.role == ROLE_PM:
|
||||
stmt = stmt.where(Warehouse.project_id == scope.project_id) # force PM scope
|
||||
elif project_id is not None:
|
||||
stmt = stmt.where(Warehouse.project_id == project_id)
|
||||
stmt = stmt.order_by(Warehouse.name)
|
||||
@@ -321,7 +323,7 @@ async def list_inventory(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
stmt = select(InventoryItem)
|
||||
if scope.role in (ROLE_PM, "supervisor"):
|
||||
if scope.role == ROLE_PM:
|
||||
stmt = stmt.join(Warehouse, Warehouse.id == InventoryItem.warehouse_id).where(
|
||||
Warehouse.project_id == scope.project_id
|
||||
)
|
||||
@@ -386,7 +388,7 @@ async def list_transactions(
|
||||
stmt = select(InventoryTransaction).join(
|
||||
InventoryItem, InventoryItem.id == InventoryTransaction.inventory_id
|
||||
).join(Warehouse, Warehouse.id == InventoryItem.warehouse_id)
|
||||
if scope.role in (ROLE_PM, "supervisor"):
|
||||
if scope.role == ROLE_PM:
|
||||
stmt = stmt.where(Warehouse.project_id == scope.project_id)
|
||||
elif project_id is not None:
|
||||
stmt = stmt.where(Warehouse.project_id == project_id)
|
||||
@@ -452,7 +454,7 @@ async def inventory_history(
|
||||
if item is None:
|
||||
raise NotFoundError("موجودی یافت نشد")
|
||||
wh = await get_warehouse_or_404(db, item.warehouse_id)
|
||||
if scope.role in (ROLE_PM, "supervisor") and wh.project_id != scope.project_id:
|
||||
if scope.role == ROLE_PM and wh.project_id != scope.project_id:
|
||||
raise ForbiddenError("این موجودی متعلق به پروژه شما نیست")
|
||||
txs = (await db.execute(
|
||||
select(InventoryTransaction)
|
||||
@@ -472,17 +474,17 @@ async def create_transaction(
|
||||
scope: Scope = Depends(inventory_scope),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
_require_inventory_modifier(scope)
|
||||
_require_pm(scope)
|
||||
# No explicit db.begin(): the session may already be inside an implicit
|
||||
# transaction (inventory_scope read from it in the JWT path); get_db
|
||||
# commits on success / rolls back on failure.
|
||||
with nullcontext():
|
||||
wh = await get_warehouse_or_404(db, payload.warehouse_id)
|
||||
if scope.project_id is not None and wh.project_id != scope.project_id:
|
||||
if wh.project_id != scope.project_id:
|
||||
raise ForbiddenError("این انبار متعلق به پروژه شما نیست")
|
||||
if payload.dest_warehouse_id is not None:
|
||||
dest = await get_warehouse_or_404(db, payload.dest_warehouse_id)
|
||||
if scope.project_id is not None and dest.project_id != scope.project_id:
|
||||
if dest.project_id != scope.project_id:
|
||||
raise ForbiddenError("انبار مقصد متعلق به پروژه شما نیست")
|
||||
txs = await apply_transaction(db, payload, performed_by=scope.actor)
|
||||
await audit_record(
|
||||
|
||||
Reference in New Issue
Block a user