98 lines
3.7 KiB
Python
98 lines
3.7 KiB
Python
import ulid
|
|
from datetime import datetime, timedelta
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import select
|
|
from typing import Optional, List
|
|
from fastapi import HTTPException, status
|
|
|
|
from app.models.ServiceModel import ServiceJob, ServiceJobEvent
|
|
from app.models.ServiceQuoteModel import ServiceJobQuote
|
|
from app.schemas.ServiceQuoteSchema import ServiceJobQuoteCreate
|
|
|
|
class QuoteService:
|
|
def create_or_revise_quote(
|
|
self, db: Session, job_id: str, staff_id: str, data: ServiceJobQuoteCreate
|
|
) -> ServiceJobQuote:
|
|
job = db.get(ServiceJob, job_id)
|
|
if not job:
|
|
raise HTTPException(status_code=404, detail="Service job not found")
|
|
|
|
# Find existing quotes to determine version
|
|
stmt = select(ServiceJobQuote).where(ServiceJobQuote.service_job_id == job_id).order_by(ServiceJobQuote.version.desc())
|
|
existing_quotes = db.execute(stmt).scalars().all()
|
|
|
|
version = 1
|
|
if existing_quotes:
|
|
version = existing_quotes[0].version + 1
|
|
# Supersede all older quotes
|
|
for old_q in existing_quotes:
|
|
if old_q.status in ["PENDING_CUSTOMER", "DRAFT"]:
|
|
old_q.status = "SUPERSEDED"
|
|
|
|
# Create new quote version
|
|
expiry_time = datetime.utcnow() + timedelta(days=4)
|
|
quote = ServiceJobQuote(
|
|
quote_id=str(ulid.ULID()),
|
|
service_job_id=job_id,
|
|
version=version,
|
|
subtotal=data.subtotal,
|
|
tax=data.tax,
|
|
additional_damage_amount=data.additional_damage_amount,
|
|
total=data.total,
|
|
status="PENDING_CUSTOMER",
|
|
reason=data.reason,
|
|
expires_at=data.expires_at or expiry_time,
|
|
created_by=staff_id,
|
|
)
|
|
db.add(quote)
|
|
|
|
# Log Event & Update Job status
|
|
job.status = "QUOTE_SENT"
|
|
event = ServiceJobEvent(
|
|
event_id=str(ulid.ULID()),
|
|
job_id=job_id,
|
|
event_type="QUOTE_SENT",
|
|
performed_by=staff_id,
|
|
notes=f"Repair estimate quote V{version} for {data.total} INR sent to customer."
|
|
)
|
|
db.add(event)
|
|
db.commit()
|
|
db.refresh(quote)
|
|
return quote
|
|
|
|
def respond_to_quote(self, db: Session, quote_id: str, customer_id: str, action: str) -> ServiceJobQuote:
|
|
quote = db.get(ServiceJobQuote, quote_id)
|
|
if not quote:
|
|
raise HTTPException(status_code=404, detail="Quote estimate not found")
|
|
|
|
job = db.get(ServiceJob, quote.service_job_id)
|
|
if not job or job.customer_id != customer_id:
|
|
raise HTTPException(status_code=403, detail="Not authorized to respond to this quote")
|
|
|
|
if quote.status != "PENDING_CUSTOMER":
|
|
raise HTTPException(status_code=400, detail=f"Cannot respond to quote in status {quote.status}")
|
|
|
|
if action.upper() == "ACCEPT":
|
|
quote.status = "ACCEPTED"
|
|
quote.accepted_at = datetime.utcnow()
|
|
job.status = "QUOTE_ACCEPTED"
|
|
note = "Customer accepted the quote estimate."
|
|
elif action.upper() == "REJECT":
|
|
quote.status = "REJECTED"
|
|
quote.rejected_at = datetime.utcnow()
|
|
job.status = "CUSTOMER_REJECTED"
|
|
note = "Customer rejected the quote estimate."
|
|
else:
|
|
raise HTTPException(status_code=400, detail="Invalid quote response action")
|
|
|
|
event = ServiceJobEvent(
|
|
event_id=str(ulid.ULID()),
|
|
job_id=job.job_id,
|
|
event_type=f"QUOTE_{quote.status}",
|
|
performed_by=customer_id,
|
|
notes=note
|
|
)
|
|
db.add(event)
|
|
db.commit()
|
|
db.refresh(quote)
|
|
return quote
|