diff --git a/apis/endpoints/admin.py b/apis/endpoints/admin.py index 09b3f0b..ddcd398 100644 --- a/apis/endpoints/admin.py +++ b/apis/endpoints/admin.py @@ -3,8 +3,9 @@ from fastapi import APIRouter, Request from services.clinicServices import ClinicServices from schemas.UpdateSchemas import ClinicStatusUpdate 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.masterAppointmentServices import MasterAppointmentServices from utils.constants import DEFAULT_LIMIT, DEFAULT_PAGE router = APIRouter() @@ -29,3 +30,57 @@ def get_users(req:Request, limit:int = DEFAULT_LIMIT, page:int = DEFAULT_PAGE, s offset = (page - 1) * limit users = AuthService().get_admins(req.state.user, limit, offset, search) 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") + \ No newline at end of file diff --git a/apis/endpoints/clinics.py b/apis/endpoints/clinics.py index 186d7ad..6104166 100644 --- a/apis/endpoints/clinics.py +++ b/apis/endpoints/clinics.py @@ -11,6 +11,7 @@ from services.clinicServices import ClinicServices # Constants from schemas.ApiResponse import ApiResponse from interface.common_response import CommonResponse +from schemas.BaseSchemas import ClinicOffersBase from utils.constants import DEFAULT_PAGE, DEFAULT_SKIP, DEFAULT_LIMIT router = APIRouter() @@ -51,4 +52,4 @@ async def update_clinic( @router.delete("/{clinic_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_clinic(clinic_id: int): ClinicServices().delete_clinic(clinic_id) - return ApiResponse(message="Clinic deleted successfully") + return ApiResponse(data="OK", message="Clinic deleted successfully") diff --git a/models/ClinicOffers.py b/models/ClinicOffers.py new file mode 100644 index 0000000..b76d4c1 --- /dev/null +++ b/models/ClinicOffers.py @@ -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 + \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py index 05e91f9..bd3d5fa 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -15,6 +15,7 @@ from .SignupPricingMaster import SignupPricingMaster from .ClinicFileVerifications import ClinicFileVerifications from .OTP import OTP from .ResetPasswordTokens import ResetPasswordTokens +from .ClinicOffers import ClinicOffers __all__ = [ "Users", @@ -33,5 +34,6 @@ __all__ = [ "SignupPricingMaster", "ClinicFileVerifications", "OTP", - "ResetPasswordTokens" + "ResetPasswordTokens", + "ClinicOffers" ] diff --git a/schemas/BaseSchemas.py b/schemas/BaseSchemas.py index a9ede96..5484d0d 100644 --- a/schemas/BaseSchemas.py +++ b/schemas/BaseSchemas.py @@ -103,6 +103,9 @@ class CalendarBase(BaseModel): time: str +class MasterAppointmentTypeBase(BaseModel): + type: str + class UserBase(BaseModel): username: str email: EmailStr @@ -133,3 +136,10 @@ class NotificationBase(BaseModel): is_read: bool sender_id: int receiver_id: int + + +class ClinicOffersBase(BaseModel): + clinic_email: str + setup_fees_waived: bool + special_offer_for_month: str + diff --git a/schemas/CreateSchemas.py b/schemas/CreateSchemas.py index 1450151..d37a232 100644 --- a/schemas/CreateSchemas.py +++ b/schemas/CreateSchemas.py @@ -29,6 +29,10 @@ class SignupPricingMasterCreate(SignupPricingMasterBase): pass +class MasterAppointmentTypeCreate(MasterAppointmentTypeBase): + pass + + class AppointmentCreateWithNames(BaseModel): doctor_name: str patient_name: str @@ -58,4 +62,8 @@ class NotificationCreate(NotificationBase): class S3Create(BaseModel): folder: str file_name: str - clinic_id: Optional[str] = None \ No newline at end of file + clinic_id: Optional[str] = None + + +class ClinicOfferCreate(ClinicOffersBase): + pass \ No newline at end of file diff --git a/schemas/ResponseSchemas.py b/schemas/ResponseSchemas.py index 4a999cf..395c4e9 100644 --- a/schemas/ResponseSchemas.py +++ b/schemas/ResponseSchemas.py @@ -175,4 +175,23 @@ class NotificationResponse(NotificationBase): update_time: datetime class Config: - orm_mode = True \ No newline at end of file + 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 + \ No newline at end of file diff --git a/services/clinicServices.py b/services/clinicServices.py index 1ef6c6b..0dd683e 100644 --- a/services/clinicServices.py +++ b/services/clinicServices.py @@ -1,10 +1,9 @@ from database import get_db from sqlalchemy.orm import Session, joinedload -from models import Clinics 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 exceptions import ResourceNotFoundException +from exceptions import ResourceNotFoundException, ValidationException from enums.enums import ClinicStatus, UserType from exceptions.unauthorized_exception import UnauthorizedException from interface.common_response import CommonResponse @@ -12,8 +11,8 @@ from sqlalchemy import or_,func, case from sqlalchemy import text from services.s3Service import get_file_key, get_signed_url -from models import ClinicFileVerifications -from schemas.BaseSchemas import ClinicFileVerificationBase +from models import ClinicFileVerifications, ClinicOffers, Clinics +from schemas.BaseSchemas import ClinicFileVerificationBase, ClinicOffersBase from services.emailService import EmailService 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.contract_doc = get_signed_url(clinic_response.contract_doc) if clinic_response.contract_doc else None + clinicFiles = None if(clinic.status != ClinicStatus.ACTIVE): 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): - try: - if user["userType"] != UserType.SUPER_ADMIN: - raise UnauthorizedException("You are not authorized to update clinic status") + if user["userType"] != UserType.SUPER_ADMIN: + 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: - raise ResourceNotFoundException("Clinic not found") + if clinic is None: + raise ResourceNotFoundException("Clinic not found") - clinic.status = status - self.db.add(clinic) - self.db.commit() - self.db.refresh(clinic) + clinic.status = status + self.db.add(clinic) + self.db.commit() + self.db.refresh(clinic) - if clinic.status == ClinicStatus.ACTIVE: - clinic_file_verification = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() + if clinic.status == ClinicStatus.ACTIVE: + clinic_file_verification = self.db.query(ClinicFileVerifications).filter(ClinicFileVerifications.clinic_id == clinic_id).first() - clinic_file_verification.logo_is_verified = True - clinic_file_verification.abn_doc_is_verified = True - clinic_file_verification.contract_doc_is_verified = True + clinic_file_verification.logo_is_verified = True + clinic_file_verification.abn_doc_is_verified = True + clinic_file_verification.contract_doc_is_verified = True - self.db.add(clinic_file_verification) - self.db.commit() + self.db.add(clinic_file_verification) + self.db.commit() - # send mail to user - self.email_service.send_apporve_clinic_email(clinic.email, clinic.name) + # send mail to user + self.email_service.send_apporve_clinic_email(clinic.email, clinic.name) - 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() + 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() - if documentStatus and "LOGO" in documentStatus: - clinic_file_verification.logo_is_verified = documentStatus.get("LOGO") - clinic_file_verification.rejection_reason = rejection_reason + if documentStatus and "LOGO" in documentStatus: + clinic_file_verification.logo_is_verified = documentStatus.get("LOGO") + clinic_file_verification.rejection_reason = rejection_reason - if documentStatus and "ABN" in documentStatus: - clinic_file_verification.abn_doc_is_verified = documentStatus.get("ABN") - clinic_file_verification.rejection_reason = rejection_reason + if documentStatus and "ABN" in documentStatus: + clinic_file_verification.abn_doc_is_verified = documentStatus.get("ABN") + clinic_file_verification.rejection_reason = rejection_reason - if documentStatus and "CONTRACT" in documentStatus: - clinic_file_verification.contract_doc_is_verified = documentStatus.get("CONTRACT") - clinic_file_verification.rejection_reason = rejection_reason + if documentStatus and "CONTRACT" in documentStatus: + clinic_file_verification.contract_doc_is_verified = documentStatus.get("CONTRACT") + clinic_file_verification.rejection_reason = rejection_reason - self.db.add(clinic_file_verification) - self.db.commit() + self.db.add(clinic_file_verification) + self.db.commit() - # send mail to user - self.email_service.send_reject_clinic_email(clinic.email, clinic.name) + # send mail to user + self.email_service.send_reject_clinic_email(clinic.email, clinic.name) - # if rejected or under review then email to clinic creator - if clinic.status == ClinicStatus.REJECTED or clinic.status == ClinicStatus.UNDER_REVIEW: - pass + # if rejected or under review then email to clinic creator + if clinic.status == ClinicStatus.REJECTED or clinic.status == ClinicStatus.UNDER_REVIEW: + pass - # handle inactive status - if clinic.status == ClinicStatus.INACTIVE: - pass + # handle inactive status + if clinic.status == ClinicStatus.INACTIVE: + pass - return - except Exception as e: - print(e) - raise Exception(e) - \ No newline at end of file + return + + def get_clinic_offers(self, user, limit:int, offset:int, search:str = ""): + + 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 \ No newline at end of file diff --git a/services/masterAppointmentServices.py b/services/masterAppointmentServices.py new file mode 100644 index 0000000..0b992e7 --- /dev/null +++ b/services/masterAppointmentServices.py @@ -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 \ No newline at end of file