ifixkart-backend/app/models/ServiceModel.py

152 lines
7.5 KiB
Python

from sqlalchemy import Column, String, Boolean, DateTime, Integer, Numeric, ForeignKey
from sqlalchemy.sql import func
from sqlalchemy.orm import relationship
from app.core.database.db_session import Base
class ServiceCatalog(Base):
__tablename__ = "service_catalog"
service_id = Column(String(26), primary_key=True)
name = Column(String(255), nullable=False)
description = Column(String(1000), nullable=True)
base_price = Column(Numeric(10, 2), nullable=False)
estimated_duration_minutes = Column(Integer, nullable=False)
workflow_type = Column(String(50), default="REPAIR_QUOTE") # FIXED_SERVICE, INSPECTION_REQUIRED, REPAIR_QUOTE
active = Column(Boolean, default=True)
from sqlalchemy import Date
class ServiceJob(Base):
__tablename__ = "service_jobs"
job_id = Column(String(26), primary_key=True)
job_no = Column(String(50), unique=True, nullable=False)
customer_id = Column(String(26), nullable=False, index=True)
device_id = Column(String(26), ForeignKey("customer_devices.device_id"), nullable=False)
source = Column(String(20), default="ONLINE") # ONLINE, WALK_IN
service_id = Column(String(26), ForeignKey("service_catalog.service_id"), nullable=True) # Nullable for Cashify-style jobs
status = Column(String(30), default="BOOKING_PENDING", index=True)
custom_service_name = Column(String(255), nullable=True)
# Granular selection IDs
device_type = Column(String(50), nullable=True)
brand_id = Column(String(26), ForeignKey("brands.brand_id"), nullable=True)
series_id = Column(String(26), ForeignKey("device_series.series_id"), nullable=True)
model_id = Column(String(26), ForeignKey("device_models.model_id"), nullable=True)
service_type_id = Column(String(26), nullable=True)
repair_service_id = Column(String(26), nullable=True)
repair_variant_id = Column(String(26), nullable=True)
# Parity Price & Config Snapshots
currency = Column(String(3), default="INR", nullable=False)
service_name_snapshot = Column(String(255), nullable=True)
variant_name_snapshot = Column(String(255), nullable=True)
base_price_snapshot = Column(Numeric(10, 2), nullable=True)
duration_snapshot = Column(Integer, nullable=True)
warranty_snapshot = Column(Integer, nullable=True)
inspection_fee_snapshot = Column(Numeric(10, 2), nullable=True)
# Queue & Priority Attributes
queue_number = Column(String(50), nullable=True)
queue_date = Column(Date, nullable=True)
priority = Column(String(20), default="NORMAL")
# Fulfillment & Contact Attributes
fulfillment_type = Column(String(32), nullable=False, default="WALK_IN") # WALK_IN, COURIER, DOORSTEP_PICKUP
fulfillment_fee = Column(Numeric(10, 2), nullable=False, default=0.00)
alt_phone = Column(String(32), nullable=True)
is_whatsapp_alt = Column(Boolean, default=True)
delivery_address = Column(String(500), nullable=True)
pre_dispatch_video_id = Column(String(26), nullable=True)
courier_name = Column(String(100), nullable=True)
awb_number = Column(String(100), nullable=True)
pickup_status = Column(String(50), nullable=True)
# Security Lock Credentials & Audit Metadata
lock_type = Column(String(16), nullable=False, default="NONE") # NONE, PIN, PASSWORD, PATTERN
lock_credential_encrypted = Column(String(500), nullable=True)
lock_credential_created_at = Column(DateTime, nullable=True)
lock_credential_accessed_at = Column(DateTime, nullable=True)
lock_credential_accessed_by = Column(String(36), nullable=True)
lock_credential_deleted_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
device = relationship("CustomerDevice")
service = relationship("ServiceCatalog")
class ServiceAppointment(Base):
__tablename__ = "service_appointments"
appointment_id = Column(String(26), primary_key=True)
service_job_id = Column(String(26), ForeignKey("service_jobs.job_id"), nullable=False, index=True)
scheduled_start = Column(DateTime, nullable=False)
scheduled_end = Column(DateTime, nullable=False)
status = Column(String(20), default="HELD", index=True) # HELD, CONFIRMED, CANCELLED, EXPIRED, COMPLETED
hold_expires_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, server_default=func.now())
confirmed_at = Column(DateTime, nullable=True)
cancelled_at = Column(DateTime, nullable=True)
job = relationship("ServiceJob")
class ServiceJobAssignment(Base):
__tablename__ = "service_job_assignments"
assignment_id = Column(String(26), primary_key=True)
service_job_id = Column(String(26), ForeignKey("service_jobs.job_id"), nullable=False, index=True)
appointment_id = Column(String(26), ForeignKey("service_appointments.appointment_id"), nullable=False)
technician_id = Column(String(26), nullable=False, index=True)
assigned_at = Column(DateTime, server_default=func.now())
assigned_by = Column(String(26), nullable=True)
unassigned_at = Column(DateTime, nullable=True)
reason = Column(String(255), nullable=True)
class ServiceJobIntake(Base):
__tablename__ = "service_job_intakes"
intake_id = Column(String(26), primary_key=True)
service_job_id = Column(String(26), ForeignKey("service_jobs.job_id"), nullable=False, unique=True)
received_by = Column(String(26), nullable=False)
received_at = Column(DateTime, server_default=func.now())
power_status = Column(String(50), nullable=True)
screen_condition = Column(String(100), nullable=True)
body_condition = Column(String(100), nullable=True)
back_condition = Column(String(100), nullable=True)
camera_condition = Column(String(100), nullable=True)
accessories = Column(String(255), nullable=True)
customer_notes = Column(String(1000), nullable=True)
technician_notes = Column(String(1000), nullable=True)
class ServiceJobInspection(Base):
__tablename__ = "service_job_inspections"
inspection_id = Column(String(26), primary_key=True)
service_job_id = Column(String(26), ForeignKey("service_jobs.job_id"), nullable=False, index=True)
technician_id = Column(String(26), nullable=False, index=True)
result = Column(String(50), nullable=False) # CUSTOMER_REPORT_CONFIRMED, CUSTOMER_REPORT_PARTIALLY_CONFIRMED, CUSTOMER_REPORT_NOT_CONFIRMED, ADDITIONAL_DAMAGE_FOUND
customer_report = Column(String(1000), nullable=True)
confirmed_damage = Column(String(1000), nullable=True)
additional_damage = Column(String(1000), nullable=True)
notes = Column(String(1000), nullable=True)
created_at = Column(DateTime, server_default=func.now())
class ServiceJobMedia(Base):
__tablename__ = "service_job_media"
media_id = Column(String(26), primary_key=True)
service_job_id = Column(String(26), ForeignKey("service_jobs.job_id"), nullable=False, index=True)
category = Column(String(50), nullable=False) # INTAKE_PHOTO, INTAKE_VIDEO, BEFORE_REPAIR, AFTER_REPAIR
file_id = Column(String(100), nullable=False)
created_at = Column(DateTime, server_default=func.now())
class ServiceJobEvent(Base):
__tablename__ = "service_job_events"
event_id = Column(String(26), primary_key=True)
job_id = Column(String(26), ForeignKey("service_jobs.job_id"), nullable=False, index=True)
event_type = Column(String(50), nullable=False)
performed_by = Column(String(26), nullable=False)
timestamp = Column(DateTime, server_default=func.now())
notes = Column(String(500), nullable=True)