feat: clinic offer api

This commit is contained in:
deepvasoya 2025-05-22 11:35:33 +05:30
parent 9c39d369c1
commit 8a7f034801
9 changed files with 300 additions and 55 deletions

View File

@ -3,8 +3,9 @@ from fastapi import APIRouter, Request
from services.clinicServices import ClinicServices from services.clinicServices import ClinicServices
from schemas.UpdateSchemas import ClinicStatusUpdate from schemas.UpdateSchemas import ClinicStatusUpdate
from schemas.ApiResponse import ApiResponse from schemas.ApiResponse import ApiResponse
from schemas.BaseSchemas import CreateSuperAdmin, ResetPasswordBase from schemas.BaseSchemas import CreateSuperAdmin, MasterAppointmentTypeBase, ClinicOffersBase
from services.authService import AuthService from services.authService import AuthService
from services.masterAppointmentServices import MasterAppointmentServices
from utils.constants import DEFAULT_LIMIT, DEFAULT_PAGE from utils.constants import DEFAULT_LIMIT, DEFAULT_PAGE
router = APIRouter() router = APIRouter()
@ -29,3 +30,57 @@ def get_users(req:Request, limit:int = DEFAULT_LIMIT, page:int = DEFAULT_PAGE, s
offset = (page - 1) * limit offset = (page - 1) * limit
users = AuthService().get_admins(req.state.user, limit, offset, search) users = AuthService().get_admins(req.state.user, limit, offset, search)
return ApiResponse(data=users, message="Users retrieved successfully") return ApiResponse(data=users, message="Users retrieved successfully")
@router.post("/master-data")
def create_master_data(appointment_type: MasterAppointmentTypeBase):
MasterAppointmentServices().create_master_appointment_type(appointment_type)
return ApiResponse(data="OK", message="Master data created successfully")
@router.get("/master-data")
def get_master_data():
appointment_types = MasterAppointmentServices().get_master_appointment_types()
return ApiResponse(data=appointment_types, message="Master data retrieved successfully")
@router.get("/clinic/offers")
async def get_clinic_offers(
req:Request,
page: int = DEFAULT_PAGE,
limit: int = DEFAULT_LIMIT,
search:str = ""
):
if page < 1:
page = 1
offset = (page - 1) * limit
clinic_offers = ClinicServices().get_clinic_offers(req.state.user, limit, offset, search)
return ApiResponse(data=clinic_offers, message="Clinic offers retrieved successfully")
@router.post("/clinic/offer")
async def create_clinic_offer(
req:Request,
clinic_offer: ClinicOffersBase
):
ClinicServices().create_clinic_offer(req.state.user, clinic_offer)
return ApiResponse(data="OK", message="Clinic offer created successfully")
@router.put("/clinic/offer/{clinic_offer_id}")
async def update_clinic_offer(
req:Request,
clinic_offer_id: int,
clinic_offer: ClinicOffersBase
):
ClinicServices().update_clinic_offer(req.state.user, clinic_offer_id, clinic_offer)
return ApiResponse(data="OK", message="Clinic offer updated successfully")
@router.delete("/clinic/offer/{clinic_offer_id}")
async def delete_clinic_offer(
req:Request,
clinic_offer_id: int
):
ClinicServices().delete_clinic_offer(req.state.user, clinic_offer_id)
return ApiResponse(data="OK", message="Clinic offer deleted successfully")

View File

@ -11,6 +11,7 @@ from services.clinicServices import ClinicServices
# Constants # Constants
from schemas.ApiResponse import ApiResponse from schemas.ApiResponse import ApiResponse
from interface.common_response import CommonResponse from interface.common_response import CommonResponse
from schemas.BaseSchemas import ClinicOffersBase
from utils.constants import DEFAULT_PAGE, DEFAULT_SKIP, DEFAULT_LIMIT from utils.constants import DEFAULT_PAGE, DEFAULT_SKIP, DEFAULT_LIMIT
router = APIRouter() router = APIRouter()
@ -51,4 +52,4 @@ async def update_clinic(
@router.delete("/{clinic_id}", status_code=status.HTTP_204_NO_CONTENT) @router.delete("/{clinic_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_clinic(clinic_id: int): async def delete_clinic(clinic_id: int):
ClinicServices().delete_clinic(clinic_id) ClinicServices().delete_clinic(clinic_id)
return ApiResponse(message="Clinic deleted successfully") return ApiResponse(data="OK", message="Clinic deleted successfully")

15
models/ClinicOffers.py Normal file
View File

@ -0,0 +1,15 @@
from sqlalchemy import Column, Integer, Boolean, DateTime, ForeignKey, String
from database import Base
from .CustomBase import CustomBase
from sqlalchemy.orm import relationship
from datetime import datetime
class ClinicOffers(Base,CustomBase):
__tablename__ = "clinic_offers"
id = Column(Integer, primary_key=True, index=True)
clinic_email = Column(String)
setup_fees_waived = Column(Boolean, default=False)
special_offer_for_month = Column(String, nullable=True) # free till specified month

View File

@ -15,6 +15,7 @@ from .SignupPricingMaster import SignupPricingMaster
from .ClinicFileVerifications import ClinicFileVerifications from .ClinicFileVerifications import ClinicFileVerifications
from .OTP import OTP from .OTP import OTP
from .ResetPasswordTokens import ResetPasswordTokens from .ResetPasswordTokens import ResetPasswordTokens
from .ClinicOffers import ClinicOffers
__all__ = [ __all__ = [
"Users", "Users",
@ -33,5 +34,6 @@ __all__ = [
"SignupPricingMaster", "SignupPricingMaster",
"ClinicFileVerifications", "ClinicFileVerifications",
"OTP", "OTP",
"ResetPasswordTokens" "ResetPasswordTokens",
"ClinicOffers"
] ]

View File

@ -103,6 +103,9 @@ class CalendarBase(BaseModel):
time: str time: str
class MasterAppointmentTypeBase(BaseModel):
type: str
class UserBase(BaseModel): class UserBase(BaseModel):
username: str username: str
email: EmailStr email: EmailStr
@ -133,3 +136,10 @@ class NotificationBase(BaseModel):
is_read: bool is_read: bool
sender_id: int sender_id: int
receiver_id: int receiver_id: int
class ClinicOffersBase(BaseModel):
clinic_email: str
setup_fees_waived: bool
special_offer_for_month: str

View File

@ -29,6 +29,10 @@ class SignupPricingMasterCreate(SignupPricingMasterBase):
pass pass
class MasterAppointmentTypeCreate(MasterAppointmentTypeBase):
pass
class AppointmentCreateWithNames(BaseModel): class AppointmentCreateWithNames(BaseModel):
doctor_name: str doctor_name: str
patient_name: str patient_name: str
@ -58,4 +62,8 @@ class NotificationCreate(NotificationBase):
class S3Create(BaseModel): class S3Create(BaseModel):
folder: str folder: str
file_name: str file_name: str
clinic_id: Optional[str] = None clinic_id: Optional[str] = None
class ClinicOfferCreate(ClinicOffersBase):
pass

View File

@ -175,4 +175,23 @@ class NotificationResponse(NotificationBase):
update_time: datetime update_time: datetime
class Config: class Config:
orm_mode = True orm_mode = True
class MasterAppointmentTypeResponse(MasterAppointmentTypeBase):
id: int
create_time: datetime
update_time: datetime
class Config:
orm_mode = True
class ClinicOfferResponse(ClinicOffersBase):
id: int
create_time: datetime
update_time: datetime
class Config:
orm_mode = True

View File

@ -1,10 +1,9 @@
from database import get_db from database import get_db
from sqlalchemy.orm import Session, joinedload from sqlalchemy.orm import Session, joinedload
from models import Clinics
from schemas.UpdateSchemas import ClinicStatusUpdate, ClinicUpdate from schemas.UpdateSchemas import ClinicStatusUpdate, ClinicUpdate
from schemas.ResponseSchemas import Clinic from schemas.ResponseSchemas import Clinic, ClinicOfferResponse
from typing import List, Literal, Optional, Union from typing import List, Literal, Optional, Union
from exceptions import ResourceNotFoundException from exceptions import ResourceNotFoundException, ValidationException
from enums.enums import ClinicStatus, UserType from enums.enums import ClinicStatus, UserType
from exceptions.unauthorized_exception import UnauthorizedException from exceptions.unauthorized_exception import UnauthorizedException
from interface.common_response import CommonResponse from interface.common_response import CommonResponse
@ -12,8 +11,8 @@ from sqlalchemy import or_,func, case
from sqlalchemy import text from sqlalchemy import text
from services.s3Service import get_file_key, get_signed_url from services.s3Service import get_file_key, get_signed_url
from models import ClinicFileVerifications from models import ClinicFileVerifications, ClinicOffers, Clinics
from schemas.BaseSchemas import ClinicFileVerificationBase from schemas.BaseSchemas import ClinicFileVerificationBase, ClinicOffersBase
from services.emailService import EmailService from services.emailService import EmailService
class ClinicServices: class ClinicServices:
@ -93,6 +92,7 @@ class ClinicServices:
clinic_response.abn_doc = get_signed_url(clinic_response.abn_doc) if clinic_response.abn_doc else None clinic_response.abn_doc = get_signed_url(clinic_response.abn_doc) if clinic_response.abn_doc else None
clinic_response.contract_doc = get_signed_url(clinic_response.contract_doc) if clinic_response.contract_doc else None clinic_response.contract_doc = get_signed_url(clinic_response.contract_doc) if clinic_response.contract_doc else None
clinicFiles = None
if(clinic.status != ClinicStatus.ACTIVE): if(clinic.status != ClinicStatus.ACTIVE):
clinicFiles = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() clinicFiles = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first()
@ -200,69 +200,131 @@ class ClinicServices:
def update_clinic_status(self, user, clinic_id: int, status: ClinicStatus, documentStatus: Optional[dict] = None, rejection_reason: Optional[str] = None): def update_clinic_status(self, user, clinic_id: int, status: ClinicStatus, documentStatus: Optional[dict] = None, rejection_reason: Optional[str] = None):
try: if user["userType"] != UserType.SUPER_ADMIN:
if user["userType"] != UserType.SUPER_ADMIN: raise UnauthorizedException("You are not authorized to update clinic status")
raise UnauthorizedException("You are not authorized to update clinic status")
clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first() clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first()
if clinic is None: if clinic is None:
raise ResourceNotFoundException("Clinic not found") raise ResourceNotFoundException("Clinic not found")
clinic.status = status clinic.status = status
self.db.add(clinic) self.db.add(clinic)
self.db.commit() self.db.commit()
self.db.refresh(clinic) self.db.refresh(clinic)
if clinic.status == ClinicStatus.ACTIVE: if clinic.status == ClinicStatus.ACTIVE:
clinic_file_verification = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() clinic_file_verification = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first()
clinic_file_verification.logo_is_verified = True clinic_file_verification.logo_is_verified = True
clinic_file_verification.abn_doc_is_verified = True clinic_file_verification.abn_doc_is_verified = True
clinic_file_verification.contract_doc_is_verified = True clinic_file_verification.contract_doc_is_verified = True
self.db.add(clinic_file_verification) self.db.add(clinic_file_verification)
self.db.commit() self.db.commit()
# send mail to user # send mail to user
self.email_service.send_apporve_clinic_email(clinic.email, clinic.name) self.email_service.send_apporve_clinic_email(clinic.email, clinic.name)
if clinic.status == ClinicStatus.REJECTED or clinic.status == ClinicStatus.UNDER_REVIEW: if clinic.status == ClinicStatus.REJECTED or clinic.status == ClinicStatus.UNDER_REVIEW:
clinic_file_verification = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() clinic_file_verification = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first()
if documentStatus and "LOGO" in documentStatus: if documentStatus and "LOGO" in documentStatus:
clinic_file_verification.logo_is_verified = documentStatus.get("LOGO") clinic_file_verification.logo_is_verified = documentStatus.get("LOGO")
clinic_file_verification.rejection_reason = rejection_reason clinic_file_verification.rejection_reason = rejection_reason
if documentStatus and "ABN" in documentStatus: if documentStatus and "ABN" in documentStatus:
clinic_file_verification.abn_doc_is_verified = documentStatus.get("ABN") clinic_file_verification.abn_doc_is_verified = documentStatus.get("ABN")
clinic_file_verification.rejection_reason = rejection_reason clinic_file_verification.rejection_reason = rejection_reason
if documentStatus and "CONTRACT" in documentStatus: if documentStatus and "CONTRACT" in documentStatus:
clinic_file_verification.contract_doc_is_verified = documentStatus.get("CONTRACT") clinic_file_verification.contract_doc_is_verified = documentStatus.get("CONTRACT")
clinic_file_verification.rejection_reason = rejection_reason clinic_file_verification.rejection_reason = rejection_reason
self.db.add(clinic_file_verification) self.db.add(clinic_file_verification)
self.db.commit() self.db.commit()
# send mail to user # send mail to user
self.email_service.send_reject_clinic_email(clinic.email, clinic.name) self.email_service.send_reject_clinic_email(clinic.email, clinic.name)
# if rejected or under review then email to clinic creator # if rejected or under review then email to clinic creator
if clinic.status == ClinicStatus.REJECTED or clinic.status == ClinicStatus.UNDER_REVIEW: if clinic.status == ClinicStatus.REJECTED or clinic.status == ClinicStatus.UNDER_REVIEW:
pass pass
# handle inactive status # handle inactive status
if clinic.status == ClinicStatus.INACTIVE: if clinic.status == ClinicStatus.INACTIVE:
pass pass
return return
except Exception as e:
print(e) def get_clinic_offers(self, user, limit:int, offset:int, search:str = ""):
raise Exception(e)
if user["userType"] != UserType.SUPER_ADMIN:
raise UnauthorizedException("You are not authorized to get clinic offers")
clinic_offers_query = self.db.query(ClinicOffers)
total = clinic_offers_query.count()
if search:
clinic_offers_query = clinic_offers_query.filter(ClinicOffers.clinic_email.contains(search))
total = clinic_offers_query.count()
clinic_offers = clinic_offers_query.limit(limit).offset(offset).all()
clinic_offers_response = [ClinicOfferResponse(**clinic_offer.__dict__.copy()) for clinic_offer in clinic_offers]
response = CommonResponse(data=clinic_offers_response, total=total)
return response
def create_clinic_offer(self, user, clinic_offer_data: ClinicOffersBase):
if user["userType"] != UserType.SUPER_ADMIN:
raise UnauthorizedException("You are not authorized to create clinic offer")
existing_offer = self.db.query(ClinicOffers).filter(ClinicOffers.clinic_email == clinic_offer_data.clinic_email).first()
if existing_offer:
raise ValidationException("Offer already exists for this clinic")
clinic_offer = ClinicOffers(**clinic_offer_data.model_dump())
self.db.add(clinic_offer)
self.db.commit()
return
def update_clinic_offer(self, user, clinic_offer_id: int, clinic_offer_data: ClinicOffersBase):
if user["userType"] != UserType.SUPER_ADMIN:
raise UnauthorizedException("You are not authorized to update clinic offer")
clinic_offer = self.db.query(ClinicOffers).filter(ClinicOffers.id == clinic_offer_id).first()
if clinic_offer is None:
raise ResourceNotFoundException("Clinic offer not found")
clinic_offer.setup_fees_waived = clinic_offer_data.setup_fees_waived
clinic_offer.special_offer_for_month = clinic_offer_data.special_offer_for_month
self.db.add(clinic_offer)
self.db.commit()
return
def delete_clinic_offer(self, user, clinic_offer_id: int):
if user["userType"] != UserType.SUPER_ADMIN:
raise UnauthorizedException("You are not authorized to delete clinic offer")
clinic_offer = self.db.query(ClinicOffers).filter(ClinicOffers.id == clinic_offer_id).first()
if clinic_offer is None:
raise ResourceNotFoundException("Clinic offer not found")
clinic_offer.soft_delete(self.db)
return

View File

@ -0,0 +1,73 @@
from sqlalchemy.orm import Session
from database import get_db
from models import MasterAppointmentTypes
from schemas.BaseSchemas import MasterAppointmentTypeBase
from exceptions import ResourceNotFoundException
from interface.common_response import CommonResponse
from schemas.ResponseSchemas import MasterAppointmentTypeResponse
class MasterAppointmentServices:
def __init__(self):
self.db: Session = next(get_db())
def get_master_appointment_types(self):
appointment_types = self.db.query(MasterAppointmentTypes).all()
total= self.db.query(MasterAppointmentTypes).count()
response = CommonResponse(data=[MasterAppointmentTypeResponse(**appointment_type.__dict__.copy()) for appointment_type in appointment_types], total=total)
return response
def is_appointment_type_exists(self, appointment_type: MasterAppointmentTypeBase):
existing_appointment_type = self.db.query(MasterAppointmentTypes).filter(MasterAppointmentTypes.type == appointment_type.type.lower()).first()
return existing_appointment_type
def create_master_appointment_type(self, appointment_type: MasterAppointmentTypeBase):
# get existing appointment type
existing_appointment_type = self.is_appointment_type_exists(appointment_type)
if existing_appointment_type:
return
appointment_type.type = appointment_type.type.lower()
appointment_type = MasterAppointmentTypes(**appointment_type.model_dump())
self.db.add(appointment_type)
self.db.commit()
self.db.refresh(appointment_type)
return
def delete_master_appointment_type(self, appointment_type_id: int):
appointment_type = self.db.query(MasterAppointmentTypes).filter(MasterAppointmentTypes.id == appointment_type_id).first()
if appointment_type is None:
raise ResourceNotFoundException("Appointment type not found")
appointment_type.soft_delete(self.db)
self.db.commit()
return
def update_master_appointment_type(self, appointment_type_id: int, appointment_type: MasterAppointmentTypeBase):
appointment_type = self.db.query(MasterAppointmentTypes).filter(MasterAppointmentTypes.id == appointment_type_id).first()
if appointment_type is None:
raise ResourceNotFoundException("Appointment type not found")
appointment_type.type = appointment_type.type.lower()
# get existing appointment type
existing_appointment_type = self.is_appointment_type_exists(appointment_type)
if existing_appointment_type and existing_appointment_type.id != appointment_type_id:
raise ResourceNotFoundException("Appointment type already exists")
update_data = appointment_type.model_dump(exclude_unset=True)
for key, value in update_data.items():
setattr(appointment_type, key, value)
self.db.add(appointment_type)
self.db.commit()
self.db.refresh(appointment_type)
return