import { Paging } from "entity"; import { NextFunction, Response } from "express"; import { Request } from "express-jwt"; import Joi from "joi"; import { ILogObj, Logger } from "tslog"; import CommonHelper from "../helpers/common"; import { ReturnHelper } from "../helpers/express/return"; import { Language } from "../langs/lang"; import { ServiceModel } from "../model/service"; import { RoomServicesModel } from "../model/room_services"; import { RoomModel } from "../model/room"; import { DepartmentModel } from "../model/department"; import { OrmHelper } from "../helpers/orm"; const log: Logger = new Logger({ name: "[RoomServicesController]", type: "pretty", }); export class RoomServicesController { // Controller - List (Room-sided, default) static async list(req: Request, res: Response, next: NextFunction): Promise { /* #swagger.tags = ['Room Services'] #swagger.security = [{ "bearerAuth": [] }] #swagger.parameters['filter'] = { description: 'Filter data in JSON format. Available fields: {\"any\": \"value\"} for searching by Room.code, Room.room, or Department.name (case-insensitive, supports partial match)', in: 'query', type: 'string', example: '{\"any\": \"radiology\"}' } #swagger.parameters['limit'] = { in: 'query', required: true, type: 'number' } #swagger.parameters['page'] = { in: 'query', required: true, type: 'number' } #swagger.parameters['with_deleted'] = { in: 'query', required: true, type: 'boolean' } #swagger.parameters['order_field'] = { in: 'query', required: true, type: 'string' } #swagger.parameters['order_direction'] = { in: 'query', required: true, schema: { '@enum': ['ASC', 'DESC'] } } */ try { const schema = Joi.object().keys({ filter: Joi.string().allow("").optional().label("Filter"), page: Joi.number().required().min(1).label("Page"), limit: Joi.number().required().min(1).label("Limit"), with_deleted: Joi.bool().required().label("With Deleted"), order_field: Joi.string().required().label("Order Field"), order_direction: Joi.string().allow("asc", "desc").required().label("Order Direction"), }); let param: Paging = await schema.validateAsync(req.query); let filter = param.filter && param.filter !== "" ? JSON.parse(param.filter) : {}; const filterAny = filter.any && filter.any !== "" ? String(filter.any) : ""; let filterObj = { ...filter } as any; delete filterObj.any; // Get total count of rooms const query = await RoomServicesModel.listByRoom(filterObj, param.with_deleted); if (filterAny !== "") { const anyVal = filterAny.includes("%") ? filterAny : `%${filterAny}%`; query.andWhere( "(Room.code ILIKE :any OR Room.room ILIKE :any OR Department.name ILIKE :any)", { any: anyVal } ); } const total_count_data = await query.getCount(); // Get all rooms with services const rooms = await query .orderBy("Room." + param.order_field, param.order_direction) .skip((param.page - 1) * param.limit) .take(param.limit) .getMany(); const { byTypeId, typeMeta } = await RoomServicesModel.getActiveServicesGroupedByType(); const list_data = rooms.map((room) => { const service_types = RoomServicesModel.deriveTypesForRoomWithMap(room, byTypeId, typeMeta); return { id: room.id, room: room.room, code: room.code, description: room.description, status: room.status, department: room.department ? { id: room.department.id, name: room.department.name, } : null, service_types, services: room.services ? room.services.map((service) => ({ id: service.id, name: service.name, service_type: service.serviceType ? { id: service.serviceType.id, name: service.serviceType.name, } : null, })) : [], }; }); const count_data = list_data.length; return ReturnHelper.successResponselist( res, 200, Language.lang.success_view, count_data, param.page, total_count_data, list_data ); } catch (e: unknown) { log.error(e); const err = e as Error; return ReturnHelper.errorResponse( res, 400, 401, Language.lang.failed_view, err.message ); } } // Controller - List by Service (Service-sided) static async listServices(req: Request, res: Response, next: NextFunction): Promise { /* #swagger.tags = ['Room Services'] #swagger.security = [{ "bearerAuth": [] }] #swagger.parameters['room_id'] = { description: 'Room ID', in: 'path', type: 'string' } #swagger.parameters['filter'] = { description: '', in: 'query', type: 'string' } #swagger.parameters['limit'] = { in: 'query', required: true, type: 'number' } #swagger.parameters['page'] = { in: 'query', required: true, type: 'number' } #swagger.parameters['with_deleted'] = { in: 'query', required: true, type: 'boolean' } #swagger.parameters['order_field'] = { in: 'query', required: true, type: 'string' } #swagger.parameters['order_direction'] = { in: 'query', required: true, schema: { '@enum': ['ASC', 'DESC'] } } */ try { const schema = Joi.object().keys({ filter: Joi.string().allow("").optional().label("Filter"), page: Joi.number().required().min(1).label("Page"), limit: Joi.number().required().min(1).label("Limit"), with_deleted: Joi.bool().required().label("With Deleted"), order_field: Joi.string().required().label("Order Field"), order_direction: Joi.string().allow("asc", "desc").required().label("Order Direction"), }); let param: Paging = await schema.validateAsync(req.query); const roomId = req.params["room_id"]; const room = await RoomModel.list({ "Room.id": roomId }).then((q) => q.getOne()); if (!room) throw { message: "Room " + Language.lang.failed_not_found }; let filter = param.filter && param.filter !== "" ? JSON.parse(param.filter) : {}; var query = await ServiceModel.list(filter); const offset = (param.page - 1) * param.limit; let limit = param.limit; const base_query = query .select([ "Service.id", "Service.name", "Service.created_at", "Service.created_by", "serviceType.id", "serviceType.name", "serviceFare.id", "serviceFare.fare", "fareType.id", "fareType.name", ]) .leftJoin("Service.serviceType", "serviceType") .leftJoin("Service.rooms", "room") .innerJoin("Service.servicefare", "serviceFare") .innerJoin("serviceFare.serviceClass", "serviceClass") .innerJoin("serviceFare.faretype", "fareType") .andWhere("room.id = :roomId", { roomId }); // .select([ // "Service.id", // "Service.name", // "Service.created_at", // "Service.created_by", // "serviceType.id", // "serviceType.name", // "serviceFare.id", // "serviceFare.fare", // "fareType.id", // "fareType.name", // ]) // .leftJoin("Service.serviceType", "serviceType") // .leftJoin("Service.rooms", "room") // .leftJoin("room.serviceclass", "roomClass") // .innerJoin("Service.servicefare", "serviceFare") // .innerJoin("serviceFare.serviceClass", "serviceClass", "serviceClass.id = roomClass.id") // .innerJoin("serviceFare.faretype", "fareType") // .andWhere("room.id = :roomId", { roomId }); const res_count = base_query.clone(); const res_list = base_query .orderBy("Service." + param.order_field, param.order_direction) .skip(offset) .take(limit); if (param.with_deleted) { res_count.withDeleted(); res_list.withDeleted(); } const current_page = param.page; const total_count_data = await res_count.getCount(); const list_data = await res_list.getMany(); const count_data = CommonHelper.countObject(list_data); return ReturnHelper.successResponselist(res, 200, Language.lang.success_view, count_data, current_page, total_count_data, list_data); } catch (e: unknown) { log.error(e); const err = e as Error; return ReturnHelper.errorResponse( res, 400, 401, Language.lang.failed_view, err.message ); } } static async detail(req: Request, res: Response, next: NextFunction): Promise { /* #swagger.tags = ['Room Services'] #swagger.security = [{ "bearerAuth": [] }] #swagger.parameters['id'] = { description: 'Room ID', in: 'path', type: 'string' } */ try { const schema = Joi.object().keys({ id: Joi.string().uuid().required().label("Room ID"), }); let param: any = await schema.validateAsync(req.params); let room = await RoomModel.list({ "Room.id": param.id }) .then((q) => q.leftJoinAndSelect("Room.services", "s") .leftJoinAndSelect("s.serviceType", "serviceType") .leftJoinAndSelect("Room.department", "department").getOne()); if (!room) throw { message: "Room " + Language.lang.failed_not_found }; const detail_data = await RoomServicesModel.formatRoomResponse(room); return ReturnHelper.successResponseAny(res, 200, Language.lang.success_view, detail_data); } catch (e: unknown) { log.error(e); const err = e as Error; return ReturnHelper.errorResponse( res, 400, 401, Language.lang.failed_not_found, err.message ); } } static async update(req: Request, res: Response, next: NextFunction): Promise { /* #swagger.tags = ['Room Services'] #swagger.security = [{ "bearerAuth": [] }] #swagger.parameters['id'] = { description: 'Room ID', in: 'path', type: 'string' } #swagger.requestBody = { required: true, content: { "application/json": { schema: { $ref: "#/components/schemas/roomServices" } } } } */ const queryRunner = OrmHelper.DB.createQueryRunner(); await queryRunner.startTransaction(); try { req.body.id = req.params["id"]; const schema = Joi.object().keys({ id: Joi.string().uuid().required().label("Room ID"), service_type_ids: Joi.array().items(Joi.string().uuid()).default([]).label("Service Type IDs"), }); let param: any = await schema.validateAsync(req.body); let room = await RoomModel.list({ "Room.id": param.id }) .then((q) => q.leftJoinAndSelect("Room.services", "s") .leftJoinAndSelect("Room.department", "department").getOne()); if (!room) throw { message: "Room " + Language.lang.failed_not_found }; room = await RoomServicesModel.applyTypesToRoom( queryRunner.manager, room, param.service_type_ids ?? [], req.auth.data.name ); await queryRunner.commitTransaction(); const response_data = await RoomServicesModel.formatRoomResponse( room, queryRunner.manager ); return ReturnHelper.successResponseAny(res, 200, Language.lang.success_update, response_data); } catch (e: unknown) { await queryRunner.rollbackTransaction(); log.error(e); const err = e as Error; return ReturnHelper.errorResponse(res, 400, 401, Language.lang.failed_view, err.message); } finally { await queryRunner.release(); } } // Controller - List Ancillary Services (Service-sided) static async listAncillaryServices(req: Request, res: Response, next: NextFunction): Promise { /* #swagger.tags = ['Room Services'] #swagger.security = [{ "bearerAuth": [] }] #swagger.parameters['filter'] = { description: '', in: 'query', type: 'string' } #swagger.parameters['limit'] = { in: 'query', required: true, type: 'number' } #swagger.parameters['page'] = { in: 'query', required: true, type: 'number' } #swagger.parameters['with_deleted'] = { in: 'query', required: true, type: 'boolean' } #swagger.parameters['order_field'] = { in: 'query', required: true, type: 'string' } #swagger.parameters['order_direction'] = { in: 'query', required: true, schema: { '@enum': ['ASC', 'DESC'] } } */ try { const schema = Joi.object().keys({ filter: Joi.string().allow("").optional().label("Filter"), page: Joi.number().required().min(1).label("Page"), limit: Joi.number().required().min(1).label("Limit"), with_deleted: Joi.bool().required().label("With Deleted"), order_field: Joi.string().required().label("Order Field"), order_direction: Joi.string().allow("asc", "desc").required().label("Order Direction"), }); let param: Paging = await schema.validateAsync(req.query); let filter = param.filter && param.filter !== "" ? JSON.parse(param.filter) : {}; var query = await ServiceModel.list(filter); const offset = (param.page - 1) * param.limit; let limit = param.limit; const base_query = query .select([ "Service.id", "Service.name", "Service.created_at", "Service.created_by", "serviceType.id", "serviceType.name", "serviceFare.id", "serviceFare.fare", "fareType.id", "fareType.name", ]) .leftJoin("Service.serviceType", "serviceType") .leftJoin("Service.rooms", "room") .leftJoin("room.department", "department") .innerJoin("Service.servicefare", "serviceFare") .innerJoin("serviceFare.serviceClass", "serviceClass") .innerJoin("serviceFare.faretype", "fareType") .andWhere("department.code = :code", { code: "08" }); const res_count = base_query.clone(); const res_list = base_query .orderBy("Service." + param.order_field, param.order_direction) .skip(offset) .take(limit); if (param.with_deleted) { res_count.withDeleted(); res_list.withDeleted(); } const current_page = param.page; const total_count_data = await res_count.getCount(); const list_data = await res_list.getMany(); const count_data = CommonHelper.countObject(list_data); return ReturnHelper.successResponselist(res, 200, Language.lang.success_view, count_data, current_page, total_count_data, list_data); } catch (e: unknown) { log.error(e); const err = e as Error; return ReturnHelper.errorResponse( res, 400, 401, Language.lang.failed_view, err.message ); } } }