Watch
1
0
Fork
You've already forked Seyyed_arc
0
forked from hesabix/arc
Seyyed_arc/hesabixAPI/app/services/warranty_service.py
2026-05-16 18:36:22 +03:30

964 lines
34 KiB
Python
Executable file

from __future__ import annotations
import secrets
import logging
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_
from adapters.db.models.warranty import (
WarrantySetting,
WarrantyCode,
WarrantyActivation,
WarrantyTracking,
WarrantyTrackingLink,
)
from adapters.db.models.business import Business
from adapters.db.models.product import Product
from adapters.db.models.product_instance import ProductInstance
from adapters.db.models.person import Person
from adapters.db.models.marketplace import BusinessPlugin, MarketplacePlugin
from adapters.db.repositories.warranty_repository import (
WarrantySettingRepository,
WarrantyCodeRepository,
WarrantyActivationRepository,
WarrantyTrackingRepository,
WarrantyTrackingLinkRepository,
)
from app.core.responses import ApiError
from app.core.cache import get_cache
logger = logging.getLogger(__name__)
# الفبای برای تولید کدهای رندوم (بدون کاراکترهای مشابه)
BASE62_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
# ========== Helper Functions ==========
def _generate_random_code(length: int = 12) -> str:
"""تولید کد رندوم"""
return "".join(secrets.choice(BASE62_ALPHABET) for _ in range(length))
def _generate_random_serial(length: int = 12) -> str:
"""تولید سریال رندوم"""
return _generate_random_code(length)
def _generate_sequential_code(
db: Session,
business_id: int,
prefix: str = "WR",
year: Optional[int] = None
) -> str:
"""تولید کد ترتیبی"""
if year is None:
year = datetime.utcnow().year
base = f"{prefix}-{year}"
like_pattern = f"{base}-%"
last = (
db.query(WarrantyCode)
.filter(
and_(
WarrantyCode.business_id == business_id,
WarrantyCode.code.like(like_pattern)
)
)
.order_by(WarrantyCode.id.desc())
.first()
)
next_num = 1
if last and last.code.startswith(base + "-"):
try:
next_num = int(last.code.split("-")[-1]) + 1
except Exception:
next_num = 1
return f"{base}-{next_num:06d}"
def _check_warranty_plugin_active(db: Session, business_id: int) -> bool:
"""بررسی فعال بودن پلاگین گارانتی برای کسب و کار"""
plugin = db.query(MarketplacePlugin).filter(
MarketplacePlugin.code == 'product_warranty',
MarketplacePlugin.is_active == True # noqa: E712
).first()
if not plugin:
return False
license = db.query(BusinessPlugin).filter(
BusinessPlugin.business_id == business_id,
BusinessPlugin.plugin_id == plugin.id,
BusinessPlugin.status == 'active'
).first()
if not license:
return False
# بررسی انقضا
if license.ends_at and license.ends_at < datetime.utcnow():
return False
return True
def _get_or_create_warranty_settings(
db: Session,
business_id: int
) -> WarrantySetting:
"""دریافت یا ایجاد تنظیمات گارانتی پیش‌فرض"""
repo = WarrantySettingRepository(db)
settings = repo.get_by_business(business_id)
if not settings:
# ایجاد تنظیمات پیش‌فرض
settings = repo.create_or_update(business_id, {
"code_format": "random",
"code_prefix": "WR",
"serial_format": "random",
"serial_length": 12,
"require_serial_verification": False,
"require_product_instance_match": False,
"auto_link_to_person": True,
"enable_tracking_link": True,
})
db.commit()
return settings
def _find_person_by_phone(db: Session, business_id: int, phone: str) -> Optional[Person]:
"""جستجوی Person بر اساس شماره تماس"""
# نرمال‌سازی شماره تماس (حذف فاصله و کاراکترهای خاص)
normalized_phone = phone.replace(" ", "").replace("-", "").replace("_", "")
# جستجو در mobile و phone
person = db.query(Person).filter(
and_(
Person.business_id == business_id,
or_(
Person.mobile == phone,
Person.mobile == normalized_phone,
Person.mobile_2 == phone,
Person.mobile_2 == normalized_phone,
Person.mobile_3 == phone,
Person.mobile_3 == normalized_phone,
Person.phone == phone,
Person.phone == normalized_phone,
)
)
).first()
return person
def _generate_tracking_link_code(db: Session) -> str:
"""تولید کد یکتا برای لینک رهگیری"""
repo = WarrantyTrackingLinkRepository(db)
for _ in range(10):
code = _generate_random_code(16)
if not repo.check_link_code_exists(code):
return code
# fallback: استفاده از timestamp
return f"{int(datetime.utcnow().timestamp())}{secrets.randbelow(9999):04d}"
# ========== Settings Management ==========
def get_warranty_settings(
db: Session,
business_id: int
) -> Dict[str, Any]:
"""دریافت تنظیمات گارانتی"""
if not _check_warranty_plugin_active(db, business_id):
raise ApiError(
"PLUGIN_NOT_ACTIVE",
"پلاگین گارانتی برای این کسب و کار فعال نیست",
http_status=403
)
settings = _get_or_create_warranty_settings(db, business_id)
return {
"id": settings.id,
"business_id": settings.business_id,
"code_format": settings.code_format,
"code_prefix": settings.code_prefix,
"serial_format": settings.serial_format,
"serial_length": settings.serial_length,
"require_serial_verification": settings.require_serial_verification,
"require_product_instance_match": settings.require_product_instance_match,
"max_activation_attempts": settings.max_activation_attempts,
"activation_lockout_duration_minutes": settings.activation_lockout_duration_minutes,
"require_customer_registration": settings.require_customer_registration,
"auto_link_to_person": settings.auto_link_to_person,
"enable_tracking_link": settings.enable_tracking_link,
"tracking_link_expires_days": settings.tracking_link_expires_days,
"enable_sms_notification": settings.enable_sms_notification,
"enable_email_notification": settings.enable_email_notification,
"security_features": settings.security_features,
"created_at": settings.created_at,
"updated_at": settings.updated_at,
}
def update_warranty_settings(
db: Session,
business_id: int,
settings_data: Dict[str, Any]
) -> Dict[str, Any]:
"""به‌روزرسانی تنظیمات گارانتی"""
if not _check_warranty_plugin_active(db, business_id):
raise ApiError(
"PLUGIN_NOT_ACTIVE",
"پلاگین گارانتی برای این کسب و کار فعال نیست",
http_status=403
)
repo = WarrantySettingRepository(db)
settings = repo.create_or_update(business_id, settings_data)
db.commit()
return get_warranty_settings(db, business_id)
# ========== Code Generation ==========
def generate_warranty_codes(
db: Session,
business_id: int,
product_id: int,
quantity: int,
warranty_duration_days: int,
user_id: Optional[int] = None,
serial_format: str = "random",
custom_serials: Optional[List[str]] = None,
code_format: Optional[str] = None,
custom_codes: Optional[List[str]] = None,
) -> List[Dict[str, Any]]:
"""تولید انبوه کدهای گارانتی"""
if not _check_warranty_plugin_active(db, business_id):
raise ApiError(
"PLUGIN_NOT_ACTIVE",
"پلاگین گارانتی برای این کسب و کار فعال نیست",
http_status=403
)
# بررسی وجود کالا
product = db.query(Product).filter(
and_(
Product.id == product_id,
Product.business_id == business_id
)
).first()
if not product:
raise ApiError("PRODUCT_NOT_FOUND", "کالا یافت نشد", http_status=404)
# دریافت تنظیمات
settings = _get_or_create_warranty_settings(db, business_id)
# استفاده از تنظیمات یا مقادیر ارسالی
code_format = code_format or settings.code_format
serial_format = serial_format or settings.serial_format
serial_length = settings.serial_length or 12
code_prefix = settings.code_prefix or "WR"
# بررسی تعداد سریال‌های دلخواه
if serial_format == "custom":
if not custom_serials or len(custom_serials) != quantity:
raise ApiError(
"INVALID_CUSTOM_SERIALS",
f"تعداد سریال‌های دلخواه باید برابر با {quantity} باشد",
http_status=400
)
if code_format == "custom":
if not custom_codes or len(custom_codes) != quantity:
raise ApiError(
"INVALID_CUSTOM_CODES",
f"تعداد کدهای دلخواه باید برابر با {quantity} باشد",
http_status=400
)
repo = WarrantyCodeRepository(db)
generated_codes = []
for i in range(quantity):
# تولید یا استفاده از کد دلخواه
if code_format == "custom" and custom_codes:
code = custom_codes[i]
if repo.check_code_exists(code, business_id):
raise ApiError(
"DUPLICATE_CODE",
f"کد {code} در این کسب و کار تکراری است",
http_status=409
)
elif code_format == "sequential":
code = _generate_sequential_code(db, business_id, code_prefix)
else: # random
for _ in range(10):
code = f"{code_prefix}-{_generate_random_code(8)}"
if not repo.check_code_exists(code, business_id):
break
else:
raise ApiError("CODE_GENERATION_FAILED", "تولید کد یکتا ناموفق بود", http_status=500)
# تولید یا استفاده از سریال دلخواه
if serial_format == "custom" and custom_serials:
warranty_serial = custom_serials[i]
if repo.check_serial_exists(business_id, warranty_serial):
raise ApiError(
"DUPLICATE_SERIAL",
f"سریال {warranty_serial} تکراری است",
http_status=409
)
else: # random
for _ in range(10):
warranty_serial = _generate_random_serial(serial_length)
if not repo.check_serial_exists(business_id, warranty_serial):
break
else:
raise ApiError("SERIAL_GENERATION_FAILED", "تولید سریال یکتا ناموفق بود", http_status=500)
# محاسبه تاریخ انقضا
expires_at = None
if warranty_duration_days > 0:
expires_at = datetime.utcnow() + timedelta(days=warranty_duration_days)
# ایجاد کد گارانتی
warranty_code = WarrantyCode(
business_id=business_id,
code=code,
warranty_serial=warranty_serial,
product_id=product_id,
status="generated",
generated_by_user_id=user_id,
generated_at=datetime.utcnow(),
warranty_duration_days=warranty_duration_days,
expires_at=expires_at,
)
db.add(warranty_code)
db.flush()
generated_codes.append({
"id": warranty_code.id,
"business_id": warranty_code.business_id,
"code": warranty_code.code,
"warranty_serial": warranty_code.warranty_serial,
"product_id": warranty_code.product_id,
"product_instance_id": warranty_code.product_instance_id,
"status": warranty_code.status,
"generated_by_user_id": warranty_code.generated_by_user_id,
"generated_at": warranty_code.generated_at,
"activated_at": warranty_code.activated_at,
"activated_by_person_id": warranty_code.activated_by_person_id,
"activated_by_customer_info": warranty_code.activated_by_customer_info,
"expires_at": warranty_code.expires_at,
"warranty_duration_days": warranty_code.warranty_duration_days,
"tracking_link_code": warranty_code.tracking_link_code,
"extra_metadata": warranty_code.extra_metadata,
"created_at": warranty_code.created_at,
"updated_at": warranty_code.updated_at,
})
db.commit()
return generated_codes
# ========== Activation ==========
def _check_activation_attempts(
db: Session,
warranty_code: WarrantyCode,
ip_address: Optional[str],
settings: WarrantySetting
) -> None:
"""بررسی محدودیت تلاش برای فعال‌سازی"""
if not settings.max_activation_attempts:
return
cache = get_cache()
if not cache:
return
# کلید cache برای IP
cache_key = f"warranty_activation_attempts:{warranty_code.id}:{ip_address or 'unknown'}"
attempts = cache.get(cache_key)
if attempts is None:
attempts = 0
lockout_minutes = settings.activation_lockout_duration_minutes or 30
if attempts >= settings.max_activation_attempts:
raise ApiError(
"TOO_MANY_ATTEMPTS",
f"تعداد تلاش‌های شما بیش از حد مجاز است. لطفاً {lockout_minutes} دقیقه دیگر تلاش کنید.",
http_status=429
)
# افزایش تعداد تلاش
cache.set(cache_key, attempts + 1, timeout=lockout_minutes * 60)
def _clear_activation_attempts(
warranty_code_id: int,
ip_address: Optional[str]
) -> None:
"""پاک کردن تعداد تلاش‌ها پس از موفقیت"""
cache = get_cache()
if cache:
cache_key = f"warranty_activation_attempts:{warranty_code_id}:{ip_address or 'unknown'}"
cache.delete(cache_key)
def activate_warranty(
db: Session,
business_id: int,
warranty_code_str: str,
warranty_serial: str,
customer_name: str,
customer_phone: str,
customer_email: Optional[str] = None,
product_serial: Optional[str] = None,
ip_address: Optional[str] = None,
user_agent: Optional[str] = None,
) -> Dict[str, Any]:
"""فعال‌سازی گارانتی توسط مشتری"""
repo = WarrantyCodeRepository(db)
# یافتن کد گارانتی در کسب و کار مشخص شده
warranty_code = repo.get_by_code(warranty_code_str, business_id)
if not warranty_code:
raise ApiError("WARRANTY_CODE_NOT_FOUND", "کد گارانتی در این کسب و کار یافت نشد", http_status=404)
# بررسی فعال بودن پلاگین برای کسب و کار
if not _check_warranty_plugin_active(db, warranty_code.business_id):
raise ApiError(
"PLUGIN_NOT_ACTIVE",
"پلاگین گارانتی برای این کسب و کار فعال نیست",
http_status=403
)
# بررسی سریال گارانتی
if warranty_code.warranty_serial != warranty_serial:
raise ApiError("INVALID_WARRANTY_SERIAL", "سریال گارانتی معتبر نیست", http_status=400)
# بررسی وضعیت
if warranty_code.status != "generated":
if warranty_code.status == "activated":
raise ApiError("WARRANTY_ALREADY_ACTIVATED", "این گارانتی قبلاً فعال شده است", http_status=400)
elif warranty_code.status == "expired":
raise ApiError("WARRANTY_EXPIRED", "این گارانتی منقضی شده است", http_status=400)
elif warranty_code.status == "revoked":
raise ApiError("WARRANTY_REVOKED", "این گارانتی لغو شده است", http_status=400)
else:
raise ApiError("INVALID_WARRANTY_STATUS", f"وضعیت گارانتی معتبر نیست: {warranty_code.status}", http_status=400)
# بررسی انقضا
if warranty_code.expires_at and warranty_code.expires_at < datetime.utcnow():
warranty_code.status = "expired"
db.commit()
raise ApiError("WARRANTY_EXPIRED", "این گارانتی منقضی شده است", http_status=400)
# دریافت تنظیمات
settings = _get_or_create_warranty_settings(db, warranty_code.business_id)
# بررسی محدودیت تلاش
_check_activation_attempts(db, warranty_code, ip_address, settings)
# بررسی نیاز به تأیید سریال کالا
product_instance = None
if settings.require_serial_verification or settings.require_product_instance_match:
if not product_serial:
raise ApiError("PRODUCT_SERIAL_REQUIRED", "وارد کردن سریال کالا الزامی است", http_status=400)
# جستجوی product_instance
product_instance = db.query(ProductInstance).filter(
and_(
ProductInstance.business_id == warranty_code.business_id,
ProductInstance.product_id == warranty_code.product_id,
ProductInstance.serial_number == product_serial
)
).first()
if not product_instance:
raise ApiError("PRODUCT_SERIAL_NOT_FOUND", "سریال کالا یافت نشد", http_status=404)
if settings.require_product_instance_match:
warranty_code.product_instance_id = product_instance.id
# اتصال به Person (اگر فعال باشد)
person = None
if settings.auto_link_to_person:
person = _find_person_by_phone(db, warranty_code.business_id, customer_phone)
# فعال‌سازی
warranty_code.status = "activated"
warranty_code.activated_at = datetime.utcnow()
warranty_code.activated_by_person_id = person.id if person else None
warranty_code.activated_by_customer_info = {
"name": customer_name,
"phone": customer_phone,
"email": customer_email,
} if not person else None
if product_instance:
warranty_code.product_instance_id = product_instance.id
# ایجاد لینک رهگیری (اگر فعال باشد)
tracking_link = None
if settings.enable_tracking_link and person:
link_code = _generate_tracking_link_code(db)
warranty_code.tracking_link_code = link_code
expires_at = None
if settings.tracking_link_expires_days:
expires_at = datetime.utcnow() + timedelta(days=settings.tracking_link_expires_days)
tracking_link = WarrantyTrackingLink(
warranty_code_id=warranty_code.id,
person_id=person.id,
link_code=link_code,
expires_at=expires_at,
is_active=True,
)
db.add(tracking_link)
# ثبت فعال‌سازی
activation = WarrantyActivation(
warranty_code_id=warranty_code.id,
person_id=person.id if person else None,
product_instance_id=product_instance.id if product_instance else None,
warranty_serial=warranty_serial,
product_serial=product_serial,
customer_name=customer_name,
customer_phone=customer_phone,
customer_email=customer_email,
activation_date=datetime.utcnow(),
ip_address=ip_address,
user_agent=user_agent,
verification_method="product_instance_match" if product_instance else "serial_match" if product_serial else "manual",
)
db.add(activation)
# ثبت رویداد رهگیری
tracking_event = WarrantyTracking(
warranty_code_id=warranty_code.id,
product_instance_id=product_instance.id if product_instance else None,
person_id=person.id if person else None,
event_type="activation",
description=f"گارانتی توسط {customer_name} فعال شد",
)
db.add(tracking_event)
db.commit()
# پاک کردن تعداد تلاش‌ها
_clear_activation_attempts(warranty_code.id, ip_address)
return {
"id": warranty_code.id,
"code": warranty_code.code,
"warranty_serial": warranty_code.warranty_serial,
"status": warranty_code.status,
"activated_at": warranty_code.activated_at,
"expires_at": warranty_code.expires_at,
"tracking_link_code": warranty_code.tracking_link_code,
"person_id": person.id if person else None,
}
# ========== Tracking ==========
def track_warranty(
db: Session,
code_or_serial: str,
business_id: Optional[int] = None
) -> Dict[str, Any]:
"""رهگیری گارانتی"""
repo = WarrantyCodeRepository(db)
# جستجو بر اساس کد یا سریال
warranty_code = repo.get_by_code(code_or_serial)
if not warranty_code:
# اگر business_id مشخص است، جستجو بر اساس سریال
if business_id:
warranty_code = repo.get_by_serial(business_id, code_or_serial)
if not warranty_code:
raise ApiError("WARRANTY_NOT_FOUND", "گارانتی یافت نشد", http_status=404)
# دریافت اطلاعات کالا
product = db.query(Product).filter(Product.id == warranty_code.product_id).first()
# دریافت اطلاعات کسب و کار
business = db.query(Business).filter(Business.id == warranty_code.business_id).first()
# دریافت تاریخچه رهگیری
tracking_repo = WarrantyTrackingRepository(db)
tracking_events = tracking_repo.list_by_warranty_code(warranty_code.id)
return {
"id": warranty_code.id,
"code": warranty_code.code,
"warranty_serial": warranty_code.warranty_serial,
"status": warranty_code.status,
"generated_at": warranty_code.generated_at,
"activated_at": warranty_code.activated_at,
"expires_at": warranty_code.expires_at,
"warranty_duration_days": warranty_code.warranty_duration_days,
"product": {
"id": product.id if product else None,
"name": product.name if product else None,
"code": product.code if product else None,
} if product else None,
"business": {
"id": business.id if business else None,
"name": business.name if business else None,
"code": business.code if business else None,
} if business else None,
"tracking_events": [
{
"id": event.id,
"event_type": event.event_type,
"description": event.description,
"created_at": event.created_at,
}
for event in tracking_events
],
}
def track_warranty_by_link(
db: Session,
link_code: str
) -> Dict[str, Any]:
"""رهگیری گارانتی از طریق لینک یکتا"""
link_repo = WarrantyTrackingLinkRepository(db)
link = link_repo.get_by_link_code(link_code)
if not link:
raise ApiError("LINK_NOT_FOUND", "لینک رهگیری یافت نشد", http_status=404)
# بررسی انقضا
if link.expires_at and link.expires_at < datetime.utcnow():
raise ApiError("LINK_EXPIRED", "لینک رهگیری منقضی شده است", http_status=400)
# بررسی فعال بودن
if not link.is_active:
raise ApiError("LINK_INACTIVE", "لینک رهگیری غیرفعال است", http_status=400)
# افزایش تعداد دسترسی
link_repo.increment_access_count(link.id)
db.commit()
# دریافت اطلاعات گارانتی
warranty_code_repo = WarrantyCodeRepository(db)
warranty_code = warranty_code_repo.get_by_id(link.warranty_code_id)
if not warranty_code:
raise ApiError("WARRANTY_CODE_NOT_FOUND", "کد گارانتی مرتبط با لینک یافت نشد", http_status=404)
return track_warranty(db, warranty_code.code, warranty_code.business_id)
# ========== List Codes ==========
def list_warranty_codes(
db: Session,
business_id: int,
status: Optional[str] = None,
product_id: Optional[int] = None,
limit: int = 100,
skip: int = 0
) -> Dict[str, Any]:
"""لیست کدهای گارانتی"""
if not _check_warranty_plugin_active(db, business_id):
raise ApiError(
"PLUGIN_NOT_ACTIVE",
"پلاگین گارانتی برای این کسب و کار فعال نیست",
http_status=403
)
repo = WarrantyCodeRepository(db)
codes = repo.list_by_business(business_id, status, product_id, limit, skip)
total = repo.count_by_business(business_id, status, product_id)
page = (skip // limit) + 1 if limit > 0 else 1
total_pages = (total + limit - 1) // limit if limit > 0 else 0
return {
"items": [
{
"id": code.id,
"business_id": code.business_id,
"code": code.code,
"warranty_serial": code.warranty_serial,
"product_id": code.product_id,
"product_instance_id": code.product_instance_id,
"status": code.status,
"generated_by_user_id": code.generated_by_user_id,
"generated_at": code.generated_at,
"activated_at": code.activated_at,
"activated_by_person_id": code.activated_by_person_id,
"activated_by_customer_info": code.activated_by_customer_info,
"expires_at": code.expires_at,
"warranty_duration_days": code.warranty_duration_days,
"tracking_link_code": code.tracking_link_code,
"extra_metadata": code.extra_metadata,
"created_at": code.created_at,
"updated_at": code.updated_at,
}
for code in codes
],
"total": total,
"limit": limit,
"skip": skip,
"page": page,
"total_pages": total_pages,
}
def list_warranty_codes_by_person(
db: Session,
business_id: int,
person_id: int,
status: Optional[str] = None,
limit: int = 100,
skip: int = 0
) -> Dict[str, Any]:
"""لیست کدهای گارانتی یک Person"""
if not _check_warranty_plugin_active(db, business_id):
raise ApiError(
"PLUGIN_NOT_ACTIVE",
"پلاگین گارانتی برای این کسب و کار فعال نیست",
http_status=403
)
repo = WarrantyCodeRepository(db)
codes = repo.list_by_person(business_id, person_id, status, limit, skip)
total = repo.count_by_person(business_id, person_id, status)
page = (skip // limit) + 1 if limit > 0 else 1
total_pages = (total + limit - 1) // limit if limit > 0 else 0
return {
"items": [
{
"id": code.id,
"business_id": code.business_id,
"code": code.code,
"warranty_serial": code.warranty_serial,
"product_id": code.product_id,
"product_instance_id": code.product_instance_id,
"status": code.status,
"generated_by_user_id": code.generated_by_user_id,
"generated_at": code.generated_at,
"activated_at": code.activated_at,
"activated_by_person_id": code.activated_by_person_id,
"activated_by_customer_info": code.activated_by_customer_info,
"expires_at": code.expires_at,
"warranty_duration_days": code.warranty_duration_days,
"tracking_link_code": code.tracking_link_code,
"extra_metadata": code.extra_metadata,
"created_at": code.created_at,
"updated_at": code.updated_at,
}
for code in codes
],
"total": total,
"limit": limit,
"skip": skip,
"page": page,
"total_pages": total_pages,
}
# ========== Delete Codes ==========
def delete_warranty_code(
db: Session,
business_id: int,
code_id: int,
force: bool = False
) -> Dict[str, Any]:
"""حذف یک کد گارانتی به صورت ایمن"""
if not _check_warranty_plugin_active(db, business_id):
raise ApiError(
"PLUGIN_NOT_ACTIVE",
"پلاگین گارانتی برای این کسب و کار فعال نیست",
http_status=403
)
repo = WarrantyCodeRepository(db)
warranty_code = repo.get_by_id(code_id)
if not warranty_code:
raise ApiError("WARRANTY_CODE_NOT_FOUND", "کد گارانتی یافت نشد", http_status=404)
# بررسی تعلق به کسب و کار
if warranty_code.business_id != business_id:
raise ApiError("ACCESS_DENIED", "شما دسترسی به این کد گارانتی ندارید", http_status=403)
# بررسی وضعیت کد برای حذف ایمن
if not force and warranty_code.status in ["activated", "used"]:
raise ApiError(
"WARRANTY_CODE_ACTIVE",
"این کد گارانتی فعال شده است. برای حذف از پارامتر force استفاده کنید.",
http_status=400
)
# حذف رکوردهای مرتبط
# 1. حذف رویدادهای رهگیری
tracking_repo = WarrantyTrackingRepository(db)
tracking_events = tracking_repo.list_by_warranty_code(warranty_code.id)
for event in tracking_events:
db.delete(event)
# 2. حذف لینک‌های رهگیری
link_repo = WarrantyTrackingLinkRepository(db)
tracking_links = link_repo.list_by_warranty_code(warranty_code.id)
for link in tracking_links:
db.delete(link)
# 3. حذف رکورد فعال‌سازی
activation_repo = WarrantyActivationRepository(db)
activation = activation_repo.get_by_warranty_code(warranty_code.id)
if activation:
db.delete(activation)
# 4. حذف کد گارانتی
code_data = {
"id": warranty_code.id,
"code": warranty_code.code,
"warranty_serial": warranty_code.warranty_serial,
"status": warranty_code.status,
}
db.delete(warranty_code)
db.commit()
return {
"success": True,
"message": "کد گارانتی با موفقیت حذف شد",
"deleted_code": code_data,
}
def delete_warranty_codes_bulk(
db: Session,
business_id: int,
code_ids: List[int],
force: bool = False
) -> Dict[str, Any]:
"""حذف گروهی کدهای گارانتی به صورت ایمن"""
if not _check_warranty_plugin_active(db, business_id):
raise ApiError(
"PLUGIN_NOT_ACTIVE",
"پلاگین گارانتی برای این کسب و کار فعال نیست",
http_status=403
)
if not code_ids:
raise ApiError("INVALID_INPUT", "لیست کدها خالی است", http_status=400)
repo = WarrantyCodeRepository(db)
tracking_repo = WarrantyTrackingRepository(db)
link_repo = WarrantyTrackingLinkRepository(db)
activation_repo = WarrantyActivationRepository(db)
deleted_codes = []
failed_codes = []
skipped_codes = []
for code_id in code_ids:
try:
warranty_code = repo.get_by_id(code_id)
if not warranty_code:
failed_codes.append({
"id": code_id,
"reason": "کد گارانتی یافت نشد"
})
continue
# بررسی تعلق به کسب و کار
if warranty_code.business_id != business_id:
failed_codes.append({
"id": code_id,
"code": warranty_code.code,
"reason": "عدم دسترسی"
})
continue
# بررسی وضعیت کد برای حذف ایمن
if not force and warranty_code.status in ["activated", "used"]:
skipped_codes.append({
"id": code_id,
"code": warranty_code.code,
"status": warranty_code.status,
"reason": "کد فعال شده است"
})
continue
# حذف رکوردهای مرتبط
# 1. حذف رویدادهای رهگیری
tracking_events = tracking_repo.list_by_warranty_code(warranty_code.id)
for event in tracking_events:
db.delete(event)
# 2. حذف لینک‌های رهگیری
tracking_links = link_repo.list_by_warranty_code(warranty_code.id)
for link in tracking_links:
db.delete(link)
# 3. حذف رکورد فعال‌سازی
activation = activation_repo.get_by_warranty_code(warranty_code.id)
if activation:
db.delete(activation)
# 4. حذف کد گارانتی
deleted_codes.append({
"id": warranty_code.id,
"code": warranty_code.code,
"warranty_serial": warranty_code.warranty_serial,
"status": warranty_code.status,
})
db.delete(warranty_code)
except Exception as e:
logger.error(f"Error deleting warranty code {code_id}: {e}")
failed_codes.append({
"id": code_id,
"reason": str(e)
})
db.commit()
return {
"success": True,
"message": f"عملیات حذف گروهی انجام شد",
"summary": {
"total_requested": len(code_ids),
"deleted": len(deleted_codes),
"skipped": len(skipped_codes),
"failed": len(failed_codes),
},
"deleted_codes": deleted_codes,
"skipped_codes": skipped_codes,
"failed_codes": failed_codes,
}