From 489d9d5780ae096ff72aa206ac38a579a777891d Mon Sep 17 00:00:00 2001 From: avicenan Date: Fri, 27 Mar 2026 16:31:09 +0700 Subject: [PATCH] feat(payment-method): add feat(card-type): add --- src/controllers/card_type.ts | 369 ++++++++++++ src/controllers/payment-method.ts | 369 ++++++++++++ src/helpers/orm.ts | 8 +- src/model/card_type.ts | 37 ++ src/model/payment_method.ts | 37 ++ src/routes/private.ts | 16 + src/swagger/builder.js | 909 +++++++++++++++--------------- 7 files changed, 1289 insertions(+), 456 deletions(-) create mode 100644 src/controllers/card_type.ts create mode 100644 src/controllers/payment-method.ts create mode 100644 src/model/card_type.ts create mode 100644 src/model/payment_method.ts diff --git a/src/controllers/card_type.ts b/src/controllers/card_type.ts new file mode 100644 index 0000000..3a2f0ef --- /dev/null +++ b/src/controllers/card_type.ts @@ -0,0 +1,369 @@ +import { Response, NextFunction, Application } from "express"; +import { Request } from "express-jwt"; +import { ReturnHelper } from "../helpers/express/return"; +import { OrmHelper } from "../helpers/orm"; +import Joi from "joi"; +import { ILogObj, Logger } from "tslog"; +import { Language } from "../langs/lang"; +import { Paging, CardType } from "entity"; +import CommonHelper from "../helpers/common"; +import * as fastcsv from 'fast-csv'; +import dayjs from "dayjs"; +import { CardTypeModel } from "../model/card_type"; + +const log: Logger = new Logger({ name: '[CardTypeController]', type: 'pretty' }); + +export class CardTypeController { + static async list(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['CardType'] + #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) : {}; + let filterAny = filter.any && filter.any !== "" ? filter.any : ""; + + let filterObj = { ...filter } as any; + delete filterObj.any; + filterObj = { ...filterObj }; + + var query = await CardTypeModel.list(filterObj, filterAny); + + const offset = (param.page - 1) * param.limit; + let limit = param.limit; + + const res_count = query; + const res_list = query + .orderBy("card_type." + param.order_field, param.order_direction) + .offset(offset) + .limit(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 export(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['CardType'] + #swagger.security = [{ + "bearerAuth": [] + }] + #swagger.parameters['filter'] = { + description: 'Filter with 2 format :
  • Simple format use text plaint will filter eq:code or like %name%
  • Advance format using field existing {status:\'Y\', any:\'SAME AS PLAINT LOGIC\', etc...}
', + in: 'query', + type: 'string' + } + #swagger.parameters['filter'] = { + in: 'query', + type: 'string' + } + #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'), + order_field: Joi.string().required().label('Order Field'), + order_direction: Joi.string().allow('asc', 'desc').required().label('Order Direction'), + }); + + const param: Paging = await schema.validateAsync(req.query); + + let filter = param.filter && param.filter !== "" ? JSON.parse(param.filter) : {}; + + var query = await CardTypeModel.list(filter); + const data = await query.getMany(); + + const filename = "card_type.csv"; + + res.setHeader('Content-Type', 'text/csv'); + res.setHeader('Content-Disposition', 'attachment; filename=' + filename); + + const csvStream = fastcsv.format({ + headers: true, + writeHeaders: true, + transform: (row: CardType): any => ({ + ...row, + created_at: dayjs(row.created_at).format('DD-MM-YYYY HH:MM:ss'), + }) + }); + csvStream.pipe(res); + + const limit = 50; + + const fetchAndWrite = async (page: any) => { + + if (CommonHelper.countObject(data) === 0) { + csvStream.end(); + } else { + data.forEach((item: any) => csvStream.write(item)); + + if (CommonHelper.countObject(data) == limit) { + fetchAndWrite(page + 1); + } else { + csvStream.end(); + } + } + }; + + fetchAndWrite(1); + } 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 create(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['CardType'] + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.requestBody = { + required: true, + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/card_type" + } + } + } + } + */ + try { + const schema = Joi.object().keys({ + name: Joi.string().max(256).required().label('Name'), + }); + + const param: any = await schema.validateAsync(req.body); + + const data = new CardType() + data.name = param.name; + data.created_by = req.auth.data.name; + data.updated_by = req.auth.data.name; + + await OrmHelper.DB.manager.save(data); + + return ReturnHelper.successResponseAny(res, 200, Language.lang.success_insert, data); + } catch (e: unknown) { + log.error(e); + const err = e as Error; + + return ReturnHelper.errorResponse(res, 500, 401, Language.lang.failed_insert, err.message); + } + } + + static async update(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['CardType'] + #swagger.security = [{ + "bearerAuth": [] + }] + #swagger.parameters['id'] = { + in: 'path', + description: 'CardType ID.', + required: true, + type: 'string' + } + + #swagger.requestBody = { + required: true, + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/card_type" + } + } + } + } + */ + + try { + const schema = Joi.object().keys({ + id: Joi.string().uuid().required().label('ID'), + name: Joi.string().max(256).required().label('Name'), + }); + + req.body.id = req.params['id']; + + const param: any = await schema.validateAsync(req.body); + + const repo = OrmHelper.DB.getRepository(CardType); + + const data = await repo.findOneBy({ id: param.id }); + + if (data != null) { + data.name = param.name; + data.updated_by = req.auth.data.name; + + await repo.save(data); + + return ReturnHelper.successResponseAny(res, 200, Language.lang.success_update, data); + } else { + return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_not_found, ""); + } + } catch (e: unknown) { + log.error(e); + const err = e as Error; + + return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_update, err.message); + } + } + + static async delete(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['CardType'] + #swagger.security = [{ + "bearerAuth": [] + }] + #swagger.parameters['id'] = { + in: 'path', + description: 'CardType ID.', + required: true, + type: 'string' + } + #swagger.parameters['hard'] = { + in: 'path', + description: 'Is Hard Delete', + required: false, + type: 'boolean' + } + */ + try { + const schema = Joi.object().keys({ + id: Joi.string().uuid().required().label('ID'), + hard: Joi.bool().optional().allow('').label('Is hard delete?') + }); + + const param: { id: string, hard: boolean } = await schema.validateAsync(req.params); + + const repo = OrmHelper.DB.getRepository(CardType); + + const existData = await repo.findOne({ where: { id: param.id } }); + if (existData && !param.hard) { + existData.deleted_by = req.auth.data.name; + await OrmHelper.DB.manager.save(existData); + } + + const affected = (!param.hard ? await repo.softDelete({ id: param.id }) : await repo.delete({ id: param.id })).affected; + + if (affected > 0) { + return ReturnHelper.successResponseAny(res, 200, Language.lang.success_delete, {}); + } else { + return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_not_found, ""); + } + } catch (e: unknown) { + log.error(e); + const err = e as Error; + + return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_delete, err.message); + } + } + + static async restore(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['CardType'] + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['id'] = { + in: 'path', + description: 'CardType ID.', + required: true, + type: 'string' + } + */ + try { + const schema = Joi.object().keys({ + id: Joi.string().uuid().required().label('ID') + }); + + const param: any = await schema.validateAsync(req.params); + + const repo = OrmHelper.DB.getRepository(CardType); + + const affected = (await repo.restore({ id: param.id })).affected; + + if (affected > 0) { + return ReturnHelper.successResponseAny(res, 200, Language.lang.success_restore, {}); + } else { + return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_not_found, ""); + } + } catch (e: unknown) { + log.error(e); + const err = e as Error; + + return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_restore, err.message); + } + } + + static async detail(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['CardType'] + #swagger.security = [{ "bearerAuth": [] }] + #swagger.parameters['id'] = { description: '', in: 'path', type: 'string' } + */ + try { + const schema = Joi.object().keys({ + id: Joi.string().uuid().required().label("CardType Id"), + }); + + let param: any = await schema.validateAsync(req.params); + + let data = await CardTypeModel.list({ "id": param.id }).then((q) => q.getOne()); + if (!data) throw { message: "CardType Not Found" }; + + return ReturnHelper.successResponseAny(res, 200, Language.lang.success_view, 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); + } + } +} \ No newline at end of file diff --git a/src/controllers/payment-method.ts b/src/controllers/payment-method.ts new file mode 100644 index 0000000..5d38e54 --- /dev/null +++ b/src/controllers/payment-method.ts @@ -0,0 +1,369 @@ +import { Response, NextFunction, Application } from "express"; +import { Request } from "express-jwt"; +import { ReturnHelper } from "../helpers/express/return"; +import { OrmHelper } from "../helpers/orm"; +import Joi from "joi"; +import { ILogObj, Logger } from "tslog"; +import { Language } from "../langs/lang"; +import { Paging, PaymentMethod } from "entity"; +import CommonHelper from "../helpers/common"; +import * as fastcsv from 'fast-csv'; +import dayjs from "dayjs"; +import { PaymentMethodModel } from "../model/payment_method"; + +const log: Logger = new Logger({ name: '[PaymentMethodController]', type: 'pretty' }); + +export class PaymentMethodController { + static async list(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['PaymentMethod'] + #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) : {}; + let filterAny = filter.any && filter.any !== "" ? filter.any : ""; + + let filterObj = { ...filter } as any; + delete filterObj.any; + filterObj = { ...filterObj }; + + var query = await PaymentMethodModel.list(filterObj, filterAny); + + const offset = (param.page - 1) * param.limit; + let limit = param.limit; + + const res_count = query; + const res_list = query + .orderBy("payment_method." + param.order_field, param.order_direction) + .offset(offset) + .limit(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 export(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['PaymentMethod'] + #swagger.security = [{ + "bearerAuth": [] + }] + #swagger.parameters['filter'] = { + description: 'Filter with 2 format :
  • Simple format use text plaint will filter eq:code or like %name%
  • Advance format using field existing {status:\'Y\', any:\'SAME AS PLAINT LOGIC\', etc...}
', + in: 'query', + type: 'string' + } + #swagger.parameters['filter'] = { + in: 'query', + type: 'string' + } + #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'), + order_field: Joi.string().required().label('Order Field'), + order_direction: Joi.string().allow('asc', 'desc').required().label('Order Direction'), + }); + + const param: Paging = await schema.validateAsync(req.query); + + let filter = param.filter && param.filter !== "" ? JSON.parse(param.filter) : {}; + + var query = await PaymentMethodModel.list(filter); + const data = await query.getMany(); + + const filename = "payment_method.csv"; + + res.setHeader('Content-Type', 'text/csv'); + res.setHeader('Content-Disposition', 'attachment; filename=' + filename); + + const csvStream = fastcsv.format({ + headers: true, + writeHeaders: true, + transform: (row: PaymentMethod): any => ({ + ...row, + created_at: dayjs(row.created_at).format('DD-MM-YYYY HH:MM:ss'), + }) + }); + csvStream.pipe(res); + + const limit = 50; + + const fetchAndWrite = async (page: any) => { + + if (CommonHelper.countObject(data) === 0) { + csvStream.end(); + } else { + data.forEach((item: any) => csvStream.write(item)); + + if (CommonHelper.countObject(data) == limit) { + fetchAndWrite(page + 1); + } else { + csvStream.end(); + } + } + }; + + fetchAndWrite(1); + } 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 create(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['PaymentMethod'] + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.requestBody = { + required: true, + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/payment_method" + } + } + } + } + */ + try { + const schema = Joi.object().keys({ + name: Joi.string().max(256).required().label('Name'), + }); + + const param: any = await schema.validateAsync(req.body); + + const data = new PaymentMethod() + data.name = param.name; + data.created_by = req.auth.data.name; + data.updated_by = req.auth.data.name; + + await OrmHelper.DB.manager.save(data); + + return ReturnHelper.successResponseAny(res, 200, Language.lang.success_insert, data); + } catch (e: unknown) { + log.error(e); + const err = e as Error; + + return ReturnHelper.errorResponse(res, 500, 401, Language.lang.failed_insert, err.message); + } + } + + static async update(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['PaymentMethod'] + #swagger.security = [{ + "bearerAuth": [] + }] + #swagger.parameters['id'] = { + in: 'path', + description: 'PaymentMethod ID.', + required: true, + type: 'string' + } + + #swagger.requestBody = { + required: true, + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/payment_method" + } + } + } + } + */ + + try { + const schema = Joi.object().keys({ + id: Joi.string().uuid().required().label('ID'), + name: Joi.string().max(256).required().label('Name'), + }); + + req.body.id = req.params['id']; + + const param: any = await schema.validateAsync(req.body); + + const repo = OrmHelper.DB.getRepository(PaymentMethod); + + const data = await repo.findOneBy({ id: param.id }); + + if (data != null) { + data.name = param.name; + data.updated_by = req.auth.data.name; + + await repo.save(data); + + return ReturnHelper.successResponseAny(res, 200, Language.lang.success_update, data); + } else { + return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_not_found, ""); + } + } catch (e: unknown) { + log.error(e); + const err = e as Error; + + return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_update, err.message); + } + } + + static async delete(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['PaymentMethod'] + #swagger.security = [{ + "bearerAuth": [] + }] + #swagger.parameters['id'] = { + in: 'path', + description: 'PaymentMethod ID.', + required: true, + type: 'string' + } + #swagger.parameters['hard'] = { + in: 'path', + description: 'Is Hard Delete', + required: false, + type: 'boolean' + } + */ + try { + const schema = Joi.object().keys({ + id: Joi.string().uuid().required().label('ID'), + hard: Joi.bool().optional().allow('').label('Is hard delete?') + }); + + const param: { id: string, hard: boolean } = await schema.validateAsync(req.params); + + const repo = OrmHelper.DB.getRepository(PaymentMethod); + + const existData = await repo.findOne({ where: { id: param.id } }); + if (existData && !param.hard) { + existData.deleted_by = req.auth.data.name; + await OrmHelper.DB.manager.save(existData); + } + + const affected = (!param.hard ? await repo.softDelete({ id: param.id }) : await repo.delete({ id: param.id })).affected; + + if (affected > 0) { + return ReturnHelper.successResponseAny(res, 200, Language.lang.success_delete, {}); + } else { + return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_not_found, ""); + } + } catch (e: unknown) { + log.error(e); + const err = e as Error; + + return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_delete, err.message); + } + } + + static async restore(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['PaymentMethod'] + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['id'] = { + in: 'path', + description: 'PaymentMethod ID.', + required: true, + type: 'string' + } + */ + try { + const schema = Joi.object().keys({ + id: Joi.string().uuid().required().label('ID') + }); + + const param: any = await schema.validateAsync(req.params); + + const repo = OrmHelper.DB.getRepository(PaymentMethod); + + const affected = (await repo.restore({ id: param.id })).affected; + + if (affected > 0) { + return ReturnHelper.successResponseAny(res, 200, Language.lang.success_restore, {}); + } else { + return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_not_found, ""); + } + } catch (e: unknown) { + log.error(e); + const err = e as Error; + + return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_restore, err.message); + } + } + + static async detail(req: Request, res: Response, next: NextFunction): Promise { + /* + #swagger.tags = ['PaymentMethod'] + #swagger.security = [{ "bearerAuth": [] }] + #swagger.parameters['id'] = { description: '', in: 'path', type: 'string' } + */ + try { + const schema = Joi.object().keys({ + id: Joi.string().uuid().required().label("PaymentMethod Id"), + }); + + let param: any = await schema.validateAsync(req.params); + + let data = await PaymentMethodModel.list({ "id": param.id }).then((q) => q.getOne()); + if (!data) throw { message: "PaymentMethod Not Found" }; + + return ReturnHelper.successResponseAny(res, 200, Language.lang.success_view, 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); + } + } +} \ No newline at end of file diff --git a/src/helpers/orm.ts b/src/helpers/orm.ts index da6289a..1e005ec 100644 --- a/src/helpers/orm.ts +++ b/src/helpers/orm.ts @@ -1,7 +1,7 @@ import config from 'config'; import { DataSource } from "typeorm"; import { ILogObj, Logger } from 'tslog'; -import { FareType,RoomStock,Administrativu, Aldeia, District, Menu, Munisipo, Suco, Application, Nationality, Diagnosis, DiagnosticProcedure, Room, ServiceType, ServiceClass, Service, ServiceFare, ServicePackage, Department, DoctorSchedule, MeasurementUnit, ReferenceRange, QueueMonitoring, ExaminationDetail, ExaminationType, User, UserRole, HrmsEmployee, HrmsDepartment, HrmsPosition, HrmsShift, HospitalInformation, ItemOrigin, ItemType, ItemTypeDetail, Unit, ItemCategory, ItemStatus, UsageInstructions, UsageTime, ItemClass, ItemClassDetail, GenericName, Factory, Supplier, FactoryToSupplier, ItemMaster, ItemGroup, PharmacyInfo, ItemPrice, SellingPricePercentage, PatientGroup, PatientGuarantor, InitialStock, DoctorItem, DoctorItemPackage, UserToRoom, RoomToPharmacy, PlanningVerificator, ScheduleSeries, ScheduleException } from 'entity' +import { FareType, RoomStock, Administrativu, Aldeia, District, Menu, Munisipo, Suco, Application, Nationality, Diagnosis, DiagnosticProcedure, Room, ServiceType, ServiceClass, Service, ServiceFare, ServicePackage, Department, DoctorSchedule, MeasurementUnit, ReferenceRange, QueueMonitoring, ExaminationDetail, ExaminationType, User, UserRole, HrmsEmployee, HrmsDepartment, HrmsPosition, HrmsShift, HospitalInformation, ItemOrigin, ItemType, ItemTypeDetail, Unit, ItemCategory, ItemStatus, UsageInstructions, UsageTime, ItemClass, ItemClassDetail, GenericName, Factory, Supplier, FactoryToSupplier, ItemMaster, ItemGroup, PharmacyInfo, ItemPrice, SellingPricePercentage, PatientGroup, PatientGuarantor, InitialStock, DoctorItem, DoctorItemPackage, UserToRoom, RoomToPharmacy, PlanningVerificator, ScheduleSeries, ScheduleException, CardType, PaymentMethod } from 'entity' export class OrmHelper { static DB: DataSource = null @@ -48,9 +48,11 @@ export class OrmHelper { RoomToPharmacy, PlanningVerificator, RoomStock, - ScheduleSeries, + ScheduleSeries, ScheduleException, - FareType + FareType, + CardType, + PaymentMethod ], subscribers: [], migrations: [], diff --git a/src/model/card_type.ts b/src/model/card_type.ts new file mode 100644 index 0000000..3cff273 --- /dev/null +++ b/src/model/card_type.ts @@ -0,0 +1,37 @@ +import { SelectQueryBuilder } from "typeorm"; +import CommonHelper from "../helpers/common"; +import { OrmHelper } from "../helpers/orm"; +import { CardType } from "entity"; + +export class CardTypeModel { + static async list(filterObj = {}, filterAny: string = ""): Promise> { + const repo = OrmHelper.DB.getRepository(CardType); + let whereAttr = []; + let whereVal: any = {}; + if (filterObj) { + const filterResult = CommonHelper.handleQueryFilter(filterObj); + whereAttr = [...whereAttr, ...filterResult.whereAttr]; + whereVal = { ...whereVal, ...filterResult.whereVal }; + } + + if (filterAny && filterAny !== "" && filterAny !== null && filterAny !== undefined) { + const filterResult = CommonHelper.handleFilter({ + filter: JSON.stringify({ any: filterAny }), + col_any_eq: [], + col_any_like: ["name"], + }); + + if (filterResult.whereAttr && filterResult.whereAttr !== "") { + whereAttr = [...whereAttr, filterResult.whereAttr]; + whereVal = { ...whereVal, ...filterResult.whereVal }; + } + } + + var query = repo.createQueryBuilder("card_type"); + if (whereAttr.length != 0) { + query = query.where(whereAttr.join(" and "), whereVal); + } + + return query; + } +} \ No newline at end of file diff --git a/src/model/payment_method.ts b/src/model/payment_method.ts new file mode 100644 index 0000000..2e6840e --- /dev/null +++ b/src/model/payment_method.ts @@ -0,0 +1,37 @@ +import { SelectQueryBuilder } from "typeorm"; +import CommonHelper from "../helpers/common"; +import { OrmHelper } from "../helpers/orm"; +import { PaymentMethod } from "entity"; + +export class PaymentMethodModel { + static async list(filterObj = {}, filterAny: string = ""): Promise> { + const repo = OrmHelper.DB.getRepository(PaymentMethod); + let whereAttr = []; + let whereVal: any = {}; + if (filterObj) { + const filterResult = CommonHelper.handleQueryFilter(filterObj); + whereAttr = [...whereAttr, ...filterResult.whereAttr]; + whereVal = { ...whereVal, ...filterResult.whereVal }; + } + + if (filterAny && filterAny !== "" && filterAny !== null && filterAny !== undefined) { + const filterResult = CommonHelper.handleFilter({ + filter: JSON.stringify({ any: filterAny }), + col_any_eq: [], + col_any_like: ["name"], + }); + + if (filterResult.whereAttr && filterResult.whereAttr !== "") { + whereAttr = [...whereAttr, filterResult.whereAttr]; + whereVal = { ...whereVal, ...filterResult.whereVal }; + } + } + + var query = repo.createQueryBuilder("payment_method"); + if (whereAttr.length != 0) { + query = query.where(whereAttr.join(" and "), whereVal); + } + + return query; + } +} \ No newline at end of file diff --git a/src/routes/private.ts b/src/routes/private.ts index 614656e..500b2cf 100644 --- a/src/routes/private.ts +++ b/src/routes/private.ts @@ -60,6 +60,8 @@ import { RoomToPharmacyController } from '../controllers/room_to_pharmacy'; import { PlanningVerificatorController } from '../controllers/pharmacy/planning_verificator'; import { ScheduleController } from '../controllers/schedule'; import { FareTypeController } from '../controllers/faretype'; +import { CardTypeController } from '../controllers/card_type'; +import { PaymentMethodController } from '../controllers/payment-method'; export class RoutePrivate { static setup(app: express.Application) { @@ -457,5 +459,19 @@ export class RoutePrivate { app.put('/api/fare-type/update/:id', FareTypeController.update) app.delete('/api/fare-type/delete/:id/:hard', FareTypeController.delete) app.put('/api/fare-type/restore/:id', FareTypeController.restore) + + app.get('/api/card-type/list', CardTypeController.list) + app.post('/api/card-type/create', CardTypeController.create) + app.get('/api/card-type/detail/:id', CardTypeController.detail) + app.put('/api/card-type/update/:id', CardTypeController.update) + app.delete('/api/card-type/delete/:id/:hard', CardTypeController.delete) + app.put('/api/card-type/restore/:id', CardTypeController.restore) + + app.get('/api/payment-method/list', PaymentMethodController.list) + app.post('/api/payment-method/create', PaymentMethodController.create) + app.get('/api/payment-method/detail/:id', PaymentMethodController.detail) + app.put('/api/payment-method/update/:id', PaymentMethodController.update) + app.delete('/api/payment-method/delete/:id/:hard', PaymentMethodController.delete) + app.put('/api/payment-method/restore/:id', PaymentMethodController.restore) } } diff --git a/src/swagger/builder.js b/src/swagger/builder.js index bec67f3..f522295 100644 --- a/src/swagger/builder.js +++ b/src/swagger/builder.js @@ -1,460 +1,463 @@ -const swaggerAutogen = require('swagger-autogen')({ openapi: '3.0.0' }); -const config = require('config'); +const swaggerAutogen = require("swagger-autogen")({ openapi: "3.0.0" }); +const config = require("config"); const doc = { - info: { - title: config.get('app.name'), - description: config.get('app.description'), - version: config.get('app.version') + info: { + title: config.get("app.name"), + description: config.get("app.description"), + version: config.get("app.version"), + }, + servers: [ + { + url: config.get("server.host_swagger"), + description: "Environtment " + config.get("app.env"), }, - servers: [ - { - url: config.get('server.host_swagger'), - description: 'Environtment ' + config.get('app.env') - }, - ], - components: { - schemas: { - product: { - $code: "123456", - $name: "Product name", - $description: "Product description", - $source: "trialBalanceBranch / cifAccount / lnkolek", - $status: "Y", - }, - branch: { - $code: "123456", - $name: "Branch name", - $status: "Y", - }, - menu: { - $module: "Dashboard", - $name: "Dashboard", - $link: "/", - $id_parent: "", - $order_number: 1, - $icon: "", - $application: "ukln", - $status: "Y" - }, - administrativu: { - $code: '123456', - $name: 'Administrativu name', - $status: "Y", - $munisipo_id: "uuid-string" - }, - application: { - $id: 'ukln', - $name: 'Dashboard Performance Business', - $reset_password_url: 'https://brilianapps.britimorleste.tl/ukln/auth/reset-password/', - $status: "Y" - }, - aldeia: { - $code: '123456', - $name: 'Aldeia name', - $status: "Y", - $suco_id: "uuid-string" - }, - class_economi: { - $code: '123456', - $name: 'Class economi name', - $status: "Y" - }, - district: { - $code: '123456', - $name: 'District name', - $status: "Y" - }, - income_tier: { - $code: '123456', - $name: 'Income tier name', - $status: "Y" - }, - munisipo: { - $code: '123456', - $name: 'Munisipo name', - $status: "Y" - }, - suco: { - $code: '123456', - $name: 'Suco name', - $status: "Y", - $administrativu_id: "uuid-string" - }, - nationality: { - $code: '123456', - $name: 'Nationality name', - $status: "Y" - }, - classEconomi: { - $code: '123456', - $name: 'Class Economi name', - $status: "Y" - }, - incomeTier: { - $code: '123456', - $name: 'Income Tier name', - $status: "Y" - }, - diagnosis: { - $diagnosis: "Diagnosis name (ICD-11)", - $code: "ABC123", - $status: "Y" - }, - diagnostic_procedure: { - $diagnosticProcedure: "Procedure name (ICD-9)", - $code: "XYZ789", - $status: "Y" - }, - room: { - $room: "Room name", - $code: "R001", - $description: "Optional description", - $department_id: "uuid-string", - $status: "Y", - $location: "Room location", - $picture: "Room picture name" - }, - laboratory: { - $room: "Room name", - $code: "R001", - $description: "Optional description", - $department_id: "uuid-string", - $status: "Y" - }, - // pharmacy: { - // $room: "Room name", - // $code: "R001", - // $description: "Optional description", - // $department_id: "uuid-string", - // $status: "Y" - // }, - serviceType: { - $name: "Registration & Tickets", - $status: { "@enum": ["Y", "N"] } - }, - serviceClass: { - $name: "Class name" - }, - service: { - $name: "e.g. ER Registration", - $service_type_id: "uuid-string", - $status: { "@enum": ["Y", "N"] } - }, - roomServices: { - $service_ids: ["uuid-string"] - }, - serviceFare: { - $service_class_id: "uuid-string", - $service_id: "uuid-string", - $faretype: "uuid-string", - $fare: 100000 - }, - servicePackage: { - $name: "Package name", - $service_class_id: "uuid-string", - $fare: 500000, - $service_ids: ["uuid-string"] - }, - department: { - $name: "igd", - $status: { "@enum": ["Y", "N"] } - }, - doctorSchedule: { - $day: "Senin", - $start_time: "08:00", - $end_time: "12:00", - $doctor_id: "uuid-string", - $room_id: "uuid-string", - $quota: "20", - $status: { "@enum": ["Y", "N"] } - }, - doctorItem: { - $doctor_id: "uuid-string", - $item_master_id: "uuid-string" - }, - doctorItemPackage: { - $doctor_id: "uuid-string", - $name: "Paket Obat Harian", - $description: "Optional description", - $item_master_ids: ["uuid-string"], - $active: true - }, - measurementUnit: { - $unit: "Milligram", - $status: { "@enum": ["Y", "N"] } - }, - referenceRange: { - $gender: { "@enum": ["male", "female"] }, - $age_range_min: "0 | (days)", - $age_range_max: "20 | (days)", - $value_min: "13.7", - $value_max: "17.5", - $status: { "@enum": ["Y", "N"] } - }, - queueMonitoring: { - $name: "Ground Floor Queue", - $slug: "ground-floor-queue", - $room_ids: ["uuid-string"], - $status: { "@enum": ["Y", "N"] } - }, - examinationType: { - $name: "Examination Type name", - $status: { "@enum": ["Y", "N"] }, - $service_ids: ["uuid-string"] - }, - examinationDetail: { - $name: "Examination Detail name", - $order_no: "Order number", - $measurement_unit_id: "uuid-string", - $reference_range_id: "uuid-string", - $status: { "@enum": ["Y", "N"] } - }, - serviceExamination: { - $examination_detail_ids: ["uuid-string"] - }, - hospitalinformation: { - $name: "Hospital Name", - $address: "Hospital Address", - $phone: "Hospital Phone", - $logo: "Hospital Logo", - }, - files: { - $file_name: "File Name", - }, - item_group: { - $group_name: 'ItemGroup name', - }, - item_origin: { - $origin_name: 'ItemOrigin name', - $origin_description: 'ItemOrigin Desc', - $department: 'Department name', - $account: 'Account name', - }, - item_type: { - $type_name: 'ItemType name', - $item_group_id: 'ItemGroup Id' - }, - item_type_detail: { - $type_detail_name: 'ItemTypeDetail name', - $item_type_id: 'ItemType Id' - }, - unit: { - $unit_name: 'Unit name', - $small_unit: 'Small unit name', - $large_unit: 'Large unit name' - }, - item_category: { - $category_name: 'ItemCategory name', - }, - item_status: { - $status_name: 'Itemstatus name', - }, - item_class: { - $item_class_name: 'ItemClass name', - }, - item_class_detail: { - $class_detail_name: 'ItemClassDetail name', - }, - usage_instructions: { - $usage_instructions_name: 'UsageInstructions name', - $usage_instructions_abbreviation: 'UsageInstructions Abbreviation' - }, - usage_time: { - $usage_time_name: 'UsageInstructions name', - $usage_time_abbreviation: 'UsageInstructions Abbreviation' - }, - generic_name: { - $generic_name: 'Generic name', - }, - factory: { - $factory_name: 'factory name', - $address: 'Factory address', - $web: 'Factory url web', - $phone: 'Factory phone', - $email: 'Factory email' - }, - supplier: { - $supplier_name: 'supplier name', - $address: 'supplier address', - $phone: 'supplier phone', - $email: 'supplier email' - }, - factory_to_supplier: { - $supplier_id: 'Supplier ID (required, UUID)', - $factory_ids: 'Array [factory-uuid-1, factory-uuid-2]' - }, - item_master: { - $item_name: 'Item name', - $generic_name_id: 'Generic name ID (UUID)', - $item_type_detail_id: 'Item type detail ID (UUID)', - $item_category_id: 'Item category ID (UUID)', - $item_class_id: 'Item class ID (UUID)', - $item_class_detail_id: 'Item class Detail ID (UUID)', - $item_status_id: 'Item status ID (UUID)', - $factory_id: 'Factory ID (UUID)', - $unit_id: 'Unit ID (UUID)', - $pack_size: 'Pack size (integer, minimum 0)', - $minimum_quantity: 'Minimum quantity (integer, minimum 0)', - $minimum_sales_quantity: 'Minimum sales quantity (integer, minimum 0)', - $strength: 'Strength value (number, minimum 0)', - $active: 'Active status (boolean)' - }, - pharmacy_info: { - $logo: 'Logo (string, max 256)', - $name: 'Name (string, max 256)', - $address: 'Address (string, max 500)', - $munisipiu: 'Munisipiu ID (UUID)', - $postu_admin: 'Postu Admin ID (UUID)', - $suco: 'Suco ID (UUID)', - $aldeia: 'Aldeia ID (UUID)', - $phone: 'Phone (string, max 30, optional)', - $default_room_id: 'Default Room ID (UUID)' - }, - item_price: { - $item_master_id: 'Item Master ID (required, UUID)', - $purchase_price: 'Purchase Price (required, number, minimum 0)', - $selling_price: 'Selling Price (required, number, minimum 0)', - $expired_date: 'Expired Date (required, date)' - }, - initial_stock: { - $itemmaster: 'Item Master uuid', - $room: 'Room uuid', - $itemorigin: 'Item Origin uuid', - $batch: 'Batch', - $exp_date: 'Exp Date', - $stock: 'Stock' - }, - selling_price_percentage: { - $item_master_id: 'Item Master ID (required, UUID)', - $item_origin_id: 'Item Origin ID (required, UUID)', - $percentage: 'Percentage (required, number, min 0, max 999.99, 2 decimal places)' - }, - patient_group: { - $patient_group_name: 'PatientGroup name', - }, - patient_guarantor: { - $guarantor_name: 'PatientGuarantor name', - $phone: '08123456789', // Nomor telepon - $province: 'West Java', // Provinsi - $city: 'Bandung', // Kota - $zip_code: '40123', // Kode Pos - $agreement_no: 'AG123456789', // Nomor Perjanjian - $agreement_desc: 'Health Insurance Agreement', // Deskripsi Perjanjian (opsional) - $patient_group_id: '2e1a1b33-bbb2-4f33-b2c4-cff5976f3f95', // Patient Group (UUID) - }, - refferal_hospital: { - $refferal_hospital_name: 'RefferalHospital name', - }, - user_to_room: { - $user_id: 'user ID (required, UUID)', - $room_ids: 'Array [room-uuid-1, room-uuid-2]' - }, - room_to_pharmacy: { - $pharmacy_room_id: 'Pharmacy Room ID (required, UUID)', - $room_ids: 'Array [room-uuid-1, room-uuid-2]' - }, - planning_verificator: { - $user_id: 'User ID (required, UUID)' - }, - scheduleRecurrence: { - $type: { "@enum": ["daily", "weekly", "monthly"] }, - $interval: 1, - $daysOfWeek: ["MO", "WE", "FR"] - }, - scheduleSeriesCreate: { - $roomId: "uuid-string", - $doctorId: "uuid-string", - $title: "Practice Schedule", - $timezone: "Asia/Jakarta", - $quota: 20, - $startDate: "2026-03-20", - $untilDate: "2026-06-30", - $startTimeLocal: "09:00:00", - $endTimeLocal: "12:00:00", - $recurrence: { $ref: "#/components/schemas/scheduleRecurrence" } - }, - scheduleSeriesUpdate: { - $roomId: "uuid-string", - $doctorId: "uuid-string", - $title: "Practice Schedule Updated", - $timezone: "Asia/Jakarta", - $quota: 25, - $startDate: "2026-03-20", - $untilDate: "2026-07-31", - $startTimeLocal: "10:00:00", - $endTimeLocal: "13:00:00", - $recurrence: { $ref: "#/components/schemas/scheduleRecurrence" } - }, - scheduleSeriesDetail: { - $id: "uuid-string", - $room_id: "uuid-string", - $doctor_id: "uuid-string", - $title: "Practice Schedule", - $timezone: "Asia/Jakarta", - $quota: 20, - $start_date: "2026-03-20", - $until_date: "2026-06-30", - $start_time_local: "09:00:00", - $end_time_local: "12:00:00", - $recurrence: { $ref: "#/components/schemas/scheduleRecurrence" }, - $status: "Active", - $room: { id: "uuid-string", room: "Room 1" }, - $doctor: { id: "uuid-string", name: "Dr A" } - }, - scheduleExceptionCreate: { - $occurrenceDate: "2026-03-24", - $isCancelled: false, - $overrideRoomId: "uuid-string", - $overrideDoctorId: "uuid-string", - $overrideTitle: "Override Practice", - $overrideStartAt: "2026-03-24T10:00:00.000Z", - $overrideEndAt: "2026-03-24T12:30:00.000Z" - }, - scheduleCalendarResource: { - $id: "room-1:doctor-1", - $roomId: "room-1", - $roomName: "Room 1", - $doctorId: "doctor-1", - $doctorName: "Dr A" - }, - scheduleCalendarEvent: { - $id: "occ:series-101:2026-03-20", - $seriesId: "series-101", - $resourceId: "room-1:doctor-1", - $title: "Practice", - $startAt: "2026-03-20T09:00:00+07:00", - $endAt: "2026-03-20T12:00:00+07:00", - $quota: 20, - $usedQuota: 7, - $leftQuota: 13, - $isRecurring: true, - $isException: false, - $status: "confirmed" - }, - scheduleCalendarMeta: { - $total_count: 24, - $page: 1, - $limit: 20 - }, - faretype: { - $name: 'Name (string)', - }, - }, - parameters: { - - }, - securitySchemes: { - bearerAuth: { - type: 'http', - scheme: 'bearer' - } - } - } + ], + components: { + schemas: { + product: { + $code: "123456", + $name: "Product name", + $description: "Product description", + $source: "trialBalanceBranch / cifAccount / lnkolek", + $status: "Y", + }, + branch: { + $code: "123456", + $name: "Branch name", + $status: "Y", + }, + menu: { + $module: "Dashboard", + $name: "Dashboard", + $link: "/", + $id_parent: "", + $order_number: 1, + $icon: "", + $application: "ukln", + $status: "Y", + }, + administrativu: { + $code: "123456", + $name: "Administrativu name", + $status: "Y", + $munisipo_id: "uuid-string", + }, + application: { + $id: "ukln", + $name: "Dashboard Performance Business", + $reset_password_url: "https://brilianapps.britimorleste.tl/ukln/auth/reset-password/", + $status: "Y", + }, + aldeia: { + $code: "123456", + $name: "Aldeia name", + $status: "Y", + $suco_id: "uuid-string", + }, + class_economi: { + $code: "123456", + $name: "Class economi name", + $status: "Y", + }, + district: { + $code: "123456", + $name: "District name", + $status: "Y", + }, + income_tier: { + $code: "123456", + $name: "Income tier name", + $status: "Y", + }, + munisipo: { + $code: "123456", + $name: "Munisipo name", + $status: "Y", + }, + suco: { + $code: "123456", + $name: "Suco name", + $status: "Y", + $administrativu_id: "uuid-string", + }, + nationality: { + $code: "123456", + $name: "Nationality name", + $status: "Y", + }, + classEconomi: { + $code: "123456", + $name: "Class Economi name", + $status: "Y", + }, + incomeTier: { + $code: "123456", + $name: "Income Tier name", + $status: "Y", + }, + diagnosis: { + $diagnosis: "Diagnosis name (ICD-11)", + $code: "ABC123", + $status: "Y", + }, + diagnostic_procedure: { + $diagnosticProcedure: "Procedure name (ICD-9)", + $code: "XYZ789", + $status: "Y", + }, + room: { + $room: "Room name", + $code: "R001", + $description: "Optional description", + $department_id: "uuid-string", + $status: "Y", + $location: "Room location", + $picture: "Room picture name", + }, + laboratory: { + $room: "Room name", + $code: "R001", + $description: "Optional description", + $department_id: "uuid-string", + $status: "Y", + }, + // pharmacy: { + // $room: "Room name", + // $code: "R001", + // $description: "Optional description", + // $department_id: "uuid-string", + // $status: "Y" + // }, + serviceType: { + $name: "Registration & Tickets", + $status: { "@enum": ["Y", "N"] }, + }, + serviceClass: { + $name: "Class name", + }, + service: { + $name: "e.g. ER Registration", + $service_type_id: "uuid-string", + $status: { "@enum": ["Y", "N"] }, + }, + roomServices: { + $service_ids: ["uuid-string"], + }, + serviceFare: { + $service_class_id: "uuid-string", + $service_id: "uuid-string", + $faretype: "uuid-string", + $fare: 100000, + }, + servicePackage: { + $name: "Package name", + $service_class_id: "uuid-string", + $fare: 500000, + $service_ids: ["uuid-string"], + }, + department: { + $name: "igd", + $status: { "@enum": ["Y", "N"] }, + }, + doctorSchedule: { + $day: "Senin", + $start_time: "08:00", + $end_time: "12:00", + $doctor_id: "uuid-string", + $room_id: "uuid-string", + $quota: "20", + $status: { "@enum": ["Y", "N"] }, + }, + doctorItem: { + $doctor_id: "uuid-string", + $item_master_id: "uuid-string", + }, + doctorItemPackage: { + $doctor_id: "uuid-string", + $name: "Paket Obat Harian", + $description: "Optional description", + $item_master_ids: ["uuid-string"], + $active: true, + }, + measurementUnit: { + $unit: "Milligram", + $status: { "@enum": ["Y", "N"] }, + }, + referenceRange: { + $gender: { "@enum": ["male", "female"] }, + $age_range_min: "0 | (days)", + $age_range_max: "20 | (days)", + $value_min: "13.7", + $value_max: "17.5", + $status: { "@enum": ["Y", "N"] }, + }, + queueMonitoring: { + $name: "Ground Floor Queue", + $slug: "ground-floor-queue", + $room_ids: ["uuid-string"], + $status: { "@enum": ["Y", "N"] }, + }, + examinationType: { + $name: "Examination Type name", + $status: { "@enum": ["Y", "N"] }, + $service_ids: ["uuid-string"], + }, + examinationDetail: { + $name: "Examination Detail name", + $order_no: "Order number", + $measurement_unit_id: "uuid-string", + $reference_range_id: "uuid-string", + $status: { "@enum": ["Y", "N"] }, + }, + serviceExamination: { + $examination_detail_ids: ["uuid-string"], + }, + hospitalinformation: { + $name: "Hospital Name", + $address: "Hospital Address", + $phone: "Hospital Phone", + $logo: "Hospital Logo", + }, + files: { + $file_name: "File Name", + }, + item_group: { + $group_name: "ItemGroup name", + }, + item_origin: { + $origin_name: "ItemOrigin name", + $origin_description: "ItemOrigin Desc", + $department: "Department name", + $account: "Account name", + }, + item_type: { + $type_name: "ItemType name", + $item_group_id: "ItemGroup Id", + }, + item_type_detail: { + $type_detail_name: "ItemTypeDetail name", + $item_type_id: "ItemType Id", + }, + unit: { + $unit_name: "Unit name", + $small_unit: "Small unit name", + $large_unit: "Large unit name", + }, + item_category: { + $category_name: "ItemCategory name", + }, + item_status: { + $status_name: "Itemstatus name", + }, + item_class: { + $item_class_name: "ItemClass name", + }, + item_class_detail: { + $class_detail_name: "ItemClassDetail name", + }, + usage_instructions: { + $usage_instructions_name: "UsageInstructions name", + $usage_instructions_abbreviation: "UsageInstructions Abbreviation", + }, + usage_time: { + $usage_time_name: "UsageInstructions name", + $usage_time_abbreviation: "UsageInstructions Abbreviation", + }, + generic_name: { + $generic_name: "Generic name", + }, + factory: { + $factory_name: "factory name", + $address: "Factory address", + $web: "Factory url web", + $phone: "Factory phone", + $email: "Factory email", + }, + supplier: { + $supplier_name: "supplier name", + $address: "supplier address", + $phone: "supplier phone", + $email: "supplier email", + }, + factory_to_supplier: { + $supplier_id: "Supplier ID (required, UUID)", + $factory_ids: "Array [factory-uuid-1, factory-uuid-2]", + }, + item_master: { + $item_name: "Item name", + $generic_name_id: "Generic name ID (UUID)", + $item_type_detail_id: "Item type detail ID (UUID)", + $item_category_id: "Item category ID (UUID)", + $item_class_id: "Item class ID (UUID)", + $item_class_detail_id: "Item class Detail ID (UUID)", + $item_status_id: "Item status ID (UUID)", + $factory_id: "Factory ID (UUID)", + $unit_id: "Unit ID (UUID)", + $pack_size: "Pack size (integer, minimum 0)", + $minimum_quantity: "Minimum quantity (integer, minimum 0)", + $minimum_sales_quantity: "Minimum sales quantity (integer, minimum 0)", + $strength: "Strength value (number, minimum 0)", + $active: "Active status (boolean)", + }, + pharmacy_info: { + $logo: "Logo (string, max 256)", + $name: "Name (string, max 256)", + $address: "Address (string, max 500)", + $munisipiu: "Munisipiu ID (UUID)", + $postu_admin: "Postu Admin ID (UUID)", + $suco: "Suco ID (UUID)", + $aldeia: "Aldeia ID (UUID)", + $phone: "Phone (string, max 30, optional)", + $default_room_id: "Default Room ID (UUID)", + }, + item_price: { + $item_master_id: "Item Master ID (required, UUID)", + $purchase_price: "Purchase Price (required, number, minimum 0)", + $selling_price: "Selling Price (required, number, minimum 0)", + $expired_date: "Expired Date (required, date)", + }, + initial_stock: { + $itemmaster: "Item Master uuid", + $room: "Room uuid", + $itemorigin: "Item Origin uuid", + $batch: "Batch", + $exp_date: "Exp Date", + $stock: "Stock", + }, + selling_price_percentage: { + $item_master_id: "Item Master ID (required, UUID)", + $item_origin_id: "Item Origin ID (required, UUID)", + $percentage: "Percentage (required, number, min 0, max 999.99, 2 decimal places)", + }, + patient_group: { + $patient_group_name: "PatientGroup name", + }, + patient_guarantor: { + $guarantor_name: "PatientGuarantor name", + $phone: "08123456789", // Nomor telepon + $province: "West Java", // Provinsi + $city: "Bandung", // Kota + $zip_code: "40123", // Kode Pos + $agreement_no: "AG123456789", // Nomor Perjanjian + $agreement_desc: "Health Insurance Agreement", // Deskripsi Perjanjian (opsional) + $patient_group_id: "2e1a1b33-bbb2-4f33-b2c4-cff5976f3f95", // Patient Group (UUID) + }, + refferal_hospital: { + $refferal_hospital_name: "RefferalHospital name", + }, + user_to_room: { + $user_id: "user ID (required, UUID)", + $room_ids: "Array [room-uuid-1, room-uuid-2]", + }, + room_to_pharmacy: { + $pharmacy_room_id: "Pharmacy Room ID (required, UUID)", + $room_ids: "Array [room-uuid-1, room-uuid-2]", + }, + planning_verificator: { + $user_id: "User ID (required, UUID)", + }, + scheduleRecurrence: { + $type: { "@enum": ["daily", "weekly", "monthly"] }, + $interval: 1, + $daysOfWeek: ["MO", "WE", "FR"], + }, + scheduleSeriesCreate: { + $roomId: "uuid-string", + $doctorId: "uuid-string", + $title: "Practice Schedule", + $timezone: "Asia/Jakarta", + $quota: 20, + $startDate: "2026-03-20", + $untilDate: "2026-06-30", + $startTimeLocal: "09:00:00", + $endTimeLocal: "12:00:00", + $recurrence: { $ref: "#/components/schemas/scheduleRecurrence" }, + }, + scheduleSeriesUpdate: { + $roomId: "uuid-string", + $doctorId: "uuid-string", + $title: "Practice Schedule Updated", + $timezone: "Asia/Jakarta", + $quota: 25, + $startDate: "2026-03-20", + $untilDate: "2026-07-31", + $startTimeLocal: "10:00:00", + $endTimeLocal: "13:00:00", + $recurrence: { $ref: "#/components/schemas/scheduleRecurrence" }, + }, + scheduleSeriesDetail: { + $id: "uuid-string", + $room_id: "uuid-string", + $doctor_id: "uuid-string", + $title: "Practice Schedule", + $timezone: "Asia/Jakarta", + $quota: 20, + $start_date: "2026-03-20", + $until_date: "2026-06-30", + $start_time_local: "09:00:00", + $end_time_local: "12:00:00", + $recurrence: { $ref: "#/components/schemas/scheduleRecurrence" }, + $status: "Active", + $room: { id: "uuid-string", room: "Room 1" }, + $doctor: { id: "uuid-string", name: "Dr A" }, + }, + scheduleExceptionCreate: { + $occurrenceDate: "2026-03-24", + $isCancelled: false, + $overrideRoomId: "uuid-string", + $overrideDoctorId: "uuid-string", + $overrideTitle: "Override Practice", + $overrideStartAt: "2026-03-24T10:00:00.000Z", + $overrideEndAt: "2026-03-24T12:30:00.000Z", + }, + scheduleCalendarResource: { + $id: "room-1:doctor-1", + $roomId: "room-1", + $roomName: "Room 1", + $doctorId: "doctor-1", + $doctorName: "Dr A", + }, + scheduleCalendarEvent: { + $id: "occ:series-101:2026-03-20", + $seriesId: "series-101", + $resourceId: "room-1:doctor-1", + $title: "Practice", + $startAt: "2026-03-20T09:00:00+07:00", + $endAt: "2026-03-20T12:00:00+07:00", + $quota: 20, + $usedQuota: 7, + $leftQuota: 13, + $isRecurring: true, + $isException: false, + $status: "confirmed", + }, + scheduleCalendarMeta: { + $total_count: 24, + $page: 1, + $limit: 20, + }, + faretype: { + $name: "Name (string)", + }, + card_type: { + $name: "Name (string)", + }, + payment_method: { + $name: "Name (string)", + }, + }, + parameters: {}, + securitySchemes: { + bearerAuth: { + type: "http", + scheme: "bearer", + }, + }, + }, }; -const outputFile = './swagger.json'; -const routes = ['../routes/private.ts', '../routes/public.ts']; +const outputFile = "./swagger.json"; +const routes = ["../routes/private.ts", "../routes/public.ts"]; swaggerAutogen(outputFile, routes, doc); -