Watch
1
0
Fork
You've already forked Seyyed_arc
0
forked from hesabix/arc
Seyyed_arc/hesabixAPI/adapters/api/v1/quick_sales.py

283 lines
11 KiB
Python
Executable file

from __future__ import annotations
import json
import logging
from datetime import date, datetime
from typing import Any, Dict
from fastapi import APIRouter, Body, Depends, Request
from sqlalchemy.orm import Session
from adapters.db.models.person import PersonType
from adapters.db.session import get_db
from app.core.auth_dependency import AuthContext, get_current_user
from app.core.permissions import (
has_business_permission_for_business,
require_business_access_dep,
require_business_permission_dep,
)
from app.core.responses import ApiError, success_response
from app.services.quick_sales_service import (
create_quick_sales_payment_button,
delete_quick_sales_payment_button,
get_or_create_anonymous_customer,
get_quick_sales_settings,
list_quick_sales_payment_buttons,
reorder_quick_sales_payment_buttons,
update_quick_sales_payment_button,
update_quick_sales_settings,
)
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/businesses/{business_id}/quick-sales", tags=["quick-sales"])
def _can(ctx: AuthContext, db: Session, business_id: int, section: str, action: str) -> bool:
return has_business_permission_for_business(ctx, db, business_id, section, action)
def _body_date(value: Any) -> date:
try:
return datetime.fromisoformat(str(value)).date()
except Exception as exc:
raise ApiError("INVALID_DATE", "تاریخ اصلاح موجودی نامعتبر است", http_status=400) from exc
@router.get(
"/settings",
summary="دریافت تنظیمات فروش سریع",
description="دریافت تنظیمات فروش سریع یک کسب‌وکار شامل مشتری ناشناس، انبار، صندوق و سایر تنظیمات",
)
def get_quick_sales_settings_endpoint(
request: Request,
business_id: int,
_: None = Depends(require_business_access_dep),
db: Session = Depends(get_db),
ctx: AuthContext = Depends(get_current_user),
) -> dict:
"""دریافت تنظیمات فروش سریع"""
data = get_quick_sales_settings(db, business_id)
return success_response(data, request)
@router.put(
"/settings",
summary="ویرایش تنظیمات فروش سریع",
description="به‌روزرسانی تنظیمات فروش سریع یک کسب‌وکار",
)
def update_quick_sales_settings_endpoint(
request: Request,
business_id: int,
payload: Dict[str, Any] = Body(...),
_: None = Depends(require_business_permission_dep("settings", "business")),
db: Session = Depends(get_db),
ctx: AuthContext = Depends(get_current_user),
) -> dict:
"""به‌روزرسانی تنظیمات فروش سریع"""
data = update_quick_sales_settings(db, business_id, payload, ctx.get_user_id())
return success_response(data, request, message="QUICK_SALES_SETTINGS_UPDATED")
@router.get(
"/anonymous-customer",
summary="دریافت یا ایجاد مشتری ناشناس",
description="دریافت مشتری ناشناس پیش‌فرض یا ایجاد آن در صورت عدم وجود",
)
def get_anonymous_customer_endpoint(
request: Request,
business_id: int,
_: None = Depends(require_business_access_dep),
db: Session = Depends(get_db),
ctx: AuthContext = Depends(get_current_user),
) -> dict:
"""دریافت یا ایجاد مشتری ناشناس"""
try:
customer = get_or_create_anonymous_customer(db, business_id)
# بررسی اینکه آیا مشتری است یا نه
person_types = []
if customer.person_types:
try:
parsed_types = json.loads(customer.person_types)
person_types = parsed_types if isinstance(parsed_types, list) else []
except (json.JSONDecodeError, TypeError) as e:
logger.warning(f"Failed to parse person_types for customer {customer.id}: {e}")
person_types = []
is_customer = PersonType.CUSTOMER.value in person_types
return success_response({
"id": customer.id,
"name": customer.alias_name,
"is_customer": is_customer,
}, request)
except Exception as e:
logger.error(f"Error in get_anonymous_customer_endpoint for business {business_id}: {type(e).__name__}: {str(e)}", exc_info=True)
raise
@router.get("/payment-buttons", summary="دکمه‌های پرداخت فروش سریع")
def list_payment_buttons_endpoint(
request: Request,
business_id: int,
_: None = Depends(require_business_access_dep),
db: Session = Depends(get_db),
ctx: AuthContext = Depends(get_current_user),
) -> dict:
data = list_quick_sales_payment_buttons(db, business_id, ctx.get_user_id())
return success_response(data, request)
@router.post("/payment-buttons", summary="ایجاد دکمه پرداخت فروش سریع")
def create_payment_button_endpoint(
request: Request,
business_id: int,
payload: Dict[str, Any] = Body(...),
_: None = Depends(require_business_access_dep),
db: Session = Depends(get_db),
ctx: AuthContext = Depends(get_current_user),
) -> dict:
data = create_quick_sales_payment_button(
db,
business_id=business_id,
user_id=ctx.get_user_id(),
payload=payload,
can_manage_business=_can(ctx, db, business_id, "settings", "business"),
)
return success_response(data, request, message="QUICK_SALES_PAYMENT_BUTTON_CREATED")
@router.put("/payment-buttons/reorder", summary="تغییر ترتیب دکمه‌های پرداخت فروش سریع")
def reorder_payment_buttons_endpoint(
request: Request,
business_id: int,
payload: Dict[str, Any] = Body(...),
_: None = Depends(require_business_access_dep),
db: Session = Depends(get_db),
ctx: AuthContext = Depends(get_current_user),
) -> dict:
data = reorder_quick_sales_payment_buttons(
db,
business_id=business_id,
user_id=ctx.get_user_id(),
button_ids=list(payload.get("button_ids") or []),
can_manage_business=_can(ctx, db, business_id, "settings", "business"),
)
return success_response(data, request, message="QUICK_SALES_PAYMENT_BUTTONS_REORDERED")
@router.put("/payment-buttons/{button_id}", summary="ویرایش دکمه پرداخت فروش سریع")
def update_payment_button_endpoint(
request: Request,
business_id: int,
button_id: int,
payload: Dict[str, Any] = Body(...),
_: None = Depends(require_business_access_dep),
db: Session = Depends(get_db),
ctx: AuthContext = Depends(get_current_user),
) -> dict:
data = update_quick_sales_payment_button(
db,
business_id=business_id,
button_id=button_id,
user_id=ctx.get_user_id(),
payload=payload,
can_manage_business=_can(ctx, db, business_id, "settings", "business"),
)
return success_response(data, request, message="QUICK_SALES_PAYMENT_BUTTON_UPDATED")
@router.delete("/payment-buttons/{button_id}", summary="حذف دکمه پرداخت فروش سریع")
def delete_payment_button_endpoint(
request: Request,
business_id: int,
button_id: int,
_: None = Depends(require_business_access_dep),
db: Session = Depends(get_db),
ctx: AuthContext = Depends(get_current_user),
) -> dict:
delete_quick_sales_payment_button(
db,
business_id=business_id,
button_id=button_id,
user_id=ctx.get_user_id(),
can_manage_business=_can(ctx, db, business_id, "settings", "business"),
)
return success_response({"id": button_id}, request, message="QUICK_SALES_PAYMENT_BUTTON_DELETED")
@router.get("/stock-discrepancies", summary="فهرست مغایرت‌های موجودی فروش سریع")
def list_stock_discrepancies_endpoint(
request: Request,
business_id: int,
status: str | None = None,
_: None = Depends(require_business_access_dep),
db: Session = Depends(get_db),
ctx: AuthContext = Depends(get_current_user),
) -> dict:
if not _can(ctx, db, business_id, "inventory", "view"):
raise ApiError("FORBIDDEN", "دسترسی مشاهده موجودی را ندارید", http_status=403)
from app.services.inventory_discrepancy_service import list_cases
return success_response(list_cases(db, business_id, status), request)
@router.post("/stock-corrections", summary="اصلاح کسری پیش از ثبت فروش سریع")
def create_stock_correction_endpoint(
request: Request,
business_id: int,
payload: Dict[str, Any] = Body(...),
_: None = Depends(require_business_access_dep),
db: Session = Depends(get_db),
ctx: AuthContext = Depends(get_current_user),
) -> dict:
if payload.get("confirm_unexplained_physical_surplus") is not True:
raise ApiError(
"PHYSICAL_SURPLUS_CONFIRMATION_REQUIRED",
"باید تأیید کنید که کالا واقعاً موجود است و منشأ ثبت‌نشده دیگری ندارد",
http_status=400,
)
if not _can(ctx, db, business_id, "goods_expense_income", "add"):
raise ApiError("FORBIDDEN", "مجوز ایجاد کالای درآمدشده را ندارید", http_status=403)
from app.services.inventory_discrepancy_service import create_goods_income_correction
result = create_goods_income_correction(
db,
business_id=business_id,
user_id=ctx.get_user_id(),
document_date=_body_date(payload.get("document_date")),
lines=list(payload.get("lines") or []),
description=str(payload.get("description") or "اصلاح کسری کشف‌شده در فروش سریع"),
user_can_allocate=_can(ctx, db, business_id, "goods_expense_income", "allocate"),
user_can_post=_can(ctx, db, business_id, "goods_expense_income", "post"),
user_can_change_unit_cost=_can(ctx, db, business_id, "goods_expense_income", "change_unit_cost"),
)
db.commit()
return success_response(result, request, message="QUICK_SALES_STOCK_CORRECTED")
@router.post("/stock-discrepancies/{case_id}/resolve", summary="رفع مغایرت با کالای درآمدشده")
def resolve_stock_discrepancy_endpoint(
request: Request,
business_id: int,
case_id: int,
payload: Dict[str, Any] = Body(...),
_: None = Depends(require_business_access_dep),
db: Session = Depends(get_db),
ctx: AuthContext = Depends(get_current_user),
) -> dict:
if not _can(ctx, db, business_id, "goods_expense_income", "add"):
raise ApiError("FORBIDDEN", "مجوز ایجاد کالای درآمدشده را ندارید", http_status=403)
from app.services.inventory_discrepancy_service import resolve_case_with_goods_income
result = resolve_case_with_goods_income(
db,
business_id=business_id,
case_id=case_id,
user_id=ctx.get_user_id(),
lines=list(payload.get("lines") or []),
document_date=_body_date(payload.get("document_date")),
user_can_allocate=_can(ctx, db, business_id, "goods_expense_income", "allocate"),
user_can_post=_can(ctx, db, business_id, "goods_expense_income", "post"),
user_can_change_unit_cost=_can(ctx, db, business_id, "goods_expense_income", "change_unit_cost"),
)
return success_response(result, request, message="INVENTORY_DISCREPANCY_RESOLVED")