diff --git a/src/command/unit/damageReportCommand.ts b/src/command/unit/damageReportCommand.ts new file mode 100644 index 0000000..62a4773 --- /dev/null +++ b/src/command/unit/damageReportCommand.ts @@ -0,0 +1,22 @@ +export interface CreateDamageReportCommand { + description: string; + reportedBy: string; + imageCount: number; + affectedId: string; + affected: "equipment" | "vehicle" | "wearable"; +} + +export interface UpdateDamageReportCommand { + id: string; + status: string; + done: boolean; +} + +export interface UpdateDamageReportRelatedMaintenanceCommand { + id: string; + maintenanceId: string; +} + +export interface DeleteDamageReportCommand { + id: string; +} diff --git a/src/command/unit/damageReportCommandHandler.ts b/src/command/unit/damageReportCommandHandler.ts new file mode 100644 index 0000000..71a72fa --- /dev/null +++ b/src/command/unit/damageReportCommandHandler.ts @@ -0,0 +1,100 @@ +import { dataSource } from "../../data-source"; +import { damageReport } from "../../entity/unit/damageReport"; +import DatabaseActionException from "../../exceptions/databaseActionException"; +import { + CreateDamageReportCommand, + UpdateDamageReportCommand, + DeleteDamageReportCommand, + UpdateDamageReportRelatedMaintenanceCommand, +} from "./damageReportCommand"; + +export default abstract class DamageReportCommandHandler { + /** + * @description create damageReport + * @param {CreateDamageReportCommand} createDamageReport + * @returns {Promise} + */ + static async create(createDamageReport: CreateDamageReportCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(damageReport) + .values({ + status: "eingereicht", + description: createDamageReport.description, + reportedBy: createDamageReport.reportedBy, + imageCount: createDamageReport.imageCount, + equipmentId: createDamageReport.affected == "equipment" ? createDamageReport.affectedId : null, + vehicleId: createDamageReport.affected == "vehicle" ? createDamageReport.affectedId : null, + wearableId: createDamageReport.affected == "wearable" ? createDamageReport.affectedId : null, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "damageReport", err); + }); + } + + /** + * @description update damageReport + * @param {UpdateDamageReportCommand} updateDamageReport + * @returns {Promise} + */ + static async update(updateDamageReport: UpdateDamageReportCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(damageReport) + .set({ + status: updateDamageReport.status, + done: updateDamageReport.done, + }) + .where("id = :id", { id: updateDamageReport.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "damageReport", err); + }); + } + + /** + * @description update damageReport related maintenance + * @param {UpdateDamageReportCommand} updateDamageReport + * @returns {Promise} + */ + static async updateRelatedMaintenance( + updateDamageReport: UpdateDamageReportRelatedMaintenanceCommand + ): Promise { + return await dataSource + .createQueryBuilder() + .update(damageReport) + .set({ + maintenanceId: updateDamageReport.maintenanceId, + }) + .where("id = :id", { id: updateDamageReport.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "damageReport->maintenance", err); + }); + } + + /** + * @description delete damageReport + * @param {DeleteDamageReportCommand} deleteDamageReport + * @returns {Promise} + */ + static async delete(deleteDamageReport: DeleteDamageReportCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(damageReport) + .where("id = :id", { id: deleteDamageReport.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("DELETE", "damageReport", err); + }); + } +} diff --git a/src/command/unit/equipment/equipmentCommand.ts b/src/command/unit/equipment/equipmentCommand.ts new file mode 100644 index 0000000..500d4c2 --- /dev/null +++ b/src/command/unit/equipment/equipmentCommand.ts @@ -0,0 +1,20 @@ +export interface CreateEquipmentCommand { + code?: string; + name: string; + location: string; + commissioned: Date; + equipmentTypeId: string; +} + +export interface UpdateEquipmentCommand { + id: string; + code?: string; + name: string; + location: string; + commissioned: Date; + decommissioned?: Date; +} + +export interface DeleteEquipmentCommand { + id: string; +} diff --git a/src/command/unit/equipment/equipmentCommandHandler.ts b/src/command/unit/equipment/equipmentCommandHandler.ts new file mode 100644 index 0000000..0aef534 --- /dev/null +++ b/src/command/unit/equipment/equipmentCommandHandler.ts @@ -0,0 +1,74 @@ +import { dataSource } from "../../../data-source"; +import { equipment } from "../../../entity/unit/equipment/equipment"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import { CreateEquipmentCommand, UpdateEquipmentCommand, DeleteEquipmentCommand } from "./equipmentCommand"; + +export default abstract class EquipmentCommandHandler { + /** + * @description create equipment + * @param {CreateEquipmentCommand} createEquipment + * @returns {Promise} + */ + static async create(createEquipment: CreateEquipmentCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(equipment) + .values({ + code: createEquipment.code, + name: createEquipment.name, + location: createEquipment.location, + commissioned: createEquipment.commissioned, + equipmentTypeId: createEquipment.equipmentTypeId, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "equipment", err); + }); + } + + /** + * @description update equipment + * @param {UpdateEquipmentCommand} updateEquipment + * @returns {Promise} + */ + static async update(updateEquipment: UpdateEquipmentCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(equipment) + .set({ + code: updateEquipment.code, + name: updateEquipment.name, + location: updateEquipment.location, + commissioned: updateEquipment.commissioned, + decommissioned: updateEquipment.decommissioned, + }) + .where("id = :id", { id: updateEquipment.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "equipment", err); + }); + } + + /** + * @description delete equipment + * @param {DeleteEquipmentCommand} deleteEquipment + * @returns {Promise} + */ + static async delete(deleteEquipment: DeleteEquipmentCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(equipment) + .where("id = :id", { id: deleteEquipment.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("DELETE", "equipment", err); + }); + } +} diff --git a/src/command/unit/equipment/equipmentTypeCommand.ts b/src/command/unit/equipment/equipmentTypeCommand.ts new file mode 100644 index 0000000..7d5c2f7 --- /dev/null +++ b/src/command/unit/equipment/equipmentTypeCommand.ts @@ -0,0 +1,14 @@ +export interface CreateEquipmentTypeCommand { + type: string; + description: string; +} + +export interface UpdateEquipmentTypeCommand { + id: string; + type: string; + description: string; +} + +export interface DeleteEquipmentTypeCommand { + id: string; +} diff --git a/src/command/unit/equipment/equipmentTypeCommandHandler.ts b/src/command/unit/equipment/equipmentTypeCommandHandler.ts new file mode 100644 index 0000000..47caec3 --- /dev/null +++ b/src/command/unit/equipment/equipmentTypeCommandHandler.ts @@ -0,0 +1,72 @@ +import { dataSource } from "../../../data-source"; +import { equipmentType } from "../../../entity/unit/equipment/equipmentType"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import { + CreateEquipmentTypeCommand, + UpdateEquipmentTypeCommand, + DeleteEquipmentTypeCommand, +} from "./equipmentTypeCommand"; + +export default abstract class EquipmentTypeCommandHandler { + /** + * @description create equipmentType + * @param {CreateEquipmentTypeCommand} createEquipmentType + * @returns {Promise} + */ + static async create(createEquipmentType: CreateEquipmentTypeCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(equipmentType) + .values({ + type: createEquipmentType.type, + description: createEquipmentType.description, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "equipmentType", err); + }); + } + + /** + * @description update equipmentType + * @param {UpdateEquipmentTypeCommand} updateEquipmentType + * @returns {Promise} + */ + static async update(updateEquipmentType: UpdateEquipmentTypeCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(equipmentType) + .set({ + type: updateEquipmentType.type, + description: updateEquipmentType.description, + }) + .where("id = :id", { id: updateEquipmentType.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "equipmentType", err); + }); + } + + /** + * @description delete equipmentType + * @param {DeleteEquipmentTypeCommand} deleteEquipmentType + * @returns {Promise} + */ + static async delete(deleteEquipmentType: DeleteEquipmentTypeCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(equipmentType) + .where("id = :id", { id: deleteEquipmentType.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("DELETE", "equipmentType", err); + }); + } +} diff --git a/src/command/unit/inspection/inspectionCommand.ts b/src/command/unit/inspection/inspectionCommand.ts new file mode 100644 index 0000000..254d690 --- /dev/null +++ b/src/command/unit/inspection/inspectionCommand.ts @@ -0,0 +1,21 @@ +export interface CreateInspectionCommand { + context: string; + nextInspection?: Date; + inspectionPlanId: string; + relatedId: string; + assigned: "vehicle" | "equipment" | "wearable"; +} + +export interface UpdateInspectionCommand { + id: string; + context: string; + nextInspection?: Date; +} + +export interface FinishInspectionCommand { + id: string; +} + +export interface DeleteInspectionCommand { + id: string; +} diff --git a/src/command/unit/inspection/inspectionCommandHandler.ts b/src/command/unit/inspection/inspectionCommandHandler.ts new file mode 100644 index 0000000..f6156b3 --- /dev/null +++ b/src/command/unit/inspection/inspectionCommandHandler.ts @@ -0,0 +1,154 @@ +import { IsNull, Not } from "typeorm"; +import { dataSource } from "../../../data-source"; +import { inspection } from "../../../entity/unit/inspection/inspection"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import InspectionService from "../../../service/unit/inspection/inspectionService"; +import InspectionVersionedPlanService from "../../../service/unit/inspection/inspectionVersionedPlanService"; +import { + CreateInspectionCommand, + UpdateInspectionCommand, + DeleteInspectionCommand, + FinishInspectionCommand, +} from "./inspectionCommand"; + +export default abstract class InspectionCommandHandler { + /** + * @description create inspection + * @param {CreateInspectionCommand} createInspection + * @returns {Promise} + */ + static async create(createInspection: CreateInspectionCommand): Promise { + let latestVersionedPlan = await InspectionVersionedPlanService.getLatestForInspectionPlan( + createInspection.inspectionPlanId + ); + let insertId = ""; + return await dataSource + .transaction(async (manager) => { + await manager + .createQueryBuilder() + .update(inspection) + .set({ + hasNewer: true, + }) + .where({ + inspectionPlanId: createInspection.inspectionPlanId, + equipmentId: createInspection.assigned == "equipment" ? createInspection.relatedId : IsNull(), + vehicleId: createInspection.assigned == "vehicle" ? createInspection.relatedId : IsNull(), + wearableId: createInspection.assigned == "wearable" ? createInspection.relatedId : IsNull(), + }) + .execute(); + + await manager + .createQueryBuilder() + .insert() + .into(inspection) + .values({ + context: createInspection.context, + nextInspection: createInspection.nextInspection, + inspectionPlanId: createInspection.inspectionPlanId, + inspectionVersionedPlanId: latestVersionedPlan.id, + equipmentId: createInspection.assigned == "equipment" ? createInspection.relatedId : null, + vehicleId: createInspection.assigned == "vehicle" ? createInspection.relatedId : null, + wearableId: createInspection.assigned == "wearable" ? createInspection.relatedId : null, + }) + .execute() + .then((result) => { + insertId = result.identifiers[0].id; + }); + }) + .then(() => { + return insertId; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "inspection", err); + }); + } + + /** + * @description update inspection + * @param {UpdateInspectionCommand} updateInspection + * @returns {Promise} + */ + static async update(updateInspection: UpdateInspectionCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(inspection) + .set({ + context: updateInspection.context, + nextInspection: updateInspection.nextInspection, + }) + .where("id = :id", { id: updateInspection.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "inspection", err); + }); + } + + /** + * @description finish inspection + * @param {FinishInspectionCommand} finishInspection + * @returns {Promise} + */ + static async finish(finishInspection: FinishInspectionCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(inspection) + .set({ + finishedAt: new Date(), + }) + .where("id = :id", { id: finishInspection.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("FINISH", "inspection", err); + }); + } + + /** + * @description delete inspection + * @param {DeleteInspectionCommand} deleteInspection + * @returns {Promise} + */ + static async delete(deleteInspection: DeleteInspectionCommand): Promise { + let deleteInspectionData = await InspectionService.getById(deleteInspection.id); + return await dataSource + .transaction(async (manager) => { + let latestInspection = await manager + .createQueryBuilder() + .from(inspection, "sub") + .where({ + inspectionPlanId: deleteInspectionData.inspectionPlanId, + inspectionVersionedPlanId: deleteInspectionData.inspectionVersionedPlanId, + equipmentId: deleteInspectionData.equipmentId ?? IsNull(), + vehicleId: deleteInspectionData.vehicleId ?? IsNull(), + wearableId: deleteInspectionData.wearableId ?? IsNull(), + }) + .andWhere({ id: Not(deleteInspection.id) }) + .orderBy("sub.createdAt", "DESC") + .limit(1) + .getOne(); + + if (latestInspection) + await manager + .createQueryBuilder() + .update(inspection) + .set({ + hasNewer: false, + }) + .where({ id: latestInspection.id }) + .execute(); + + await manager + .createQueryBuilder() + .delete() + .from(inspection) + .where("id = :id", { id: deleteInspection.id }) + .execute(); + }) + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("DELETE", "inspection", err); + }); + } +} diff --git a/src/command/unit/inspection/inspectionPlanCommand.ts b/src/command/unit/inspection/inspectionPlanCommand.ts new file mode 100644 index 0000000..0daac13 --- /dev/null +++ b/src/command/unit/inspection/inspectionPlanCommand.ts @@ -0,0 +1,20 @@ +import { PlanTimeDefinition } from "../../../viewmodel/admin/unit/inspection/inspectionPlan.models"; + +export interface CreateInspectionPlanCommand { + title: string; + inspectionInterval: PlanTimeDefinition; + remindTime: PlanTimeDefinition; + relatedId: string; + assigned: "vehicle" | "equipment"; +} + +export interface UpdateInspectionPlanCommand { + id: string; + title: string; + inspectionInterval: PlanTimeDefinition; + remindTime?: PlanTimeDefinition; +} + +export interface DeleteInspectionPlanCommand { + id: string; +} diff --git a/src/command/unit/inspection/inspectionPlanCommandHandler.ts b/src/command/unit/inspection/inspectionPlanCommandHandler.ts new file mode 100644 index 0000000..179f8a7 --- /dev/null +++ b/src/command/unit/inspection/inspectionPlanCommandHandler.ts @@ -0,0 +1,76 @@ +import { dataSource } from "../../../data-source"; +import { inspectionPlan } from "../../../entity/unit/inspection/inspectionPlan"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import { + CreateInspectionPlanCommand, + UpdateInspectionPlanCommand, + DeleteInspectionPlanCommand, +} from "./inspectionPlanCommand"; + +export default abstract class InspectionPlanCommandHandler { + /** + * @description create inspectionPlan + * @param {CreateInspectionPlanCommand} createInspectionPlan + * @returns {Promise} + */ + static async create(createInspectionPlan: CreateInspectionPlanCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(inspectionPlan) + .values({ + title: createInspectionPlan.title, + inspectionInterval: createInspectionPlan.inspectionInterval, + remindTime: createInspectionPlan.remindTime, + equipmentTypeId: createInspectionPlan.assigned == "equipment" ? createInspectionPlan.relatedId : null, + vehicleTypeId: createInspectionPlan.assigned == "vehicle" ? createInspectionPlan.relatedId : null, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "inspectionPlan", err); + }); + } + + /** + * @description update inspectionPlan + * @param {UpdateInspectionPlanCommand} updateInspectionPlan + * @returns {Promise} + */ + static async update(updateInspectionPlan: UpdateInspectionPlanCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(inspectionPlan) + .set({ + title: updateInspectionPlan.title, + inspectionInterval: updateInspectionPlan.inspectionInterval, + remindTime: updateInspectionPlan.remindTime, + }) + .where("id = :id", { id: updateInspectionPlan.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "inspectionPlan", err); + }); + } + + /** + * @description delete inspectionPlan + * @param {DeleteInspectionPlanCommand} deleteInspectionPlan + * @returns {Promise} + */ + static async delete(deleteInspectionPlan: DeleteInspectionPlanCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(inspectionPlan) + .where("id = :id", { id: deleteInspectionPlan.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("DELETE", "inspectionPlan", err); + }); + } +} diff --git a/src/command/unit/inspection/inspectionPointCommand.ts b/src/command/unit/inspection/inspectionPointCommand.ts new file mode 100644 index 0000000..8163bae --- /dev/null +++ b/src/command/unit/inspection/inspectionPointCommand.ts @@ -0,0 +1,13 @@ +import { InspectionPointEnum } from "../../../enums/inspectionEnum"; + +export interface CreateInspectionPointCommand { + id?: string; + title: string; + description: string; + type: InspectionPointEnum; + min?: number; + max?: number; + others?: string; + sort: number; + versionedPointId?: string; +} diff --git a/src/command/unit/inspection/inspectionPointCommandHandler.ts b/src/command/unit/inspection/inspectionPointCommandHandler.ts new file mode 100644 index 0000000..a704f04 --- /dev/null +++ b/src/command/unit/inspection/inspectionPointCommandHandler.ts @@ -0,0 +1,74 @@ +import { dataSource } from "../../../data-source"; +import { inspectionPoint } from "../../../entity/unit/inspection/inspectionPoint"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import InspectionPointService from "../../../service/unit/inspection/inspectionPointService"; +import { CreateInspectionPointCommand } from "./inspectionPointCommand"; + +export default abstract class InspectionPointCommandHandler { + /** + * @description create inspectionPoint + * @param {CreateInspectionPointCommand} createInspectionPoint + * @returns {Promise} + */ + static async create(createInspectionPoint: CreateInspectionPointCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(inspectionPoint) + .values({ + title: createInspectionPoint.title, + description: createInspectionPoint.description, + type: createInspectionPoint.type, + min: createInspectionPoint.min, + max: createInspectionPoint.max, + sort: createInspectionPoint.sort, + versionedPlanId: createInspectionPoint.versionedPointId, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "inspectionPoint", err); + }); + } + + /** + * @description sync points + * @param {string} versionedPlanId + * @param {Array} sync + * @returns {Promise} + */ + static async sync(versionedPlanId: string, sync: Array): Promise { + let points = await InspectionPointService.getAllForVersionedPlan(versionedPlanId); + await dataSource + .transaction(async (manager) => { + let remove = points.filter((r) => !sync.some((cp) => cp.id == r.id)); + await manager + .createQueryBuilder() + .insert() + .into(inspectionPoint) + .values( + sync.map((s) => ({ + ...s, + id: points.some((p) => p.id == s.id) ? s.id : undefined, + versionedPlanId, + })) + ) + .orUpdate(["title", "description", "min", "max", "others", "sort"], ["id"]) + .execute(); + + if (remove.length != 0) + await manager + .createQueryBuilder() + .delete() + .from(inspectionPoint) + .where("id IN (:...ids)", { ids: remove.map((r) => r.id) }) + .andWhere({ versionedPlanId }) + .execute(); + }) + .catch((err) => { + throw new DatabaseActionException("SYNC", "inspectionPoint", err); + }); + } +} diff --git a/src/command/unit/inspection/inspectionPointResultCommand.ts b/src/command/unit/inspection/inspectionPointResultCommand.ts new file mode 100644 index 0000000..c1893d3 --- /dev/null +++ b/src/command/unit/inspection/inspectionPointResultCommand.ts @@ -0,0 +1,5 @@ +export interface CreateOrUpdateInspectionPointResultCommand { + inspectionId: string; + inspectionPointId: string; + value: string; +} diff --git a/src/command/unit/inspection/inspectionPointResultCommandHandler.ts b/src/command/unit/inspection/inspectionPointResultCommandHandler.ts new file mode 100644 index 0000000..8ffeb6a --- /dev/null +++ b/src/command/unit/inspection/inspectionPointResultCommandHandler.ts @@ -0,0 +1,54 @@ +import { dataSource } from "../../../data-source"; +import { inspectionPointResult } from "../../../entity/unit/inspection/inspectionPointResult"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import { CreateOrUpdateInspectionPointResultCommand } from "./inspectionPointResultCommand"; + +export default abstract class InspectionPointResultCommandHandler { + /** + * @description create inspectionPointResult + * @param {CreateOrUpdateInspectionPointResultCommand} createInspectionPointResult + * @returns {Promise} + */ + static async createOrUpdate( + createInspectionPointResult: CreateOrUpdateInspectionPointResultCommand + ): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(inspectionPointResult) + .values({ + inspectionId: createInspectionPointResult.inspectionId, + inspectionPointId: createInspectionPointResult.inspectionPointId, + value: createInspectionPointResult.value, + }) + .orUpdate(["value"], ["inspectionId", "inspectionPointId"]) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE or UPDATE", "inspectionPointResult", err); + }); + } + + /** + * @description create inspectionPointResult + * @param {Array} results + * @returns {Promise} + */ + static async createOrUpdateMultiple(results: Array): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(inspectionPointResult) + .values(results) + .orUpdate(["value"], ["inspectionId", "inspectionPointId"]) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE or UPDATE", "inspectionPointResult", err); + }); + } +} diff --git a/src/command/unit/inspection/inspectionVersionedPlanCommand.ts b/src/command/unit/inspection/inspectionVersionedPlanCommand.ts new file mode 100644 index 0000000..825de12 --- /dev/null +++ b/src/command/unit/inspection/inspectionVersionedPlanCommand.ts @@ -0,0 +1,3 @@ +export interface CreateInspectionVersionedPlanCommand { + inspectionPlanId: string; +} diff --git a/src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts b/src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts new file mode 100644 index 0000000..151aa16 --- /dev/null +++ b/src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts @@ -0,0 +1,62 @@ +import { dataSource } from "../../../data-source"; +import { inspectionPoint } from "../../../entity/unit/inspection/inspectionPoint"; +import { inspectionVersionedPlan } from "../../../entity/unit/inspection/inspectionVersionedPlan"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import InspectionVersionedPlanService from "../../../service/unit/inspection/inspectionVersionedPlanService"; +import { CreateInspectionPointCommand } from "./inspectionPointCommand"; +import { CreateInspectionVersionedPlanCommand } from "./inspectionVersionedPlanCommand"; + +export default abstract class InspectionVersionedPlanCommandHandler { + /** + * @description create inspectionVersionedPlan + * @param {CreateInspectionVersionedPlanCommand} createInspectionVersionedPlan + * @returns {Promise} + */ + static async create( + createInspectionVersionedPlan: CreateInspectionVersionedPlanCommand, + inspectionPoints: Array + ): Promise { + let count = await InspectionVersionedPlanService.countForPlanId(createInspectionVersionedPlan.inspectionPlanId); + let returnId = ""; + + return await dataSource + .transaction(async (manager) => { + await manager + .createQueryBuilder() + .insert() + .into(inspectionVersionedPlan) + .values({ + inspectionPlanId: createInspectionVersionedPlan.inspectionPlanId, + version: count, + }) + .execute() + .then((result) => { + returnId = result.identifiers[0].id; + }); + + await manager + .createQueryBuilder() + .insert() + .into(inspectionPoint) + .values( + inspectionPoints.map((ip) => ({ + title: ip.title, + description: ip.description, + type: ip.type, + min: ip.min, + max: ip.max, + others: ip.others, + sort: ip.sort, + versionedPlanId: returnId, + })) + ) + .execute(); + }) + .then(() => { + return returnId; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "inspectionVersionedPlan", err); + }); + } +} diff --git a/src/command/unit/maintenanceCommand.ts b/src/command/unit/maintenanceCommand.ts new file mode 100644 index 0000000..637ed3b --- /dev/null +++ b/src/command/unit/maintenanceCommand.ts @@ -0,0 +1,16 @@ +export interface CreateMaintenanceCommand { + description: string; + affectedId: string; + affected: "equipment" | "vehicle" | "wearable"; +} + +export interface UpdateMaintenanceCommand { + id: string; + status: string; + done: boolean; + description: string; +} + +export interface DeleteMaintenanceCommand { + id: string; +} diff --git a/src/command/unit/maintenanceCommandHandler.ts b/src/command/unit/maintenanceCommandHandler.ts new file mode 100644 index 0000000..96819dd --- /dev/null +++ b/src/command/unit/maintenanceCommandHandler.ts @@ -0,0 +1,72 @@ +import { dataSource } from "../../data-source"; +import { maintenance } from "../../entity/unit/maintenance"; +import DatabaseActionException from "../../exceptions/databaseActionException"; +import { CreateMaintenanceCommand, UpdateMaintenanceCommand, DeleteMaintenanceCommand } from "./maintenanceCommand"; + +export default abstract class MaintenanceCommandHandler { + /** + * @description create maintenance + * @param {CreateMaintenanceCommand} createMaintenance + * @returns {Promise} + */ + static async create(createMaintenance: CreateMaintenanceCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(maintenance) + .values({ + status: "gestartet", + description: createMaintenance.description, + equipmentId: createMaintenance.affected == "equipment" ? createMaintenance.affectedId : null, + vehicleId: createMaintenance.affected == "vehicle" ? createMaintenance.affectedId : null, + wearableId: createMaintenance.affected == "wearable" ? createMaintenance.affectedId : null, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "maintenance", err); + }); + } + + /** + * @description update maintenance + * @param {UpdateMaintenanceCommand} updateMaintenance + * @returns {Promise} + */ + static async update(updateMaintenance: UpdateMaintenanceCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(maintenance) + .set({ + status: updateMaintenance.status, + done: updateMaintenance.done, + description: updateMaintenance.description, + }) + .where("id = :id", { id: updateMaintenance.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "maintenance", err); + }); + } + + /** + * @description delete maintenance + * @param {DeleteMaintenanceCommand} deleteMaintenance + * @returns {Promise} + */ + static async delete(deleteMaintenance: DeleteMaintenanceCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(maintenance) + .where("id = :id", { id: deleteMaintenance.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("DELETE", "maintenance", err); + }); + } +} diff --git a/src/command/unit/vehicle/vehicleCommand.ts b/src/command/unit/vehicle/vehicleCommand.ts new file mode 100644 index 0000000..1310c0b --- /dev/null +++ b/src/command/unit/vehicle/vehicleCommand.ts @@ -0,0 +1,20 @@ +export interface CreateVehicleCommand { + code?: string; + name: string; + location: string; + commissioned: Date; + vehicleTypeId: string; +} + +export interface UpdateVehicleCommand { + id: string; + code?: string; + name: string; + location: string; + commissioned: Date; + decommissioned?: Date; +} + +export interface DeleteVehicleCommand { + id: string; +} diff --git a/src/command/unit/vehicle/vehicleCommandHandler.ts b/src/command/unit/vehicle/vehicleCommandHandler.ts new file mode 100644 index 0000000..db493f6 --- /dev/null +++ b/src/command/unit/vehicle/vehicleCommandHandler.ts @@ -0,0 +1,73 @@ +import { dataSource } from "../../../data-source"; +import { vehicle } from "../../../entity/unit/vehicle/vehicle"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import { CreateVehicleCommand, UpdateVehicleCommand, DeleteVehicleCommand } from "./vehicleCommand"; + +export default abstract class VehicleCommandHandler { + /** + * @description create vehicle + * @param {CreateVehicleCommand} createVehicle + * @returns {Promise} + */ + static async create(createVehicle: CreateVehicleCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(vehicle) + .values({ + code: createVehicle.code, + name: createVehicle.name, + location: createVehicle.location, + commissioned: createVehicle.commissioned, + vehicleTypeId: createVehicle.vehicleTypeId, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "vehicle", err); + }); + } + + /** + * @description update vehicle + * @param {UpdateVehicleCommand} updateVehicle + * @returns {Promise} + */ + static async update(updateVehicle: UpdateVehicleCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(vehicle) + .set({ + code: updateVehicle.code, + name: updateVehicle.name, + location: updateVehicle.location, + commissioned: updateVehicle.commissioned, + }) + .where("id = :id", { id: updateVehicle.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "vehicle", err); + }); + } + + /** + * @description delete vehicle + * @param {DeleteVehicleCommand} deleteVehicle + * @returns {Promise} + */ + static async delete(deleteVehicle: DeleteVehicleCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(vehicle) + .where("id = :id", { id: deleteVehicle.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("DELETE", "vehicle", err); + }); + } +} diff --git a/src/command/unit/vehicle/vehicleTypeCommand.ts b/src/command/unit/vehicle/vehicleTypeCommand.ts new file mode 100644 index 0000000..d155fe8 --- /dev/null +++ b/src/command/unit/vehicle/vehicleTypeCommand.ts @@ -0,0 +1,14 @@ +export interface CreateVehicleTypeCommand { + type: string; + description: string; +} + +export interface UpdateVehicleTypeCommand { + id: string; + type: string; + description: string; +} + +export interface DeleteVehicleTypeCommand { + id: string; +} diff --git a/src/command/unit/vehicle/vehicleTypeCommandHandler.ts b/src/command/unit/vehicle/vehicleTypeCommandHandler.ts new file mode 100644 index 0000000..de25fd1 --- /dev/null +++ b/src/command/unit/vehicle/vehicleTypeCommandHandler.ts @@ -0,0 +1,68 @@ +import { dataSource } from "../../../data-source"; +import { vehicleType } from "../../../entity/unit/vehicle/vehicleType"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import { CreateVehicleTypeCommand, UpdateVehicleTypeCommand, DeleteVehicleTypeCommand } from "./vehicleTypeCommand"; + +export default abstract class VehicleTypeCommandHandler { + /** + * @description create vehicleType + * @param {CreateVehicleTypeCommand} createVehicleType + * @returns {Promise} + */ + static async create(createVehicleType: CreateVehicleTypeCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(vehicleType) + .values({ + type: createVehicleType.type, + description: createVehicleType.description, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "vehicleType", err); + }); + } + + /** + * @description update vehicleType + * @param {UpdateVehicleTypeCommand} updateVehicleType + * @returns {Promise} + */ + static async update(updateVehicleType: UpdateVehicleTypeCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(vehicleType) + .set({ + type: updateVehicleType.type, + description: updateVehicleType.description, + }) + .where("id = :id", { id: updateVehicleType.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "vehicleType", err); + }); + } + + /** + * @description delete vehicleType + * @param {DeleteVehicleTypeCommand} deleteVehicleType + * @returns {Promise} + */ + static async delete(deleteVehicleType: DeleteVehicleTypeCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(vehicleType) + .where("id = :id", { id: deleteVehicleType.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("DELETE", "vehicleType", err); + }); + } +} diff --git a/src/command/unit/wearable/wearableCommand.ts b/src/command/unit/wearable/wearableCommand.ts new file mode 100644 index 0000000..c58eeb7 --- /dev/null +++ b/src/command/unit/wearable/wearableCommand.ts @@ -0,0 +1,22 @@ +export interface CreateWearableCommand { + code?: string; + name: string; + location: string; + commissioned: Date; + wearableTypeId: string; + wearerId?: string; +} + +export interface UpdateWearableCommand { + id: string; + code?: string; + name: string; + location: string; + commissioned: Date; + decommissioned?: Date; + wearerId?: string; +} + +export interface DeleteWearableCommand { + id: string; +} diff --git a/src/command/unit/wearable/wearableCommandHandler.ts b/src/command/unit/wearable/wearableCommandHandler.ts new file mode 100644 index 0000000..a13b773 --- /dev/null +++ b/src/command/unit/wearable/wearableCommandHandler.ts @@ -0,0 +1,76 @@ +import { dataSource } from "../../../data-source"; +import { wearable } from "../../../entity/unit/wearable/wearable"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import { CreateWearableCommand, UpdateWearableCommand, DeleteWearableCommand } from "./wearableCommand"; + +export default abstract class WearableCommandHandler { + /** + * @description create wearable + * @param {CreateWearableCommand} createWearable + * @returns {Promise} + */ + static async create(createWearable: CreateWearableCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(wearable) + .values({ + code: createWearable.code, + name: createWearable.name, + location: createWearable.location, + commissioned: createWearable.commissioned, + wearableTypeId: createWearable.wearableTypeId, + wearerId: createWearable.wearerId, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "wearable", err); + }); + } + + /** + * @description update wearable + * @param {UpdateWearableCommand} updateWearable + * @returns {Promise} + */ + static async update(updateWearable: UpdateWearableCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(wearable) + .set({ + code: updateWearable.code, + name: updateWearable.name, + location: updateWearable.location, + commissioned: updateWearable.commissioned, + decommissioned: updateWearable.decommissioned, + wearerId: updateWearable.wearerId, + }) + .where("id = :id", { id: updateWearable.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "wearable", err); + }); + } + + /** + * @description delete wearable + * @param {DeleteWearableCommand} deleteWearable + * @returns {Promise} + */ + static async delete(deleteWearable: DeleteWearableCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(wearable) + .where("id = :id", { id: deleteWearable.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("DELETE", "wearable", err); + }); + } +} diff --git a/src/command/unit/wearable/wearableTypeCommand.ts b/src/command/unit/wearable/wearableTypeCommand.ts new file mode 100644 index 0000000..45cbc41 --- /dev/null +++ b/src/command/unit/wearable/wearableTypeCommand.ts @@ -0,0 +1,14 @@ +export interface CreateWearableTypeCommand { + type: string; + description: string; +} + +export interface UpdateWearableTypeCommand { + id: string; + type: string; + description: string; +} + +export interface DeleteWearableTypeCommand { + id: string; +} diff --git a/src/command/unit/wearable/wearableTypeCommandHandler.ts b/src/command/unit/wearable/wearableTypeCommandHandler.ts new file mode 100644 index 0000000..b8f0f6f --- /dev/null +++ b/src/command/unit/wearable/wearableTypeCommandHandler.ts @@ -0,0 +1,68 @@ +import { dataSource } from "../../../data-source"; +import { wearableType } from "../../../entity/unit/wearable/wearableType"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import { CreateWearableTypeCommand, UpdateWearableTypeCommand, DeleteWearableTypeCommand } from "./wearableTypeCommand"; + +export default abstract class WearableTypeCommandHandler { + /** + * @description create wearableType + * @param {CreateWearableTypeCommand} createWearableType + * @returns {Promise} + */ + static async create(createWearableType: CreateWearableTypeCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(wearableType) + .values({ + type: createWearableType.type, + description: createWearableType.description, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "wearableType", err); + }); + } + + /** + * @description update wearableType + * @param {UpdateWearableTypeCommand} updateWearableType + * @returns {Promise} + */ + static async update(updateWearableType: UpdateWearableTypeCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(wearableType) + .set({ + type: updateWearableType.type, + description: updateWearableType.description, + }) + .where("id = :id", { id: updateWearableType.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "wearableType", err); + }); + } + + /** + * @description delete wearableType + * @param {DeleteWearableTypeCommand} deleteWearableType + * @returns {Promise} + */ + static async delete(deleteWearableType: DeleteWearableTypeCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(wearableType) + .where("id = :id", { id: deleteWearableType.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("DELETE", "wearableType", err); + }); + } +} diff --git a/src/controller/admin/unit/damageReportController.ts b/src/controller/admin/unit/damageReportController.ts new file mode 100644 index 0000000..443a6cc --- /dev/null +++ b/src/controller/admin/unit/damageReportController.ts @@ -0,0 +1,120 @@ +import { Request, Response } from "express"; +import DamageReportService from "../../../service/unit/damageReportService"; +import DamageReportFactory from "../../../factory/admin/unit/damageReport"; +import { CreateDamageReportCommand, UpdateDamageReportCommand } from "../../../command/unit/damageReportCommand"; +import DamageReportCommandHandler from "../../../command/unit/damageReportCommandHandler"; +import BadRequestException from "../../../exceptions/badRequestException"; + +/** + * @description get all damageReports by status + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllDamageReportsByStatus(req: Request, res: Response): Promise { + let done = req.query.done === "true"; + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let noLimit = req.query.noLimit === "true"; + + let [damageReports, total] = await DamageReportService.getAll(done, { offset, count, noLimit }); + + res.json({ + damageReports: DamageReportFactory.mapToBase(damageReports), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get all damageReports for related id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllDamageReportsForRelated(req: Request, res: Response): Promise { + let relation = req.params.related as "vehicle" | "equipment" | "wearable"; + let relationId = req.params.relatedId as string; + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let noLimit = req.query.noLimit === "true"; + + let where; + if (relation == "equipment") { + where = { equipmentId: relationId }; + } else if (relation == "vehicle") { + where = { vehicleId: relationId }; + } else { + where = { wearableId: relationId }; + } + let [damageReports, total] = await DamageReportService.getAllForRelated(where, { offset, count, noLimit }); + + res.json({ + damageReports: DamageReportFactory.mapToBase(damageReports), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get damageReport by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getDamageReportById(req: Request, res: Response): Promise { + const damageReportId = req.params.id; + let damageReport = await DamageReportService.getById(damageReportId); + + res.json(DamageReportFactory.mapToSingle(damageReport)); +} + +/** + * @description create damageReport + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createDamageReport(req: Request, res: Response): Promise { + const description = req.body.description; + const reportedBy = req.body.reportedBy; + const affectedId = req.body.affectedId; + const affected = req.body.affected; + + if (affected != "equipment" && affected != "vehicle" && affected != "wearable") + throw new BadRequestException("set assigned to equipment or vehicle or wearable"); + + let createDamageReport: CreateDamageReportCommand = { + description, + reportedBy, + imageCount: 0, + affectedId, + affected, + }; + let damageReportId = await DamageReportCommandHandler.create(createDamageReport); + + res.status(200).send(damageReportId); +} + +/** + * @description update damageReport by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateDamageReportById(req: Request, res: Response): Promise { + const damageReportId = req.params.id; + const status = req.body.status; + const done = req.body.done; + + let updateDamageReport: UpdateDamageReportCommand = { + id: damageReportId, + status, + done, + }; + await DamageReportCommandHandler.update(updateDamageReport); + + res.sendStatus(204); +} diff --git a/src/controller/admin/unit/equipmentController.ts b/src/controller/admin/unit/equipmentController.ts new file mode 100644 index 0000000..caee218 --- /dev/null +++ b/src/controller/admin/unit/equipmentController.ts @@ -0,0 +1,133 @@ +import { Request, Response } from "express"; +import EquipmentService from "../../../service/unit/equipment/equipmentService"; +import EquipmentFactory from "../../../factory/admin/unit/equipment/equipment"; +import { + CreateEquipmentCommand, + DeleteEquipmentCommand, + UpdateEquipmentCommand, +} from "../../../command/unit/equipment/equipmentCommand"; +import EquipmentCommandHandler from "../../../command/unit/equipment/equipmentCommandHandler"; + +/** + * @description get all equipments + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllEquipments(req: Request, res: Response): Promise { + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let search = (req.query.search as string) ?? ""; + let noLimit = req.query.noLimit === "true"; + let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); + + let [equipments, total] = await EquipmentService.getAll({ offset, count, search, noLimit, ids }); + + res.json({ + equipments: EquipmentFactory.mapToBase(equipments), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get equipment by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getEquipmentById(req: Request, res: Response): Promise { + const equipmentId = req.params.id; + let equipment = await EquipmentService.getById(equipmentId); + + res.json(EquipmentFactory.mapToSingle(equipment)); +} + +/** + * @description get equipment by Ids + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getEquipmentsByIds(req: Request, res: Response): Promise { + let ids = req.body.ids as Array; + + let [equipments, total] = await EquipmentService.getAll({ noLimit: true, ids }); + + res.json({ + equipments: EquipmentFactory.mapToBase(equipments), + total: total, + offset: 0, + count: total, + }); +} + +/** + * @description create equipment + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createEquipment(req: Request, res: Response): Promise { + const name = req.body.name; + const code = req.body.code || null; + const location = req.body.location; + const commissioned = req.body.commissioned; + const equipmentTypeId = req.body.equipmentTypeId; + + let createEquipment: CreateEquipmentCommand = { + code, + name, + location, + commissioned, + equipmentTypeId, + }; + let equipmentId = await EquipmentCommandHandler.create(createEquipment); + + res.status(200).send(equipmentId); +} + +/** + * @description update equipment by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateEquipmentById(req: Request, res: Response): Promise { + const equipmentId = req.params.id; + const name = req.body.name; + const code = req.body.code || null; + const location = req.body.location; + const commissioned = req.body.commissioned; + const decommissioned = req.body.decommissioned || null; + + let updateEquipment: UpdateEquipmentCommand = { + id: equipmentId, + name, + code, + location, + commissioned, + decommissioned, + }; + await EquipmentCommandHandler.update(updateEquipment); + + res.sendStatus(204); +} + +/** + * @description delete equipment by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteEquipmentById(req: Request, res: Response): Promise { + const equipmentId = req.params.id; + + let deleteEquipment: DeleteEquipmentCommand = { + id: equipmentId, + }; + await EquipmentCommandHandler.delete(deleteEquipment); + + res.sendStatus(204); +} diff --git a/src/controller/admin/unit/equipmentTypeController.ts b/src/controller/admin/unit/equipmentTypeController.ts new file mode 100644 index 0000000..cfae069 --- /dev/null +++ b/src/controller/admin/unit/equipmentTypeController.ts @@ -0,0 +1,101 @@ +import { Request, Response } from "express"; +import EquipmentTypeService from "../../../service/unit/equipment/equipmentTypeService"; +import EquipmentTypeFactory from "../../../factory/admin/unit/equipment/equipmentType"; +import { + CreateEquipmentTypeCommand, + DeleteEquipmentTypeCommand, + UpdateEquipmentTypeCommand, +} from "../../../command/unit/equipment/equipmentTypeCommand"; +import EquipmentTypeCommandHandler from "../../../command/unit/equipment/equipmentTypeCommandHandler"; + +/** + * @description get all equipmentTypes + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllEquipmentTypes(req: Request, res: Response): Promise { + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let search = (req.query.search as string) ?? ""; + let noLimit = req.query.noLimit === "true"; + + let [equipmentTypes, total] = await EquipmentTypeService.getAll({ offset, count, search, noLimit }); + + res.json({ + equipmentTypes: EquipmentTypeFactory.mapToBase(equipmentTypes), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get equipmentType by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getEquipmentTypeById(req: Request, res: Response): Promise { + const equipmentTypeId = req.params.id; + let equipmentType = await EquipmentTypeService.getById(equipmentTypeId); + + res.json(EquipmentTypeFactory.mapToSingle(equipmentType)); +} + +/** + * @description create equipmentType + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createEquipmentType(req: Request, res: Response): Promise { + const type = req.body.type; + const description = req.body.description; + + let createEquipmentType: CreateEquipmentTypeCommand = { + type, + description, + }; + let equipmentTypeId = await EquipmentTypeCommandHandler.create(createEquipmentType); + + res.status(200).send(equipmentTypeId); +} + +/** + * @description update equipmentType by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateEquipmentTypeById(req: Request, res: Response): Promise { + const equipmentTypeId = req.params.id; + const type = req.body.type; + const description = req.body.description; + + let updateEquipmentType: UpdateEquipmentTypeCommand = { + id: equipmentTypeId, + type, + description, + }; + await EquipmentTypeCommandHandler.update(updateEquipmentType); + + res.sendStatus(204); +} + +/** + * @description delete equipmentType by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteEquipmentTypeById(req: Request, res: Response): Promise { + const equipmentTypeId = req.params.id; + + let deleteEquipmentType: DeleteEquipmentTypeCommand = { + id: equipmentTypeId, + }; + await EquipmentTypeCommandHandler.delete(deleteEquipmentType); + + res.sendStatus(204); +} diff --git a/src/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts new file mode 100644 index 0000000..f91b3d0 --- /dev/null +++ b/src/controller/admin/unit/inspectionController.ts @@ -0,0 +1,392 @@ +import { Request, Response } from "express"; +import InspectionService from "../../../service/unit/inspection/inspectionService"; +import InspectionFactory from "../../../factory/admin/unit/inspection/inspection"; +import { + CreateInspectionCommand, + DeleteInspectionCommand, + FinishInspectionCommand, + UpdateInspectionCommand, +} from "../../../command/unit/inspection/inspectionCommand"; +import InspectionCommandHandler from "../../../command/unit/inspection/inspectionCommandHandler"; +import BadRequestException from "../../../exceptions/badRequestException"; +import ForbiddenRequestException from "../../../exceptions/forbiddenRequestException"; +import { CreateOrUpdateInspectionPointResultCommand } from "../../../command/unit/inspection/inspectionPointResultCommand"; +import InspectionPointResultCommandHandler from "../../../command/unit/inspection/inspectionPointResultCommandHandler"; +import { InspectionPointEnum } from "../../../enums/inspectionEnum"; +import multer from "multer"; +import { FileSystemHelper } from "../../../helpers/fileSystemHelper"; +import { PdfExport } from "../../../helpers/pdfExport"; +import { PDFDocument } from "pdf-lib"; +import sharp from "sharp"; +import InspectionPointService from "../../../service/unit/inspection/inspectionPointService"; +import InspectionPointResultService from "../../../service/unit/inspection/inspectionPointResultService"; + +/** + * @description get all inspections sorted by id not having newer inspection + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllInspectionsSortedNotHavingNewer(req: Request, res: Response): Promise { + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let noLimit = req.query.noLimit === "true"; + + let [inspections, total] = await InspectionService.getAllSortedNotHavingNewer({ offset, count, noLimit }); + + res.json({ + inspections: InspectionFactory.mapToBaseNext(inspections), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get all inspections running + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllInspectionsRunning(req: Request, res: Response): Promise { + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let noLimit = req.query.noLimit === "true"; + + let [inspections, total] = await InspectionService.getAllRunning({ offset, count, noLimit }); + + res.json({ + inspections: InspectionFactory.mapToBaseMinified(inspections), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get all inspections for related id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllInspectionsForRelated(req: Request, res: Response): Promise { + let relation = req.params.related as "vehicle" | "equipment" | "wearable"; + let relationId = req.params.relatedId as string; + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let noLimit = req.query.noLimit === "true"; + + let where; + if (relation == "equipment") { + where = { equipmentId: relationId }; + } else if (relation == "vehicle") { + where = { vehicleId: relationId }; + } else { + where = { wearableId: relationId }; + } + let [inspections, total] = await InspectionService.getAllForRelated(where, { offset, count, noLimit }); + + res.json({ + inspections: InspectionFactory.mapToBaseMinified(inspections), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get inspection by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getInspectionPrintoutById(req: Request, res: Response): Promise { + const inspectionId = req.params.id; + let inspection = await InspectionService.getById(inspectionId); + + if (inspection.finishedAt == null) + throw new ForbiddenRequestException("this inspection has not been finished yet and it so does not have a printout"); + + let filepath = FileSystemHelper.formatPath("inspection", inspection.id, "printout.pdf"); + + res.sendFile(filepath, { + headers: { + "Content-Type": "application/pdf", + }, + }); +} + +/** + * @description get inspection by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getInspectionPointUpload(req: Request, res: Response): Promise { + const inspectionId = req.params.id; + const inspectionPointId = req.params.pointId; + let result = await InspectionPointResultService.getForInspectionAndPoint(inspectionId, inspectionPointId); + + let filepath = FileSystemHelper.formatPath("inspection", inspectionId, result.value); + + if (result.inspectionPoint.others === "pdf") { + res.sendFile(filepath, { + headers: { + "Content-Type": "application/pdf", + }, + }); + } else { + let image = await sharp(filepath).png().toBuffer(); + res.set({ + "Content-Type": "image/png", + }); + res.send(image); + } +} + +/** + * @description get inspection by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getInspectionById(req: Request, res: Response): Promise { + const inspectionId = req.params.id; + let inspection = await InspectionService.getById(inspectionId); + + res.json(InspectionFactory.mapToSingle(inspection)); +} + +/** + * @description create inspection + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createInspection(req: Request, res: Response): Promise { + const context = req.body.context; + const inspectionPlanId = req.body.inspectionPlanId; + const relatedId = req.body.relatedId; + const assigned = req.body.assigned; + const nextInspection = req.body.nextInspection || null; + + if (assigned != "equipment" && assigned != "vehicle" && assigned != "wearable") + throw new BadRequestException("set assigned to equipment or vehicle or wearable"); + + let existsUnfinished = await InspectionService.existsUnfinishedInspectionToPlan( + inspectionPlanId, + assigned, + relatedId + ); + if (existsUnfinished) throw new ForbiddenRequestException("there is already an unfinished inspection existing"); + + let createInspection: CreateInspectionCommand = { + context, + nextInspection, + inspectionPlanId, + relatedId, + assigned, + }; + let inspectionId = await InspectionCommandHandler.create(createInspection); + + res.status(200).send(inspectionId); +} + +/** + * @description update inspection by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateInspectionById(req: Request, res: Response): Promise { + const inspectionId = req.params.id; + const context = req.body.context; + const nextInspection = req.body.nextInspection || null; + + let updateInspection: UpdateInspectionCommand = { + id: inspectionId, + context, + nextInspection, + }; + await InspectionCommandHandler.update(updateInspection); + + res.sendStatus(204); +} + +/** + * @description update inspection by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateInspectionResults(req: Request, res: Response): Promise { + const inspectionId = req.params.id; + const pointResults = JSON.parse(req.body.results) as Array<{ inspectionPointId: string; value: string }>; + const pointFiles = req.files as Array; + + let inspection = await InspectionService.getById(inspectionId); + + let updateResults: Array = pointResults.map((pr) => ({ + inspectionPointId: pr.inspectionPointId, + value: + inspection.inspectionVersionedPlan.inspectionPoints.find((ip) => ip.id == pr.inspectionPointId).type == + InspectionPointEnum.file && pr.value == "set" + ? pointFiles.find((f) => f.filename.startsWith(pr.inspectionPointId))?.filename + : pr.value, + inspectionId, + })); + await InspectionPointResultCommandHandler.createOrUpdateMultiple(updateResults); + + res.sendStatus(204); +} + +/** + * @description finish inspection by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function finishInspection(req: Request, res: Response): Promise { + const inspectionId = req.params.id; + + let inspection = await InspectionService.getById(inspectionId); + + function getValueToInspectionPoint(inspectionPointId: string) { + return inspection.pointResults.find((c) => c.inspectionPointId == inspectionPointId)?.value; + } + + let everythingFilled = inspection.inspectionVersionedPlan.inspectionPoints.every((p) => { + if (p.type == InspectionPointEnum.file) { + return getValueToInspectionPoint(p.id); + } else if (p.type == InspectionPointEnum.oknok) { + let value = getValueToInspectionPoint(p.id); + return (["true", "false"].includes(value) ? (value as "true" | "false") : "") != ""; + } else { + return !!getValueToInspectionPoint(p.id); + } + }); + if (!everythingFilled) throw new ForbiddenRequestException("fill out every field before finishing inspection"); + + let formattedInspection = InspectionFactory.mapToSingle(inspection); + let title = `Prüf-Ausdruck_${[formattedInspection.related.code ?? "", formattedInspection.related.name].join("_")}_${ + formattedInspection.inspectionPlan.title + }_${new Date(formattedInspection.finished ?? "").toLocaleDateString("de-de")}`; + + let inspectionPoints = []; + for (const ip of formattedInspection.inspectionVersionedPlan.inspectionPoints.sort( + (a, b) => (a.sort ?? 0) - (b.sort ?? 0) + )) { + let value = formattedInspection.checks.find((c) => c.inspectionPointId == ip.id).value; + let image = ""; + if (ip.type == InspectionPointEnum.file && ip.others == "img") { + const imagePath = FileSystemHelper.formatPath("inspection", inspection.id, value); + let pngImageBytes = await sharp(imagePath).png().toBuffer(); + image = `data:image/png;base64,${pngImageBytes.toString("base64")}`; + } else if (ip.type == InspectionPointEnum.oknok) { + value = value ? "OK" : "Nicht OK"; + } + inspectionPoints.push({ + title: ip.title, + description: ip.description, + type: ip.type, + min: ip.min, + max: ip.max, + others: ip.others, + value: value, + image: image, + }); + } + + let pdf = await PdfExport.renderFile({ + template: "inspection", + title, + saveToDisk: false, + data: { + inspector: `${req.lastname}, ${req.firstname}`, + context: formattedInspection.context || "---", + createdAt: formattedInspection.created, + finishedAt: formattedInspection.finished ?? new Date(), + nextInspection: formattedInspection.nextInspection, + related: formattedInspection.related, + plan: formattedInspection.inspectionPlan, + planVersion: formattedInspection.inspectionVersionedPlan.version, + planTitle: formattedInspection.inspectionPlan.title, + checks: inspectionPoints, + }, + }); + + const finalDocument = await PDFDocument.create(); + const printout = await PDFDocument.load(pdf); + const copiedPages = await finalDocument.copyPages(printout, printout.getPageIndices()); + copiedPages.forEach((page) => finalDocument.addPage(page)); + + let resultsForAppend = inspectionPoints.filter((ip) => ip.type == InspectionPointEnum.file && ip.others == "pdf"); + + if (resultsForAppend.length !== 0) { + const appendixPage = finalDocument.addPage(); + const { width, height } = appendixPage.getSize(); + appendixPage.drawText("Anhang:", { + x: 50, + y: height - 50, + size: 24, + }); + } + + for (const appendix of resultsForAppend) { + const appendixPdfBytes = FileSystemHelper.readFileAsBase64("inspection", inspection.id, appendix.value); + const appendixPdf = await PDFDocument.load(appendixPdfBytes); + const appendixPages = await finalDocument.copyPages(appendixPdf, appendixPdf.getPageIndices()); + appendixPages.forEach((page) => finalDocument.addPage(page)); + + /** print image + const imagePath = FileSystemHelper.formatPath("inspection", inspection.id, checkValue); + let pngImageBytes = await sharp(imagePath).png().toBuffer(); + let image = await finalDocument.embedPng(pngImageBytes); + let dims = image.scale(1); + if (image) { + const page = finalDocument.addPage(); + const { width, height } = page.getSize(); + const x = (width - dims.width) / 2; + const y = (height - dims.height) / 2; + page.drawImage(image, { + x, + y, + width: dims.width, + height: dims.height, + }); + } + */ + } + + const mergedPdfBytes = await finalDocument.save(); + FileSystemHelper.writeFile(`inspection/${inspection.id}`, `printout.pdf`, mergedPdfBytes); + + let finish: FinishInspectionCommand = { + id: inspectionId, + }; + await InspectionCommandHandler.finish(finish); + + res.sendStatus(204); +} + +/** + * @description delete inspection by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteInspectionById(req: Request, res: Response): Promise { + const inspectionId = req.params.id; + + let deleteInspectionData = await InspectionService.getById(inspectionId); + if (deleteInspectionData.finishedAt != null) { + throw new ForbiddenRequestException("Cannot delete as inspection is already finished"); + } + + let deleteInspection: DeleteInspectionCommand = { + id: inspectionId, + }; + await InspectionCommandHandler.delete(deleteInspection); + + res.sendStatus(204); +} diff --git a/src/controller/admin/unit/inspectionPlanController.ts b/src/controller/admin/unit/inspectionPlanController.ts new file mode 100644 index 0000000..1165485 --- /dev/null +++ b/src/controller/admin/unit/inspectionPlanController.ts @@ -0,0 +1,214 @@ +import { Request, Response } from "express"; +import InspectionPlanService from "../../../service/unit/inspection/inspectionPlanService"; +import InspectionPlanFactory from "../../../factory/admin/unit/inspection/inspectionPlan"; +import { + CreateInspectionPlanCommand, + DeleteInspectionPlanCommand, + UpdateInspectionPlanCommand, +} from "../../../command/unit/inspection/inspectionPlanCommand"; +import InspectionPlanCommandHandler from "../../../command/unit/inspection/inspectionPlanCommandHandler"; +import BadRequestException from "../../../exceptions/badRequestException"; +import TypeTester from "../../../helpers/typeTester"; +import InspectionPointService from "../../../service/unit/inspection/inspectionPointService"; +import InspectionPointFactory from "../../../factory/admin/unit/inspection/inspectionPoint"; +import InspectionService from "../../../service/unit/inspection/inspectionService"; +import InspectionVersionedPlanCommandHandler from "../../../command/unit/inspection/inspectionVersionedPlanCommandHandler"; +import InspectionPointCommandHandler from "../../../command/unit/inspection/inspectionPointCommandHandler"; + +/** + * @description get all inspectionPlans + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllInspectionPlans(req: Request, res: Response): Promise { + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let search = (req.query.search as string) ?? ""; + let noLimit = req.query.noLimit === "true"; + let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); + + let [inspectionPlans, total] = await InspectionPlanService.getAll({ + offset, + count, + search, + noLimit, + ids, + }); + + res.json({ + inspectionPlans: InspectionPlanFactory.mapToBase(inspectionPlans), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get all inspectionPlans + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllInspectionPlansForRelated(req: Request, res: Response): Promise { + let relation = req.params.related as "vehicleType" | "equipmentType" | "wearableType"; + let relationTypeId = req.params.relatedTypeId as string; + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let search = (req.query.search as string) ?? ""; + let noLimit = req.query.noLimit === "true"; + let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); + + let where; + if (relation == "equipmentType") { + where = { equipmentTypeId: relationTypeId }; + } else if (relation == "vehicleType") { + where = { vehicleTypeId: relationTypeId }; + } else { + where = { wearableTypeId: relationTypeId }; + } + let [inspectionPlans, total] = await InspectionPlanService.getAllForRelated(where, { + offset, + count, + search, + noLimit, + ids, + }); + + res.json({ + inspectionPlans: inspectionPlans, + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get inspectionPoints by planid + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getInspectionPointsByPlanId(req: Request, res: Response): Promise { + const inspectionPlanId = req.params.id; + let inspectionPlan = await InspectionPlanService.getById(inspectionPlanId); + + if (!inspectionPlan.latestVersionedPlan) { + res.json([]); + } else { + let inspectionPoints = await InspectionPointService.getAllForVersionedPlan(inspectionPlan.latestVersionedPlan.id); + + res.json(InspectionPointFactory.mapToBase(inspectionPoints)); + } +} + +/** + * @description get inspectionPlan by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getInspectionPlanById(req: Request, res: Response): Promise { + const inspectionPlanId = req.params.id; + let inspectionPlan = await InspectionPlanService.getById(inspectionPlanId); + + res.json(InspectionPlanFactory.mapToSingle(inspectionPlan)); +} + +/** + * @description create inspectionPlan + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createInspectionPlan(req: Request, res: Response): Promise { + const title = req.body.title; + const inspectionInterval = req.body.inspectionInterval; + const remindTime = req.body.remindTime; + const relatedId = req.body.relatedId; + const assigned = req.body.assigned; + + TypeTester.testPlanTimeDefinition(inspectionInterval, "inspectionInterval", true); + TypeTester.testPlanTimeDefinition(remindTime, "remindTime", true); + + if (assigned != "equipment" && assigned != "vehicle" && assigned != "wearable") + throw new BadRequestException("set assigned to equipment or vehicle or wearable"); + + let createInspectionPlan: CreateInspectionPlanCommand = { + title, + inspectionInterval, + remindTime, + relatedId, + assigned, + }; + let inspectionPlanId = await InspectionPlanCommandHandler.create(createInspectionPlan); + + res.status(200).send(inspectionPlanId); +} + +/** + * @description update inspectionPlan by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateInspectionPlanById(req: Request, res: Response): Promise { + const inspectionPlanId = req.params.id; + const title = req.body.title; + const inspectionInterval = req.body.inspectionInterval; + const remindTime = req.body.remindTime; + + TypeTester.testPlanTimeDefinition(inspectionInterval, "inspectionInterval", true); + TypeTester.testPlanTimeDefinition(remindTime, "remindTime", true); + + let updateInspectionPlan: UpdateInspectionPlanCommand = { + id: inspectionPlanId, + title, + inspectionInterval, + remindTime, + }; + await InspectionPlanCommandHandler.update(updateInspectionPlan); + + res.sendStatus(204); +} + +/** + * @description get inspectionPoints by planid + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateInspectionPointsByPlanId(req: Request, res: Response): Promise { + const inspectionPlanId = req.params.id; + const inspectionPoints = req.body; + let inspectionPlan = await InspectionPlanService.getById(inspectionPlanId); + + let usedVersionedPlan = inspectionPlan?.latestVersionedPlan?.id + ? await InspectionService.usesVersionedInspectionPlan(inspectionPlan.latestVersionedPlan.id) + : true; + + if (usedVersionedPlan) { + await InspectionVersionedPlanCommandHandler.create({ inspectionPlanId }, inspectionPoints); + } else { + await InspectionPointCommandHandler.sync(inspectionPlan.latestVersionedPlan.id, inspectionPoints); + } + + res.sendStatus(204); +} + +/** + * @description delete inspectionPlan by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteInspectionPlanById(req: Request, res: Response): Promise { + const inspectionPlanId = req.params.id; + + let deleteInspectionPlan: DeleteInspectionPlanCommand = { + id: inspectionPlanId, + }; + await InspectionPlanCommandHandler.delete(deleteInspectionPlan); + + res.sendStatus(204); +} diff --git a/src/controller/admin/unit/maintenanceController.ts b/src/controller/admin/unit/maintenanceController.ts new file mode 100644 index 0000000..78c6a7b --- /dev/null +++ b/src/controller/admin/unit/maintenanceController.ts @@ -0,0 +1,119 @@ +import { Request, Response } from "express"; +import MaintenanceService from "../../../service/unit/maintenanceService"; +import MaintenanceFactory from "../../../factory/admin/unit/maintenance"; +import { CreateMaintenanceCommand, UpdateMaintenanceCommand } from "../../../command/unit/maintenanceCommand"; +import MaintenanceCommandHandler from "../../../command/unit/maintenanceCommandHandler"; +import BadRequestException from "../../../exceptions/badRequestException"; + +/** + * @description get all maintenances by status + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllMaintenancesByStatus(req: Request, res: Response): Promise { + let done = req.query.done === "true"; + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let noLimit = req.query.noLimit === "true"; + + let [maintenances, total] = await MaintenanceService.getAll(done, { offset, count, noLimit }); + + res.json({ + maintenances: MaintenanceFactory.mapToBase(maintenances), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get all maintenances for related id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllMaintenancesForRelated(req: Request, res: Response): Promise { + let relation = req.params.related as "vehicle" | "equipment" | "wearable"; + let relationId = req.params.relatedId as string; + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let noLimit = req.query.noLimit === "true"; + + let where; + if (relation == "equipment") { + where = { equipmentId: relationId }; + } else if (relation == "vehicle") { + where = { vehicleId: relationId }; + } else { + where = { wearableId: relationId }; + } + let [maintenances, total] = await MaintenanceService.getAllForRelated(where, { offset, count, noLimit }); + + res.json({ + maintenances: MaintenanceFactory.mapToBase(maintenances), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get maintenance by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getMaintenanceById(req: Request, res: Response): Promise { + const maintenanceId = req.params.id; + let maintenance = await MaintenanceService.getById(maintenanceId); + + res.json(MaintenanceFactory.mapToSingle(maintenance)); +} + +/** + * @description create maintenance + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createMaintenance(req: Request, res: Response): Promise { + const description = req.body.description; + const affectedId = req.body.affectedId; + const affected = req.body.affected; + + if (affected != "equipment" && affected != "vehicle" && affected != "wearable") + throw new BadRequestException("set assigned to equipment or vehicle or wearable"); + + let createMaintenance: CreateMaintenanceCommand = { + description, + affectedId, + affected, + }; + let maintenanceId = await MaintenanceCommandHandler.create(createMaintenance); + + res.status(200).send(maintenanceId); +} + +/** + * @description update maintenance by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateMaintenanceById(req: Request, res: Response): Promise { + const maintenanceId = req.params.id; + const description = req.body.description; + const status = req.body.status; + const done = req.body.done; + + let updateMaintenance: UpdateMaintenanceCommand = { + id: maintenanceId, + description, + status, + done, + }; + await MaintenanceCommandHandler.update(updateMaintenance); + + res.sendStatus(204); +} diff --git a/src/controller/admin/unit/vehicleController.ts b/src/controller/admin/unit/vehicleController.ts new file mode 100644 index 0000000..a54d2c1 --- /dev/null +++ b/src/controller/admin/unit/vehicleController.ts @@ -0,0 +1,133 @@ +import { Request, Response } from "express"; +import VehicleService from "../../../service/unit/vehicle/vehicleService"; +import VehicleFactory from "../../../factory/admin/unit/vehicle/vehicle"; +import { + CreateVehicleCommand, + DeleteVehicleCommand, + UpdateVehicleCommand, +} from "../../../command/unit/vehicle/vehicleCommand"; +import VehicleCommandHandler from "../../../command/unit/vehicle/vehicleCommandHandler"; + +/** + * @description get all vehicles + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllVehicles(req: Request, res: Response): Promise { + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let search = (req.query.search as string) ?? ""; + let noLimit = req.query.noLimit === "true"; + let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); + + let [vehicles, total] = await VehicleService.getAll({ offset, count, search, noLimit, ids }); + + res.json({ + vehicles: VehicleFactory.mapToBase(vehicles), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get vehicle by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getVehicleById(req: Request, res: Response): Promise { + const vehicleId = req.params.id; + let vehicle = await VehicleService.getById(vehicleId); + + res.json(VehicleFactory.mapToSingle(vehicle)); +} + +/** + * @description get vehicle by Ids + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getVehiclesByIds(req: Request, res: Response): Promise { + let ids = req.body.ids as Array; + + let [vehicles, total] = await VehicleService.getAll({ noLimit: true, ids }); + + res.json({ + vehicles: VehicleFactory.mapToBase(vehicles), + total: total, + offset: 0, + count: total, + }); +} + +/** + * @description create vehicle + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createVehicle(req: Request, res: Response): Promise { + const name = req.body.name; + const code = req.body.code || null; + const location = req.body.location; + const commissioned = req.body.commissioned; + const vehicleTypeId = req.body.vehicleTypeId; + + let createVehicle: CreateVehicleCommand = { + code, + name, + location, + commissioned, + vehicleTypeId, + }; + let vehicleId = await VehicleCommandHandler.create(createVehicle); + + res.status(200).send(vehicleId); +} + +/** + * @description update vehicle by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateVehicleById(req: Request, res: Response): Promise { + const vehicleId = req.params.id; + const name = req.body.name; + const code = req.body.code || null; + const location = req.body.location; + const commissioned = req.body.commissioned; + const decommissioned = req.body.decommissioned || null; + + let updateVehicle: UpdateVehicleCommand = { + id: vehicleId, + code, + name, + location, + commissioned, + decommissioned, + }; + await VehicleCommandHandler.update(updateVehicle); + + res.sendStatus(204); +} + +/** + * @description delete vehicle by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteVehicleById(req: Request, res: Response): Promise { + const vehicleId = req.params.id; + + let deleteVehicle: DeleteVehicleCommand = { + id: vehicleId, + }; + await VehicleCommandHandler.delete(deleteVehicle); + + res.sendStatus(204); +} diff --git a/src/controller/admin/unit/vehicleTypeController.ts b/src/controller/admin/unit/vehicleTypeController.ts new file mode 100644 index 0000000..b105ea1 --- /dev/null +++ b/src/controller/admin/unit/vehicleTypeController.ts @@ -0,0 +1,101 @@ +import { Request, Response } from "express"; +import VehicleTypeService from "../../../service/unit/vehicle/vehicleTypeService"; +import VehicleTypeFactory from "../../../factory/admin/unit/vehicle/vehicleType"; +import { + CreateVehicleTypeCommand, + DeleteVehicleTypeCommand, + UpdateVehicleTypeCommand, +} from "../../../command/unit/vehicle/vehicleTypeCommand"; +import VehicleTypeCommandHandler from "../../../command/unit/vehicle/vehicleTypeCommandHandler"; + +/** + * @description get all vehicleTypes + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllVehicleTypes(req: Request, res: Response): Promise { + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let search = (req.query.search as string) ?? ""; + let noLimit = req.query.noLimit === "true"; + + let [vehicleTypes, total] = await VehicleTypeService.getAll({ offset, count, search, noLimit }); + + res.json({ + vehicleTypes: VehicleTypeFactory.mapToBase(vehicleTypes), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get vehicleType by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getVehicleTypeById(req: Request, res: Response): Promise { + const vehicleTypeId = req.params.id; + let vehicleType = await VehicleTypeService.getById(vehicleTypeId); + + res.json(VehicleTypeFactory.mapToSingle(vehicleType)); +} + +/** + * @description create vehicleType + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createVehicleType(req: Request, res: Response): Promise { + const type = req.body.type; + const description = req.body.description; + + let createVehicleType: CreateVehicleTypeCommand = { + type, + description, + }; + let vehicleTypeId = await VehicleTypeCommandHandler.create(createVehicleType); + + res.status(200).send(vehicleTypeId); +} + +/** + * @description update vehicleType by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateVehicleTypeById(req: Request, res: Response): Promise { + const vehicleTypeId = req.params.id; + const type = req.body.type; + const description = req.body.description; + + let updateVehicleType: UpdateVehicleTypeCommand = { + id: vehicleTypeId, + type, + description, + }; + await VehicleTypeCommandHandler.update(updateVehicleType); + + res.sendStatus(204); +} + +/** + * @description delete vehicleType by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteVehicleTypeById(req: Request, res: Response): Promise { + const vehicleTypeId = req.params.id; + + let deleteVehicleType: DeleteVehicleTypeCommand = { + id: vehicleTypeId, + }; + await VehicleTypeCommandHandler.delete(deleteVehicleType); + + res.sendStatus(204); +} diff --git a/src/controller/admin/unit/wearableController.ts b/src/controller/admin/unit/wearableController.ts new file mode 100644 index 0000000..b28fb46 --- /dev/null +++ b/src/controller/admin/unit/wearableController.ts @@ -0,0 +1,137 @@ +import { Request, Response } from "express"; +import WearableService from "../../../service/unit/wearable/wearableService"; +import WearableFactory from "../../../factory/admin/unit/wearable/wearable"; +import { + CreateWearableCommand, + DeleteWearableCommand, + UpdateWearableCommand, +} from "../../../command/unit/wearable/wearableCommand"; +import WearableCommandHandler from "../../../command/unit/wearable/wearableCommandHandler"; + +/** + * @description get all wearables + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllWearables(req: Request, res: Response): Promise { + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let search = (req.query.search as string) ?? ""; + let noLimit = req.query.noLimit === "true"; + let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); + + let [wearables, total] = await WearableService.getAll({ offset, count, search, noLimit, ids }); + + res.json({ + wearables: WearableFactory.mapToBase(wearables), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get wearable by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getWearableById(req: Request, res: Response): Promise { + const wearableId = req.params.id; + let wearable = await WearableService.getById(wearableId); + + res.json(WearableFactory.mapToSingle(wearable)); +} + +/** + * @description get wearable by Ids + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getWearablesByIds(req: Request, res: Response): Promise { + let ids = req.body.ids as Array; + + let [wearables, total] = await WearableService.getAll({ noLimit: true, ids }); + + res.json({ + wearables: WearableFactory.mapToBase(wearables), + total: total, + offset: 0, + count: total, + }); +} + +/** + * @description create wearable + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createWearable(req: Request, res: Response): Promise { + const name = req.body.name; + const code = req.body.code || null; + const location = req.body.location; + const commissioned = req.body.commissioned; + const wearableTypeId = req.body.wearableTypeId; + const wearerId = req.body.wearerId || null; + + let createWearable: CreateWearableCommand = { + code, + name, + location, + commissioned, + wearableTypeId, + wearerId, + }; + let wearableId = await WearableCommandHandler.create(createWearable); + + res.status(200).send(wearableId); +} + +/** + * @description update wearable by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateWearableById(req: Request, res: Response): Promise { + const wearableId = req.params.id; + const name = req.body.name; + const code = req.body.code || null; + const location = req.body.location; + const commissioned = req.body.commissioned; + const decommissioned = req.body.decommissioned || null; + const wearerId = req.body.wearerId || null; + + let updateWearable: UpdateWearableCommand = { + id: wearableId, + code, + name, + location, + commissioned, + decommissioned, + wearerId, + }; + await WearableCommandHandler.update(updateWearable); + + res.sendStatus(204); +} + +/** + * @description delete wearable by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteWearableById(req: Request, res: Response): Promise { + const wearableId = req.params.id; + + let deleteWearable: DeleteWearableCommand = { + id: wearableId, + }; + await WearableCommandHandler.delete(deleteWearable); + + res.sendStatus(204); +} diff --git a/src/controller/admin/unit/wearableTypeController.ts b/src/controller/admin/unit/wearableTypeController.ts new file mode 100644 index 0000000..8597916 --- /dev/null +++ b/src/controller/admin/unit/wearableTypeController.ts @@ -0,0 +1,101 @@ +import { Request, Response } from "express"; +import WearableTypeService from "../../../service/unit/wearable/wearableTypeService"; +import WearableTypeFactory from "../../../factory/admin/unit/wearable/wearableType"; +import { + CreateWearableTypeCommand, + DeleteWearableTypeCommand, + UpdateWearableTypeCommand, +} from "../../../command/unit/wearable/wearableTypeCommand"; +import WearableTypeCommandHandler from "../../../command/unit/wearable/wearableTypeCommandHandler"; + +/** + * @description get all wearableTypes + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllWearableTypes(req: Request, res: Response): Promise { + let offset = parseInt((req.query.offset as string) ?? "0"); + let count = parseInt((req.query.count as string) ?? "25"); + let search = (req.query.search as string) ?? ""; + let noLimit = req.query.noLimit === "true"; + + let [wearableTypes, total] = await WearableTypeService.getAll({ offset, count, search, noLimit }); + + res.json({ + wearableTypes: WearableTypeFactory.mapToBase(wearableTypes), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get wearableType by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getWearableTypeById(req: Request, res: Response): Promise { + const wearableTypeId = req.params.id; + let wearableType = await WearableTypeService.getById(wearableTypeId); + + res.json(WearableTypeFactory.mapToSingle(wearableType)); +} + +/** + * @description create wearableType + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createWearableType(req: Request, res: Response): Promise { + const type = req.body.type; + const description = req.body.description; + + let createWearableType: CreateWearableTypeCommand = { + type, + description, + }; + let wearableTypeId = await WearableTypeCommandHandler.create(createWearableType); + + res.status(200).send(wearableTypeId); +} + +/** + * @description update wearableType by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateWearableTypeById(req: Request, res: Response): Promise { + const wearableTypeId = req.params.id; + const type = req.body.type; + const description = req.body.description; + + let updateWearableType: UpdateWearableTypeCommand = { + id: wearableTypeId, + type, + description, + }; + await WearableTypeCommandHandler.update(updateWearableType); + + res.sendStatus(204); +} + +/** + * @description delete wearableType by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteWearableTypeById(req: Request, res: Response): Promise { + const wearableTypeId = req.params.id; + + let deleteWearableType: DeleteWearableTypeCommand = { + id: wearableTypeId, + }; + await WearableTypeCommandHandler.delete(deleteWearableType); + + res.sendStatus(204); +} diff --git a/src/data-source.ts b/src/data-source.ts index ed56dad..67e15d5 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -20,6 +20,7 @@ import { member } from "./entity/club/member/member"; import { memberAwards } from "./entity/club/member/memberAwards"; import { memberExecutivePositions } from "./entity/club/member/memberExecutivePositions"; import { memberQualifications } from "./entity/club/member/memberQualifications"; +import { memberEducations } from "./entity/club/member/memberEducations"; import { membership } from "./entity/club/member/membership"; import { protocol } from "./entity/club/protocol/protocol"; import { protocolAgenda } from "./entity/club/protocol/protocolAgenda"; @@ -46,10 +47,24 @@ import { webapiPermission } from "./entity/management/webapi_permission"; import { salutation } from "./entity/configuration/salutation"; import { setting } from "./entity/management/setting"; import { education } from "./entity/configuration/education"; -import { memberEducations } from "./entity/club/member/memberEducations"; + +import { equipmentType } from "./entity/unit/equipment/equipmentType"; +import { equipment } from "./entity/unit/equipment/equipment"; +import { vehicleType } from "./entity/unit/vehicle/vehicleType"; +import { vehicle } from "./entity/unit/vehicle/vehicle"; +import { wearableType } from "./entity/unit/wearable/wearableType"; +import { wearable } from "./entity/unit/wearable/wearable"; +import { damageReport } from "./entity/unit/damageReport"; +import { inspectionPlan } from "./entity/unit/inspection/inspectionPlan"; +import { inspectionVersionedPlan } from "./entity/unit/inspection/inspectionVersionedPlan"; +import { inspectionPoint } from "./entity/unit/inspection/inspectionPoint"; +import { inspection } from "./entity/unit/inspection/inspection"; +import { inspectionPointResult } from "./entity/unit/inspection/inspectionPointResult"; +import { maintenance } from "./entity/unit/maintenance"; import { BackupAndResetDatabase1749296262915 } from "./migrations/1749296262915-BackupAndResetDatabase"; import { CreateSchema1749296280721 } from "./migrations/1749296280721-CreateSchema"; +import { UnitBase1749361405703 } from "./migrations/1749361405703-UnitBase"; configCheck(); @@ -107,8 +122,21 @@ const dataSource = new DataSource({ webapi, webapiPermission, setting, + equipmentType, + equipment, + vehicleType, + vehicle, + wearableType, + wearable, + damageReport, + maintenance, + inspectionPlan, + inspectionVersionedPlan, + inspectionPoint, + inspection, + inspectionPointResult, ], - migrations: [BackupAndResetDatabase1749296262915, CreateSchema1749296280721], + migrations: [BackupAndResetDatabase1749296262915, CreateSchema1749296280721, UnitBase1749361405703], migrationsRun: true, migrationsTransactionMode: "each", subscribers: [], diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts new file mode 100644 index 0000000..0a4fd7e --- /dev/null +++ b/src/entity/unit/damageReport.ts @@ -0,0 +1,69 @@ +import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; +import { equipment } from "./equipment/equipment"; +import { wearable } from "./wearable/wearable"; +import { vehicle } from "./vehicle/vehicle"; +import { maintenance } from "./maintenance"; + +@Entity() +export class damageReport { + @PrimaryGeneratedColumn("uuid") + id: string; + + @CreateDateColumn() + reportedAt: Date; + + @Column({ type: "varchar", length: 255 }) + status: string; + + @Column({ type: "boolean", default: false }) + done: boolean; + + @Column({ type: "text" }) + description: string; + + @Column({ type: "varchar", length: 255 }) + reportedBy: string; + + @Column({ type: "int", default: 0 }) + imageCount: number; + + @Column({ nullable: true, default: null }) + equipmentId?: string; + + @Column({ nullable: true, default: null }) + vehicleId?: string; + + @Column({ nullable: true, default: null }) + wearableId?: string; + + @Column({ nullable: true, default: null }) + maintenanceId?: string; + + @ManyToOne(() => equipment, (e) => e.reports, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + equipment?: equipment; + + @ManyToOne(() => vehicle, (v) => v.reports, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + vehicle?: vehicle; + + @ManyToOne(() => wearable, (w) => w.reports, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + wearable?: wearable; + + @ManyToOne(() => maintenance, (m) => m.reports, { + nullable: true, + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }) + maintenance?: maintenance; +} diff --git a/src/entity/unit/equipment/equipment.ts b/src/entity/unit/equipment/equipment.ts new file mode 100644 index 0000000..2d3b022 --- /dev/null +++ b/src/entity/unit/equipment/equipment.ts @@ -0,0 +1,46 @@ +import { Column, ColumnType, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { getTypeByORM } from "../../../migrations/ormHelper"; +import { equipmentType } from "./equipmentType"; +import { damageReport } from "../damageReport"; +import { inspection } from "../inspection/inspection"; +import { maintenance } from "../maintenance"; + +@Entity() +export class equipment { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255, nullable: true, default: null, unique: true }) + code?: string; + + @Column({ type: "varchar", length: 255 }) + name: string; + + @Column({ type: "varchar", length: 255 }) + location: string; + + @Column({ type: getTypeByORM("date").type as ColumnType }) + commissioned: Date; + + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) + decommissioned?: Date; + + @Column() + equipmentTypeId: string; + + @ManyToOne(() => equipmentType, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) + equipmentType: equipmentType; + + @OneToMany(() => damageReport, (d) => d.equipment, { cascade: ["insert"] }) + reports: damageReport[]; + + @OneToMany(() => maintenance, (m) => m.equipment, { cascade: ["insert"] }) + maintenances: maintenance[]; + + @OneToMany(() => inspection, (i) => i.equipment, { cascade: ["insert"] }) + inspections: inspection[]; +} diff --git a/src/entity/unit/equipment/equipmentType.ts b/src/entity/unit/equipment/equipmentType.ts new file mode 100644 index 0000000..40c363b --- /dev/null +++ b/src/entity/unit/equipment/equipmentType.ts @@ -0,0 +1,23 @@ +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { equipment } from "./equipment"; +import { inspectionPlan } from "../inspection/inspectionPlan"; + +@Entity() +export class equipmentType { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255, unique: true }) + type: string; + + @Column({ type: "text", nullable: true }) + description: string; + + @OneToMany(() => equipment, (e) => e.equipmentType, { cascade: ["insert"] }) + equipment: equipment[]; + + @OneToMany(() => inspectionPlan, (ip) => ip.equipmentType, { cascade: ["insert"] }) + inspectionPlans: inspectionPlan[]; + + equipmentCount: number; +} diff --git a/src/entity/unit/inspection/inspection.ts b/src/entity/unit/inspection/inspection.ts new file mode 100644 index 0000000..eff01ec --- /dev/null +++ b/src/entity/unit/inspection/inspection.ts @@ -0,0 +1,82 @@ +import { Column, ColumnType, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { inspectionPlan } from "./inspectionPlan"; +import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; +import { getTypeByORM } from "../../../migrations/ormHelper"; +import { vehicle } from "../vehicle/vehicle"; +import { equipment } from "../equipment/equipment"; +import { inspectionPointResult } from "./inspectionPointResult"; +import { wearable } from "../wearable/wearable"; + +@Entity() +export class inspection { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "text" }) + context: string; + + @CreateDateColumn() + createdAt: Date; + + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) + finishedAt?: Date; + + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) + nextInspection?: Date; + + @Column({ type: "boolean", default: false }) + hasNewer: boolean; + + @Column() + inspectionPlanId: string; + + @Column() + inspectionVersionedPlanId: string; + + @Column({ nullable: true, default: null }) + equipmentId?: string; + + @Column({ nullable: true, default: null }) + vehicleId?: string; + + @Column({ nullable: true, default: null }) + wearableId?: string; + + @ManyToOne(() => inspectionPlan, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) + inspectionPlan: inspectionPlan; + + @ManyToOne(() => inspectionVersionedPlan, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) + inspectionVersionedPlan: inspectionVersionedPlan; + + @ManyToOne(() => equipment, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + equipment: equipment; + + @ManyToOne(() => vehicle, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + vehicle: vehicle; + + @ManyToOne(() => wearable, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + wearable: wearable; + + @OneToMany(() => inspectionPointResult, (ipr) => ipr.inspection, { cascade: ["insert"] }) + pointResults: inspectionPointResult[]; +} diff --git a/src/entity/unit/inspection/inspectionPlan.ts b/src/entity/unit/inspection/inspectionPlan.ts new file mode 100644 index 0000000..9fa6fad --- /dev/null +++ b/src/entity/unit/inspection/inspectionPlan.ts @@ -0,0 +1,61 @@ +import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { PlanTimeDefinition } from "../../../viewmodel/admin/unit/inspection/inspectionPlan.models"; +import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; +import { equipmentType } from "../equipment/equipmentType"; +import { vehicleType } from "../vehicle/vehicleType"; +import { wearableType } from "../wearable/wearableType"; + +@Entity() +export class inspectionPlan { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255 }) + title: string; + + @Column({ type: "varchar", length: 255 }) + inspectionInterval: PlanTimeDefinition; + + @Column({ type: "varchar", length: 255 }) + remindTime: PlanTimeDefinition; + + @CreateDateColumn() + createdAt: Date; + + @Column({ nullable: true, default: null }) + equipmentTypeId?: string; + + @Column({ nullable: true, default: null }) + vehicleTypeId?: string; + + @Column({ nullable: true, default: null }) + wearableTypeId?: string; + + @ManyToOne(() => equipmentType, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + equipmentType?: equipmentType; + + @ManyToOne(() => vehicleType, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + vehicleType?: vehicleType; + + @ManyToOne(() => wearableType, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + wearableType?: wearableType; + + @OneToMany(() => inspectionVersionedPlan, (ivp) => ivp.inspectionPlan, { + cascade: ["insert"], + }) + versionedPlans: inspectionVersionedPlan[]; + + latestVersionedPlan?: inspectionVersionedPlan; +} diff --git a/src/entity/unit/inspection/inspectionPoint.ts b/src/entity/unit/inspection/inspectionPoint.ts new file mode 100644 index 0000000..84c0a80 --- /dev/null +++ b/src/entity/unit/inspection/inspectionPoint.ts @@ -0,0 +1,51 @@ +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, Unique } from "typeorm"; +import { InspectionPointEnum } from "../../../enums/inspectionEnum"; +import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; + +@Entity() +export class inspectionPoint { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255 }) + title: string; + + @Column({ type: "text" }) + description: string; + + @Column({ + type: "varchar", + length: 255, + transformer: { + to(value: InspectionPointEnum) { + return value.toString(); + }, + from(value: string) { + return InspectionPointEnum[value as keyof typeof InspectionPointEnum]; + }, + }, + }) + type: InspectionPointEnum; + + @Column({ type: "int", nullable: true, default: null }) + min?: number; + + @Column({ type: "int", nullable: true, default: null }) + max?: number; + + @Column({ type: "varchar", length: 255, default: null }) + others?: string; + + @Column({ type: "int", default: 0 }) + sort: number; + + @Column() + versionedPlanId: string; + + @ManyToOne(() => inspectionVersionedPlan, { + nullable: false, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + versionedPlan: inspectionVersionedPlan; +} diff --git a/src/entity/unit/inspection/inspectionPointResult.ts b/src/entity/unit/inspection/inspectionPointResult.ts new file mode 100644 index 0000000..4f98e74 --- /dev/null +++ b/src/entity/unit/inspection/inspectionPointResult.ts @@ -0,0 +1,29 @@ +import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm"; +import { inspection } from "./inspection"; +import { inspectionPoint } from "./inspectionPoint"; + +@Entity() +export class inspectionPointResult { + @PrimaryColumn() + inspectionId: string; + + @PrimaryColumn() + inspectionPointId: string; + + @Column({ type: "text" }) + value: string; + + @ManyToOne(() => inspection, { + nullable: false, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + inspection: inspection; + + @ManyToOne(() => inspectionPoint, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) + inspectionPoint: inspectionPoint; +} diff --git a/src/entity/unit/inspection/inspectionVersionedPlan.ts b/src/entity/unit/inspection/inspectionVersionedPlan.ts new file mode 100644 index 0000000..59d8d4a --- /dev/null +++ b/src/entity/unit/inspection/inspectionVersionedPlan.ts @@ -0,0 +1,31 @@ +import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, Unique } from "typeorm"; +import { inspectionPlan } from "./inspectionPlan"; +import { inspectionPoint } from "./inspectionPoint"; + +@Entity() +@Unique("unique_version", ["version", "inspectionPlanId"]) +export class inspectionVersionedPlan { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "int", default: 0 }) + version: number; + + @CreateDateColumn() + createdAt: Date; + + @Column() + inspectionPlanId: string; + + @ManyToOne(() => inspectionPlan, { + nullable: false, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + inspectionPlan: inspectionPlan; + + @OneToMany(() => inspectionPoint, (ip) => ip.versionedPlan, { + cascade: ["insert"], + }) + inspectionPoints: inspectionPoint[]; +} diff --git a/src/entity/unit/maintenance.ts b/src/entity/unit/maintenance.ts new file mode 100644 index 0000000..3c9b7c0 --- /dev/null +++ b/src/entity/unit/maintenance.ts @@ -0,0 +1,56 @@ +import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { equipment } from "./equipment/equipment"; +import { wearable } from "./wearable/wearable"; +import { vehicle } from "./vehicle/vehicle"; +import { damageReport } from "./damageReport"; + +@Entity() +export class maintenance { + @PrimaryGeneratedColumn("uuid") + id: string; + + @CreateDateColumn() + createdAt: Date; + + @Column({ type: "varchar", length: 255 }) + status: string; + + @Column({ type: "boolean", default: false }) + done: boolean; + + @Column({ type: "text" }) + description: string; + + @Column({ nullable: true, default: null }) + equipmentId?: string; + + @Column({ nullable: true, default: null }) + vehicleId?: string; + + @Column({ nullable: true, default: null }) + wearableId?: string; + + @ManyToOne(() => equipment, (e) => e.maintenances, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + equipment?: equipment; + + @ManyToOne(() => vehicle, (v) => v.maintenances, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + vehicle?: vehicle; + + @ManyToOne(() => wearable, (w) => w.maintenances, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + wearable?: wearable; + + @OneToMany(() => damageReport, (dr) => dr.maintenance) + reports: damageReport[]; +} diff --git a/src/entity/unit/vehicle/vehicle.ts b/src/entity/unit/vehicle/vehicle.ts new file mode 100644 index 0000000..3f75651 --- /dev/null +++ b/src/entity/unit/vehicle/vehicle.ts @@ -0,0 +1,46 @@ +import { Column, ColumnType, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { getTypeByORM } from "../../../migrations/ormHelper"; +import { vehicleType } from "./vehicleType"; +import { damageReport } from "../damageReport"; +import { inspection } from "../inspection/inspection"; +import { maintenance } from "../maintenance"; + +@Entity() +export class vehicle { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255, nullable: true, default: null, unique: true }) + code?: string; + + @Column({ type: "varchar", length: 255 }) + name: string; + + @Column({ type: "varchar", length: 255 }) + location: string; + + @Column({ type: getTypeByORM("date").type as ColumnType }) + commissioned: Date; + + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) + decommissioned?: Date; + + @Column() + vehicleTypeId: string; + + @ManyToOne(() => vehicleType, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) + vehicleType: vehicleType; + + @OneToMany(() => damageReport, (d) => d.vehicle, { cascade: ["insert"] }) + reports: damageReport[]; + + @OneToMany(() => maintenance, (m) => m.vehicle, { cascade: ["insert"] }) + maintenances: maintenance[]; + + @OneToMany(() => inspection, (i) => i.vehicle, { cascade: ["insert"] }) + inspections: inspection[]; +} diff --git a/src/entity/unit/vehicle/vehicleType.ts b/src/entity/unit/vehicle/vehicleType.ts new file mode 100644 index 0000000..2ed071a --- /dev/null +++ b/src/entity/unit/vehicle/vehicleType.ts @@ -0,0 +1,23 @@ +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { vehicle } from "./vehicle"; +import { inspectionPlan } from "../inspection/inspectionPlan"; + +@Entity() +export class vehicleType { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255, unique: true }) + type: string; + + @Column({ type: "text", nullable: true }) + description: string; + + @OneToMany(() => vehicle, (e) => e.vehicleType, { cascade: ["insert"] }) + vehicle: vehicle[]; + + @OneToMany(() => inspectionPlan, (ip) => ip.vehicleType, { cascade: ["insert"] }) + inspectionPlans: inspectionPlan[]; + + vehicleCount: number; +} diff --git a/src/entity/unit/wearable/wearable.ts b/src/entity/unit/wearable/wearable.ts new file mode 100644 index 0000000..2c5ce44 --- /dev/null +++ b/src/entity/unit/wearable/wearable.ts @@ -0,0 +1,57 @@ +import { Column, ColumnType, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { getTypeByORM } from "../../../migrations/ormHelper"; +import { wearableType } from "./wearableType"; +import { damageReport } from "../damageReport"; +import { member } from "../../club/member/member"; +import { inspection } from "../inspection/inspection"; +import { maintenance } from "../maintenance"; + +@Entity() +export class wearable { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255, nullable: true, default: null, unique: true }) + code?: string; + + @Column({ type: "varchar", length: 255 }) + name: string; + + @Column({ type: "varchar", length: 255 }) + location: string; + + @Column({ type: getTypeByORM("date").type as ColumnType }) + commissioned: Date; + + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) + decommissioned?: Date; + + @Column() + wearableTypeId: string; + + @Column({ nullable: true, default: null }) + wearerId?: string; + + @ManyToOne(() => wearableType, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) + wearableType: wearableType; + + @ManyToOne(() => member, { + nullable: true, + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }) + wearer?: member; + + @OneToMany(() => damageReport, (d) => d.wearable, { cascade: ["insert"] }) + reports: damageReport[]; + + @OneToMany(() => maintenance, (m) => m.wearable, { cascade: ["insert"] }) + maintenances: maintenance[]; + + @OneToMany(() => inspection, (i) => i.wearable, { cascade: ["insert"] }) + inspections: inspection[]; +} diff --git a/src/entity/unit/wearable/wearableType.ts b/src/entity/unit/wearable/wearableType.ts new file mode 100644 index 0000000..a7d7538 --- /dev/null +++ b/src/entity/unit/wearable/wearableType.ts @@ -0,0 +1,23 @@ +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { wearable as wearable } from "./wearable"; +import { inspectionPlan } from "../inspection/inspectionPlan"; + +@Entity() +export class wearableType { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255, unique: true }) + type: string; + + @Column({ type: "text", nullable: true }) + description: string; + + @OneToMany(() => wearable, (e) => e.wearableType, { cascade: ["insert"] }) + wearable: wearable[]; + + @OneToMany(() => inspectionPlan, (ip) => ip.wearableType, { cascade: ["insert"] }) + inspectionPlans: inspectionPlan[]; + + wearableCount: number; +} diff --git a/src/enums/inspectionEnum.ts b/src/enums/inspectionEnum.ts new file mode 100644 index 0000000..58143e6 --- /dev/null +++ b/src/enums/inspectionEnum.ts @@ -0,0 +1,6 @@ +export enum InspectionPointEnum { + oknok = "oknok", + text = "text", + number = "number", + file = "file", +} diff --git a/src/factory/admin/club/member/member.ts b/src/factory/admin/club/member/member.ts index 9dd157f..a331d11 100644 --- a/src/factory/admin/club/member/member.ts +++ b/src/factory/admin/club/member/member.ts @@ -15,7 +15,7 @@ export default abstract class MemberFactory { public static mapToSingle(record: member): MemberViewModel { return { id: record?.id, - salutation: SalutationFactory.mapToSingle(record?.salutation), + salutation: record?.salutation ? SalutationFactory.mapToSingle(record?.salutation) : null, firstname: record?.firstname, lastname: record?.lastname, nameaffix: record?.nameaffix, diff --git a/src/factory/admin/unit/damageReport.ts b/src/factory/admin/unit/damageReport.ts new file mode 100644 index 0000000..fb9115e --- /dev/null +++ b/src/factory/admin/unit/damageReport.ts @@ -0,0 +1,57 @@ +import { damageReport } from "../../../entity/unit/damageReport"; +import { DamageReportAssigned, DamageReportViewModel } from "../../../viewmodel/admin/unit/damageReport.models"; +import EquipmentFactory from "./equipment/equipment"; +import MaintenanceFactory from "./maintenance"; +import VehicleFactory from "./vehicle/vehicle"; +import WearableFactory from "./wearable/wearable"; + +export default abstract class DamageReportFactory { + /** + * @description map record to damageReport + * @param {damageReport} record + * @returns {DamageReportViewModel} + */ + public static mapToSingle(record: damageReport): DamageReportViewModel { + let assigned: DamageReportAssigned; + if (record?.equipmentId) { + assigned = { + relatedId: record.equipmentId, + assigned: "equipment", + related: EquipmentFactory.mapToSingle(record.equipment), + }; + } else if (record?.vehicleId) { + assigned = { + relatedId: record.vehicleId, + assigned: "vehicle", + related: VehicleFactory.mapToSingle(record.vehicle), + }; + } else { + assigned = { + relatedId: record.wearableId, + assigned: "wearable", + related: WearableFactory.mapToSingle(record.wearable), + }; + } + + return { + id: record.id, + reportedAt: record.reportedAt, + status: record.status, + done: record.done, + description: record.description, + imageCount: record.imageCount, + reportedBy: record?.reportedBy, + ...assigned, + maintenance: record.maintenance ? MaintenanceFactory.mapToSingle(record.maintenance) : null, + }; + } + + /** + * @description map records to damageReport + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/unit/equipment/equipment.ts b/src/factory/admin/unit/equipment/equipment.ts new file mode 100644 index 0000000..8f0b520 --- /dev/null +++ b/src/factory/admin/unit/equipment/equipment.ts @@ -0,0 +1,32 @@ +import { equipment } from "../../../../entity/unit/equipment/equipment"; +import { EquipmentViewModel } from "../../../../viewmodel/admin/unit/equipment/equipment.models"; +import EquipmentTypeFactory from "./equipmentType"; + +export default abstract class EquipmentFactory { + /** + * @description map record to equipment + * @param {equipment} record + * @returns {EquipmentViewModel} + */ + public static mapToSingle(record: equipment): EquipmentViewModel { + return { + id: record.id, + code: record?.code, + name: record.name, + location: record.location, + commissioned: record.commissioned, + decommissioned: record?.decommissioned, + equipmentTypeId: record?.equipmentTypeId, + equipmentType: record?.equipmentType ? EquipmentTypeFactory.mapToSingle(record.equipmentType) : null, + }; + } + + /** + * @description map records to equipment + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/unit/equipment/equipmentType.ts b/src/factory/admin/unit/equipment/equipmentType.ts new file mode 100644 index 0000000..6906215 --- /dev/null +++ b/src/factory/admin/unit/equipment/equipmentType.ts @@ -0,0 +1,27 @@ +import { equipmentType } from "../../../../entity/unit/equipment/equipmentType"; +import { EquipmentTypeViewModel } from "../../../../viewmodel/admin/unit/equipment/equipmentType.models"; + +export default abstract class EquipmentTypeFactory { + /** + * @description map record to equipmentType + * @param {equipmentType} record + * @returns {EquipmentTypeViewModel} + */ + public static mapToSingle(record: equipmentType): EquipmentTypeViewModel { + return { + id: record.id, + type: record.type, + description: record.description, + equipmentCount: record.equipmentCount, + }; + } + + /** + * @description map records to equipmentType + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/unit/inspection/inspection.ts b/src/factory/admin/unit/inspection/inspection.ts new file mode 100644 index 0000000..ac27226 --- /dev/null +++ b/src/factory/admin/unit/inspection/inspection.ts @@ -0,0 +1,133 @@ +import { inspection } from "../../../../entity/unit/inspection/inspection"; +import { + InspectionNextViewModel, + InspectionRelated, + InspectionViewModel, + MinifiedInspectionViewModel, +} from "../../../../viewmodel/admin/unit/inspection/inspection.models"; +import EquipmentFactory from "../equipment/equipment"; +import VehicleFactory from "../vehicle/vehicle"; +import WearableFactory from "../wearable/wearable"; +import InspectionPlanFactory from "./inspectionPlan"; +import InspectionPointResultFactory from "./inspectionPointResult"; +import InspectionVersionedPlanFactory from "./inspectionVersionedPlan"; + +export default abstract class InspectionFactory { + /** + * @description map record to inspection + * @param {inspection} record + * @returns {InspectionViewModel} + */ + public static mapRelated(record: inspection): InspectionRelated { + let related: InspectionRelated; + if (record?.equipmentId) { + related = { + relatedId: record.equipmentId, + assigned: "equipment", + related: EquipmentFactory.mapToSingle(record.equipment), + }; + } else if (record?.vehicleId) { + related = { + relatedId: record.vehicleId, + assigned: "vehicle", + related: VehicleFactory.mapToSingle(record.vehicle), + }; + } else { + related = { + relatedId: record.wearableId, + assigned: "wearable", + related: WearableFactory.mapToSingle(record.wearable), + }; + } + return related; + } + + /** + * @description map record to inspection + * @param {inspection} record + * @returns {InspectionViewModel} + */ + public static mapToSingle(record: inspection): InspectionViewModel { + let related = this.mapRelated(record); + + return { + id: record.id, + inspectionPlanId: record.inspectionPlanId, + inspectionPlan: InspectionPlanFactory.mapToSingle(record.inspectionPlan), + inspectionVersionedPlanId: record.inspectionVersionedPlanId, + inspectionVersionedPlan: InspectionVersionedPlanFactory.mapToSingle(record.inspectionVersionedPlan), + context: record.context, + created: record.createdAt, + finished: record?.finishedAt, + isOpen: record?.finishedAt == undefined, + nextInspection: record?.nextInspection, + checks: InspectionPointResultFactory.mapToBase(record.pointResults), + ...related, + }; + } + + /** + * @description map records to inspection + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } + + /** + * @description map record to minified inspection + * @param {inspection} record + * @returns {MinifiedInspectionViewModel} + */ + public static mapToSingleMinified(record: inspection): MinifiedInspectionViewModel { + let related = this.mapRelated(record); + + return { + id: record.id, + inspectionPlanId: record.inspectionPlanId, + inspectionPlan: InspectionPlanFactory.mapToSingle(record.inspectionPlan), + context: record.context, + created: record.createdAt, + finished: record?.finishedAt, + isOpen: record?.finishedAt == undefined, + nextInspection: record?.nextInspection, + ...related, + }; + } + + /** + * @description map records to minified inspection + * @param {Array} records + * @returns {Array} + */ + public static mapToBaseMinified(records: Array): Array { + return records.map((r) => this.mapToSingleMinified(r)); + } + + /** + * @description map record to next inspection + * @param {inspection} record + * @returns {InspectionNextViewModel} + */ + public static mapToSingleNext(record: inspection): InspectionNextViewModel { + let related = this.mapRelated(record); + + return { + id: record.id, + inspectionPlanId: record.inspectionPlanId, + inspectionPlan: InspectionPlanFactory.mapToSingle(record.inspectionPlan), + dueDate: record?.nextInspection, + ...related, + }; + } + + /** + * @description map records to next inspection + * @param {Array} records + * @returns {Array} + */ + public static mapToBaseNext(records: Array): Array { + return records.map((r) => this.mapToSingleNext(r)); + } +} diff --git a/src/factory/admin/unit/inspection/inspectionPlan.ts b/src/factory/admin/unit/inspection/inspectionPlan.ts new file mode 100644 index 0000000..6e48ed5 --- /dev/null +++ b/src/factory/admin/unit/inspection/inspectionPlan.ts @@ -0,0 +1,63 @@ +import { inspectionPlan } from "../../../../entity/unit/inspection/inspectionPlan"; +import { + InspectionPlanRelated, + InspectionPlanViewModel, +} from "../../../../viewmodel/admin/unit/inspection/inspectionPlan.models"; +import EquipmentFactory from "../equipment/equipment"; +import EquipmentTypeFactory from "../equipment/equipmentType"; +import VehicleFactory from "../vehicle/vehicle"; +import VehicleTypeFactory from "../vehicle/vehicleType"; +import WearableTypeFactory from "../wearable/wearableType"; +import InspectionPointFactory from "./inspectionPoint"; + +export default abstract class InspectionPlanFactory { + /** + * @description map record to inspectionPlan + * @param {inspectionPlan} record + * @returns {InspectionPlanViewModel} + */ + public static mapToSingle(record: inspectionPlan): InspectionPlanViewModel { + let related: InspectionPlanRelated; + if (record?.equipmentTypeId) { + related = { + relatedId: record.equipmentTypeId, + assigned: "equipment", + related: EquipmentTypeFactory.mapToSingle(record.equipmentType), + }; + } else if (record?.vehicleTypeId) { + related = { + relatedId: record.vehicleTypeId, + assigned: "vehicle", + related: VehicleTypeFactory.mapToSingle(record.vehicleType), + }; + } else { + related = { + relatedId: record.wearableTypeId, + assigned: "wearable", + related: WearableTypeFactory.mapToSingle(record.wearableType), + }; + } + + return { + id: record.id, + title: record.title, + inspectionInterval: record.inspectionInterval, + remindTime: record.remindTime, + version: record?.latestVersionedPlan?.version ?? 0, + created: record.createdAt, + inspectionPoints: record.latestVersionedPlan + ? InspectionPointFactory.mapToBase(record.latestVersionedPlan.inspectionPoints) + : [], + ...related, + }; + } + + /** + * @description map records to inspectionPlan + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/unit/inspection/inspectionPoint.ts b/src/factory/admin/unit/inspection/inspectionPoint.ts new file mode 100644 index 0000000..b494542 --- /dev/null +++ b/src/factory/admin/unit/inspection/inspectionPoint.ts @@ -0,0 +1,31 @@ +import { inspectionPoint } from "../../../../entity/unit/inspection/inspectionPoint"; +import { InspectionPointViewModel } from "../../../../viewmodel/admin/unit/inspection/inspectionPlan.models"; + +export default abstract class InspectionPointFactory { + /** + * @description map record to inspectionPoint + * @param {inspectionPoint} record + * @returns {InspectionPointViewModel} + */ + public static mapToSingle(record: inspectionPoint): InspectionPointViewModel { + return { + id: record.id, + title: record.title, + description: record.description, + type: record.type, + sort: record.sort, + min: record?.min, + max: record?.max, + others: record?.others, + }; + } + + /** + * @description map records to inspectionPoint + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/unit/inspection/inspectionPointResult.ts b/src/factory/admin/unit/inspection/inspectionPointResult.ts new file mode 100644 index 0000000..e210e57 --- /dev/null +++ b/src/factory/admin/unit/inspection/inspectionPointResult.ts @@ -0,0 +1,28 @@ +import { inspectionPointResult } from "../../../../entity/unit/inspection/inspectionPointResult"; +import { InspectionPointResultViewModel } from "../../../../viewmodel/admin/unit/inspection/inspection.models"; +import InspectionPointFactory from "./inspectionPoint"; + +export default abstract class InspectionPointResultFactory { + /** + * @description map record to inspectionPointResult + * @param {inspectionPointResult} record + * @returns {InspectionPointResultViewModel} + */ + public static mapToSingle(record: inspectionPointResult): InspectionPointResultViewModel { + return { + inspectionId: record.inspectionId, + inspectionPointId: record.inspectionPointId, + inspectionPoint: InspectionPointFactory.mapToSingle(record.inspectionPoint), + value: record.value, + }; + } + + /** + * @description map records to inspectionPointResult + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/unit/inspection/inspectionVersionedPlan.ts b/src/factory/admin/unit/inspection/inspectionVersionedPlan.ts new file mode 100644 index 0000000..c655342 --- /dev/null +++ b/src/factory/admin/unit/inspection/inspectionVersionedPlan.ts @@ -0,0 +1,28 @@ +import { inspectionVersionedPlan } from "../../../../entity/unit/inspection/inspectionVersionedPlan"; +import { InspectionVersionedPlanViewModel } from "../../../../viewmodel/admin/unit/inspection/inspectionPlan.models"; +import InspectionPointFactory from "./inspectionPoint"; + +export default abstract class InspectionVersionedPlanFactory { + /** + * @description map record to inspectionVersionedPlan + * @param {inspectionVersionedPlan} record + * @returns {InspectionVersionedPlanViewModel} + */ + public static mapToSingle(record: inspectionVersionedPlan): InspectionVersionedPlanViewModel { + return { + id: record.id, + version: record.version, + created: record.createdAt, + inspectionPoints: InspectionPointFactory.mapToBase(record.inspectionPoints), + }; + } + + /** + * @description map records to inspectionVersionedPlan + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/unit/maintenance.ts b/src/factory/admin/unit/maintenance.ts new file mode 100644 index 0000000..69f4c78 --- /dev/null +++ b/src/factory/admin/unit/maintenance.ts @@ -0,0 +1,55 @@ +import { maintenance } from "../../../entity/unit/maintenance"; +import { MaintenanceAssigned, MaintenanceViewModel } from "../../../viewmodel/admin/unit/maintenance.models"; +import DamageReportFactory from "./damageReport"; +import EquipmentFactory from "./equipment/equipment"; +import VehicleFactory from "./vehicle/vehicle"; +import WearableFactory from "./wearable/wearable"; + +export default abstract class MaintenanceFactory { + /** + * @description map record to maintenance + * @param {maintenance} record + * @returns {MaintenanceViewModel} + */ + public static mapToSingle(record: maintenance): MaintenanceViewModel { + let assigned: MaintenanceAssigned; + if (record?.equipmentId) { + assigned = { + relatedId: record.equipmentId, + assigned: "equipment", + related: EquipmentFactory.mapToSingle(record.equipment), + }; + } else if (record?.vehicleId) { + assigned = { + relatedId: record.vehicleId, + assigned: "vehicle", + related: VehicleFactory.mapToSingle(record.vehicle), + }; + } else { + assigned = { + relatedId: record.wearableId, + assigned: "wearable", + related: WearableFactory.mapToSingle(record.wearable), + }; + } + + return { + id: record.id, + createdAt: record.createdAt, + status: record.status, + done: record.done, + description: record.description, + ...assigned, + reports: record.reports ? DamageReportFactory.mapToBase(record.reports) : [], + }; + } + + /** + * @description map records to maintenance + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/unit/vehicle/vehicle.ts b/src/factory/admin/unit/vehicle/vehicle.ts new file mode 100644 index 0000000..fd7a86d --- /dev/null +++ b/src/factory/admin/unit/vehicle/vehicle.ts @@ -0,0 +1,32 @@ +import { vehicle } from "../../../../entity/unit/vehicle/vehicle"; +import { VehicleViewModel } from "../../../../viewmodel/admin/unit/vehicle/vehicle.models"; +import VehicleTypeFactory from "./vehicleType"; + +export default abstract class VehicleFactory { + /** + * @description map record to vehicle + * @param {vehicle} record + * @returns {VehicleViewModel} + */ + public static mapToSingle(record: vehicle): VehicleViewModel { + return { + id: record.id, + code: record?.code, + name: record.name, + location: record.location, + commissioned: record.commissioned, + decommissioned: record?.decommissioned, + vehicleTypeId: record?.vehicleTypeId, + vehicleType: record?.vehicleType ? VehicleTypeFactory.mapToSingle(record.vehicleType) : null, + }; + } + + /** + * @description map records to vehicle + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/unit/vehicle/vehicleType.ts b/src/factory/admin/unit/vehicle/vehicleType.ts new file mode 100644 index 0000000..b282b79 --- /dev/null +++ b/src/factory/admin/unit/vehicle/vehicleType.ts @@ -0,0 +1,27 @@ +import { vehicleType } from "../../../../entity/unit/vehicle/vehicleType"; +import { VehicleTypeViewModel } from "../../../../viewmodel/admin/unit/vehicle/vehicleType.models"; + +export default abstract class VehicleTypeFactory { + /** + * @description map record to vehicleType + * @param {vehicleType} record + * @returns {VehicleTypeViewModel} + */ + public static mapToSingle(record: vehicleType): VehicleTypeViewModel { + return { + id: record.id, + type: record.type, + description: record.description, + vehicleCount: record.vehicleCount, + }; + } + + /** + * @description map records to vehicleType + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/unit/wearable/wearable.ts b/src/factory/admin/unit/wearable/wearable.ts new file mode 100644 index 0000000..d3b39af --- /dev/null +++ b/src/factory/admin/unit/wearable/wearable.ts @@ -0,0 +1,35 @@ +import { wearable } from "../../../../entity/unit/wearable/wearable"; +import { WearableViewModel } from "../../../../viewmodel/admin/unit/wearable/wearable.models"; +import MemberFactory from "../../club/member/member"; +import WearableTypeFactory from "./wearableType"; + +export default abstract class WearableFactory { + /** + * @description map record to wearable + * @param {wearable} record + * @returns {WearableViewModel} + */ + public static mapToSingle(record: wearable): WearableViewModel { + return { + id: record.id, + code: record?.code, + name: record.name, + location: record.location, + commissioned: record.commissioned, + decommissioned: record?.decommissioned, + wearableTypeId: record?.wearableTypeId, + wearableType: record?.wearableType ? WearableTypeFactory.mapToSingle(record.wearableType) : null, + wearerId: record?.wearerId, + wearer: record?.wearer ? MemberFactory.mapToSingle(record.wearer) : null, + }; + } + + /** + * @description map records to wearable + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/unit/wearable/wearableType.ts b/src/factory/admin/unit/wearable/wearableType.ts new file mode 100644 index 0000000..89c1066 --- /dev/null +++ b/src/factory/admin/unit/wearable/wearableType.ts @@ -0,0 +1,27 @@ +import { wearableType } from "../../../../entity/unit/wearable/wearableType"; +import { WearableTypeViewModel } from "../../../../viewmodel/admin/unit/wearable/wearableType.models"; + +export default abstract class WearableTypeFactory { + /** + * @description map record to wearableType + * @param {wearableType} record + * @returns {WearableTypeViewModel} + */ + public static mapToSingle(record: wearableType): WearableTypeViewModel { + return { + id: record.id, + type: record.type, + description: record.description, + wearableCount: record.wearableCount, + }; + } + + /** + * @description map records to wearableType + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/handlebars.config.ts b/src/handlebars.config.ts index 19b32ad..6494994 100644 --- a/src/handlebars.config.ts +++ b/src/handlebars.config.ts @@ -59,3 +59,7 @@ Handlebars.registerHelper("longdatetimeWithWeekday", function (aString) { Handlebars.registerHelper("json", function (context) { return JSON.stringify(context); }); + +Handlebars.registerHelper("eq", function (p, q, options) { + return p == q ? options.fn(this) : options.inverse(this); +}); diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts index 42632df..d510f8d 100644 --- a/src/helpers/backupHelper.ts +++ b/src/helpers/backupHelper.ts @@ -8,7 +8,6 @@ import DatabaseActionException from "../exceptions/databaseActionException"; import { availableTemplates } from "../type/templateTypes"; import SettingHelper from "./settingsHelper"; import { LoginRoutineEnum } from "../enums/loginRoutineEnum"; -import { education } from "../entity/configuration/education"; export type BackupSection = | "member" @@ -21,7 +20,9 @@ export type BackupSection = | "template" | "user" | "webapi" - | "settings"; + | "settings" + | "unitBase" + | "unitInstances"; export type BackupSectionRefered = { [key in BackupSection]?: Array; @@ -35,7 +36,7 @@ export type BackupFileContentSection = Array | { [key: string]: Array export default abstract class BackupHelper { // ! Order matters because of foreign keys private static readonly backupSection: Array<{ type: BackupSection; orderOnInsert: number; orderOnClear: number }> = [ - { type: "member", orderOnInsert: 2, orderOnClear: 2 }, // CLEAR depends on protcol INSERT depends on Base + { type: "member", orderOnInsert: 2, orderOnClear: 2 }, // CLEAR depends on protcol and wearables INSERT depends on Base { type: "memberBase", orderOnInsert: 1, orderOnClear: 3 }, // CLEAR depends on member { type: "protocol", orderOnInsert: 3, orderOnClear: 1 }, // INSERT depends on member { type: "newsletter", orderOnInsert: 3, orderOnClear: 1 }, // INSERT depends on member & query & calendar @@ -46,6 +47,8 @@ export default abstract class BackupHelper { { type: "user", orderOnInsert: 1, orderOnClear: 1 }, { type: "webapi", orderOnInsert: 1, orderOnClear: 1 }, { type: "settings", orderOnInsert: 1, orderOnClear: 1 }, + { type: "unitBase", orderOnInsert: 1, orderOnClear: 2 }, + { type: "unitInstances", orderOnInsert: 3, orderOnClear: 1 }, // INSERT depends on member in wearable ]; private static readonly backupSectionRefered: BackupSectionRefered = { @@ -83,6 +86,8 @@ export default abstract class BackupHelper { user: ["user", "user_permission", "role", "role_permission", "invite"], webapi: ["webapi", "webapi_permission"], settings: ["setting"], + unitBase: ["equipment_type", "vehicle_type", "wearable_type"], + unitInstances: ["equipment", "vehicle", "wearable"], }; private static transactionManager: EntityManager; @@ -162,7 +167,7 @@ export default abstract class BackupHelper { for (const section of sections.filter((s) => Object.keys(backup).includes(s.type))) { let refered = this.backupSectionRefered[section.type]; for (const ref of refered) { - await this.transactionManager.getRepository(ref).delete({}); + await this.transactionManager.getRepository(ref).deleteAll(); } } } @@ -229,6 +234,10 @@ export default abstract class BackupHelper { return await this.getWebapi(); case "settings": return await this.getSettings(); + case "unitBase": + return await this.getUnitBase(); + case "unitInstances": + return await this.getUnitInstances(); default: return []; } @@ -490,6 +499,27 @@ export default abstract class BackupHelper { .select(["setting.topic", "setting.key", "setting.value"]) .getMany(); } + private static async getUnitBase(): Promise<{ [key: string]: Array }> { + return { + equipment_type: await dataSource.getRepository("equipment_type").find(), + vehicle_type: await dataSource.getRepository("vehicle_type").find(), + wearable_type: await dataSource.getRepository("wearable_type").find(), + inspection_plan: await dataSource.getRepository("inspection_plan").find(), + inspection_versioned_plan: await dataSource.getRepository("inspection_versioned_plan").find(), + inspection_point: await dataSource.getRepository("inspection_point").find(), + }; + } + private static async getUnitInstances(): Promise<{ [key: string]: Array }> { + return { + equipment: await dataSource.getRepository("equipment").find(), + vehicle: await dataSource.getRepository("vehicle").find(), + wearable: await dataSource.getRepository("wearable").find(), + maintenance: await dataSource.getRepository("maintenance").find(), + damage_report: await dataSource.getRepository("damage_report").find(), + inspection: await dataSource.getRepository("inspection").find(), + inspection_point_result: await dataSource.getRepository("inspection_point_result").find(), + }; + } private static async setSectionData( section: BackupSection, @@ -507,6 +537,8 @@ export default abstract class BackupHelper { if (section == "user" && !Array.isArray(data)) await this.setUser(data); if (section == "webapi" && Array.isArray(data)) await this.setWebapi(data); if (section == "settings" && Array.isArray(data)) await this.setSettings(data); + if (section == "unitBase" && !Array.isArray(data)) await this.setUnitBase(data); + if (section == "unitInstances" && !Array.isArray(data)) await this.setUnitInstances(data); } private static async setMemberData(data: Array): Promise { @@ -870,4 +902,24 @@ export default abstract class BackupHelper { private static async setSettings(data: Array): Promise { await this.transactionManager.getRepository("setting").save(data); } + private static async setUnitBase(data: { [key: string]: Array }): Promise { + await this.transactionManager.getRepository("equipment_type").save(data["equipment_type"]); + await this.transactionManager.getRepository("vehicle_type").save(data["vehicle_type"]); + await this.transactionManager.getRepository("wearable_type").save(data["wearable_type"]); + + await this.transactionManager.getRepository("inspection_plan").save(data["inspection_plan"]); + await this.transactionManager.getRepository("inspection_versioned_plan").save(data["inspection_versioned_plan"]); + await this.transactionManager.getRepository("inspection_point").save(data["inspection_point"]); + } + private static async setUnitInstances(data: { [key: string]: Array }): Promise { + await this.transactionManager.getRepository("equipment").save(data["equipment"]); + await this.transactionManager.getRepository("vehicle").save(data["vehicle"]); + await this.transactionManager.getRepository("wearable").save(data["wearable"]); + + await this.transactionManager.getRepository("damage_report").save(data["damage_report"]); + await this.transactionManager.getRepository("maintenance").save(data["maintenance"]); + + await this.transactionManager.getRepository("inspection").save(data["inspection"]); + await this.transactionManager.getRepository("inspection_point_result").save(data["inspection_point_result"]); + } } diff --git a/src/helpers/fileSystemHelper.ts b/src/helpers/fileSystemHelper.ts index 9bc409a..c97bf4f 100644 --- a/src/helpers/fileSystemHelper.ts +++ b/src/helpers/fileSystemHelper.ts @@ -15,7 +15,7 @@ export abstract class FileSystemHelper { return readFileSync(this.formatPath(...filePath), "utf8"); } - static readFileasBase64(...filePath: string[]) { + static readFileAsBase64(...filePath: string[]) { this.createFolder(...filePath); return readFileSync(this.formatPath(...filePath), "base64"); } diff --git a/src/helpers/pdfExport.ts b/src/helpers/pdfExport.ts index 429f551..b42564e 100644 --- a/src/helpers/pdfExport.ts +++ b/src/helpers/pdfExport.ts @@ -127,7 +127,7 @@ export abstract class PdfExport { const mergedPdf = await PDFDocument.create(); for (const pdfPath of pdfFilePaths) { - const pdfBytes = FileSystemHelper.readFileasBase64(inputFolder, pdfPath); + const pdfBytes = FileSystemHelper.readFileAsBase64(inputFolder, pdfPath); const pdf = await PDFDocument.load(pdfBytes); const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices()); copiedPages.forEach((page) => mergedPdf.addPage(page)); diff --git a/src/helpers/typeTester.ts b/src/helpers/typeTester.ts new file mode 100644 index 0000000..ab37808 --- /dev/null +++ b/src/helpers/typeTester.ts @@ -0,0 +1,18 @@ +import { PlanTimeDefinition } from "../viewmodel/admin/unit/inspection/inspectionPlan.models"; + +export default abstract class TypeTester { + static testPlanTimeDefinition(val: string, key: string = "", throwErr: boolean = false): PlanTimeDefinition | null { + if (/^(\d+-(d|m|y)|\d+\/(\d+|\*))$/.test(val)) { + return val as PlanTimeDefinition; + } else if (throwErr) { + throw Error( + [ + key, + "provided String does not match PlanTimeDefinition Format: ${number}-${'d'|'m'|'y'} or ${number}/${number|'*'}", + ].join(": ") + ); + } else { + return null; + } + } +} diff --git a/src/index.ts b/src/index.ts index 13de2ee..1e28a8c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,8 @@ declare global { export interface Request { userId: string; username: string; + firstname: string; + lastname: string; isOwner: boolean; permissions: PermissionObject; isPWA: boolean; diff --git a/src/middleware/authenticate.ts b/src/middleware/authenticate.ts index abeb832..c961b19 100644 --- a/src/middleware/authenticate.ts +++ b/src/middleware/authenticate.ts @@ -35,6 +35,8 @@ export default async function authenticate(req: Request, res: Response, next: Ne req.userId = decoded.userId; req.username = decoded.username; + req.firstname = decoded.firstname; + req.lastname = decoded.lastname; req.isOwner = decoded.isOwner; req.permissions = decoded.permissions; req.isWebApiRequest = decoded?.sub == "webapi_access_token"; diff --git a/src/middleware/multer.ts b/src/middleware/multer.ts index ebcec4e..84a677c 100644 --- a/src/middleware/multer.ts +++ b/src/middleware/multer.ts @@ -3,6 +3,7 @@ import { FileSystemHelper } from "../helpers/fileSystemHelper"; import path from "path"; import BadRequestException from "../exceptions/badRequestException"; +/**Settings image upload */ export const clubImageStorage = multer.diskStorage({ destination: FileSystemHelper.formatPath("/app"), filename: function (req, file, cb) { @@ -33,3 +34,27 @@ export const clubImageUpload = clubImageMulter.fields([ { name: "icon", maxCount: 1 }, { name: "logo", maxCount: 1 }, ]); + +/**Inspection file upload */ +export const inspectionFileStorage = multer.diskStorage({ + destination: function (req, file, cb) { + FileSystemHelper.createFolder("inspection", req.params.id); + cb(null, FileSystemHelper.formatPath("inspection", req.params.id)); + }, + filename: function (req, file, cb) { + cb(null, file.originalname); + }, +}); + +export const inspectionFileMulter = multer({ + storage: inspectionFileStorage, + fileFilter(req, file, cb) { + if (file.mimetype.startsWith("image/") || file.mimetype === "application/pdf") { + cb(null, true); + } else { + cb(new BadRequestException("Wrong file format")); + } + }, +}); + +export const inspectionFileUpload = inspectionFileMulter.array("files"); diff --git a/src/migrations/1749361405703-UnitBase.ts b/src/migrations/1749361405703-UnitBase.ts new file mode 100644 index 0000000..97c657f --- /dev/null +++ b/src/migrations/1749361405703-UnitBase.ts @@ -0,0 +1,80 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +import { + inspection_plan_table, + inspection_versioned_plan_table, + inspection_point_table, + inspection_table, + inspection_point_result_table, +} from "./baseSchemaTables/inspection"; +import { + equipment_type_table, + equipment_table, + vehicle_type_table, + vehicle_table, + wearable_type_table, + wearable_table, +} from "./baseSchemaTables/unit"; +import { maintenance_table, damage_report_table } from "./baseSchemaTables/unit_extend"; +import { availableTemplates } from "../type/templateTypes"; +import { template_usage_table } from "./baseSchemaTables/query_template"; + +export class UnitBase1749361405703 implements MigrationInterface { + name = "UnitBase1749361405703"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createTable(equipment_type_table, true, true, true); + await queryRunner.createTable(equipment_table, true, true, true); + await queryRunner.createTable(vehicle_type_table, true, true, true); + await queryRunner.createTable(vehicle_table, true, true, true); + await queryRunner.createTable(wearable_type_table, true, true, true); + await queryRunner.createTable(wearable_table, true, true, true); + + await queryRunner.createTable(maintenance_table, true, true, true); + await queryRunner.createTable(damage_report_table, true, true, true); + + await queryRunner.createTable(inspection_plan_table, true, true, true); + await queryRunner.createTable(inspection_versioned_plan_table, true, true, true); + await queryRunner.createTable(inspection_point_table, true, true, true); + + await queryRunner.createTable(inspection_table, true, true, true); + await queryRunner.createTable(inspection_point_result_table, true, true, true); + + await queryRunner.manager + .createQueryBuilder() + .insert() + .into(template_usage_table.name) + .values( + availableTemplates.map((at) => ({ + scope: at, + })) + ) + .orIgnore() + .execute(); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable(inspection_point_result_table, true, true, true); + await queryRunner.dropTable(inspection_table, true, true, true); + + await queryRunner.dropTable(inspection_point_table, true, true, true); + await queryRunner.dropTable(inspection_versioned_plan_table, true, true, true); + await queryRunner.dropTable(inspection_plan_table, true, true, true); + + await queryRunner.dropTable(damage_report_table, true, true, true); + await queryRunner.dropTable(maintenance_table, true, true, true); + + await queryRunner.dropTable(wearable_table, true, true, true); + await queryRunner.dropTable(wearable_type_table, true, true, true); + await queryRunner.dropTable(vehicle_table, true, true, true); + await queryRunner.dropTable(vehicle_type_table, true, true, true); + await queryRunner.dropTable(equipment_table, true, true, true); + await queryRunner.dropTable(equipment_type_table, true, true, true); + + await queryRunner.manager + .createQueryBuilder() + .delete() + .from(template_usage_table.name) + .where({ scope: "inspection" }) + .execute(); + } +} diff --git a/src/migrations/baseSchemaTables/inspection.ts b/src/migrations/baseSchemaTables/inspection.ts new file mode 100644 index 0000000..fad995f --- /dev/null +++ b/src/migrations/baseSchemaTables/inspection.ts @@ -0,0 +1,167 @@ +import { Table, TableForeignKey, TableUnique } from "typeorm"; +import { getTypeByORM, isUUIDPrimary, getDefaultByORM } from "../ormHelper"; + +export const inspection_plan_table = new Table({ + name: "inspection_plan", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "title", ...getTypeByORM("varchar") }, + { name: "inspectionInterval", ...getTypeByORM("varchar") }, + { name: "remindTime", ...getTypeByORM("varchar") }, + { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, + { name: "equipmentTypeId", ...getTypeByORM("uuid", true) }, + { name: "vehicleTypeId", ...getTypeByORM("uuid", true) }, + { name: "wearableTypeId", ...getTypeByORM("uuid", true) }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["equipmentTypeId"], + referencedColumnNames: ["id"], + referencedTableName: "equipment_type", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["vehicleTypeId"], + referencedColumnNames: ["id"], + referencedTableName: "vehicle_type", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["wearableTypeId"], + referencedColumnNames: ["id"], + referencedTableName: "wearable_type", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const inspection_versioned_plan_table = new Table({ + name: "inspection_versioned_plan", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "version", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, + { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, + { name: "inspectionPlanId", ...getTypeByORM("uuid") }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["inspectionPlanId"], + referencedColumnNames: ["id"], + referencedTableName: "inspection_plan", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], + uniques: [ + new TableUnique({ + name: "unique_version", + columnNames: ["version", "inspectionPlanId"], + }), + ], +}); + +export const inspection_point_table = new Table({ + name: "inspection_point", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "title", ...getTypeByORM("varchar") }, + { name: "description", ...getTypeByORM("text") }, + { name: "type", ...getTypeByORM("varchar") }, + { name: "min", ...getTypeByORM("int", true) }, + { name: "max", ...getTypeByORM("int", true) }, + { name: "others", ...getTypeByORM("varchar", true) }, + { name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, + { name: "versionedPlanId", ...getTypeByORM("uuid") }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["versionedPlanId"], + referencedColumnNames: ["id"], + referencedTableName: "inspection_versioned_plan", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const inspection_table = new Table({ + name: "inspection", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "context", ...getTypeByORM("text") }, + { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, + { name: "finishedAt", ...getTypeByORM("date", true) }, + { name: "nextInspection", ...getTypeByORM("date", true) }, + { name: "hasNewer", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "inspectionPlanId", ...getTypeByORM("uuid") }, + { name: "inspectionVersionedPlanId", ...getTypeByORM("uuid") }, + { name: "equipmentId", ...getTypeByORM("uuid", true) }, + { name: "vehicleId", ...getTypeByORM("uuid", true) }, + { name: "wearableId", ...getTypeByORM("uuid", true) }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["inspectionPlanId"], + referencedColumnNames: ["id"], + referencedTableName: "inspection_plan", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["inspectionVersionedPlanId"], + referencedColumnNames: ["id"], + referencedTableName: "inspection_versioned_plan", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["equipmentId"], + referencedColumnNames: ["id"], + referencedTableName: "equipment", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["vehicleId"], + referencedColumnNames: ["id"], + referencedTableName: "vehicle", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["wearableId"], + referencedColumnNames: ["id"], + referencedTableName: "wearable", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const inspection_point_result_table = new Table({ + name: "inspection_point_result", + columns: [ + { name: "inspectionId", ...getTypeByORM("uuid"), isPrimary: true }, + { name: "inspectionPointId", ...getTypeByORM("uuid"), isPrimary: true }, + { name: "value", ...getTypeByORM("text") }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["inspectionId"], + referencedColumnNames: ["id"], + referencedTableName: "inspection", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["inspectionPointId"], + referencedColumnNames: ["id"], + referencedTableName: "inspection_point", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); diff --git a/src/migrations/baseSchemaTables/unit.ts b/src/migrations/baseSchemaTables/unit.ts new file mode 100644 index 0000000..4d18e11 --- /dev/null +++ b/src/migrations/baseSchemaTables/unit.ts @@ -0,0 +1,103 @@ +import { Table, TableForeignKey } from "typeorm"; +import { getTypeByORM, isUUIDPrimary, getDefaultByORM } from "../ormHelper"; + +export const equipment_table = new Table({ + name: "equipment", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "code", ...getTypeByORM("varchar", true), isUnique: true }, + { name: "name", ...getTypeByORM("varchar") }, + { name: "location", ...getTypeByORM("varchar") }, + { name: "commissioned", ...getTypeByORM("date") }, + { name: "decommissioned", ...getTypeByORM("date", true) }, + { name: "equipmentTypeId", ...getTypeByORM("uuid") }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["equipmentTypeId"], + referencedColumnNames: ["id"], + referencedTableName: "equipment_type", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); + +export const equipment_type_table = new Table({ + name: "equipment_type", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "type", ...getTypeByORM("varchar"), isUnique: true }, + { name: "description", ...getTypeByORM("text", true) }, + ], +}); + +export const vehicle_table = new Table({ + name: "vehicle", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "code", ...getTypeByORM("varchar", true), isUnique: true }, + { name: "name", ...getTypeByORM("varchar") }, + { name: "location", ...getTypeByORM("varchar") }, + { name: "commissioned", ...getTypeByORM("date") }, + { name: "decommissioned", ...getTypeByORM("date", true) }, + { name: "vehicleTypeId", ...getTypeByORM("uuid") }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["vehicleTypeId"], + referencedColumnNames: ["id"], + referencedTableName: "vehicle_type", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); + +export const vehicle_type_table = new Table({ + name: "vehicle_type", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "type", ...getTypeByORM("varchar"), isUnique: true }, + { name: "description", ...getTypeByORM("text", true) }, + ], +}); + +export const wearable_table = new Table({ + name: "wearable", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "code", ...getTypeByORM("varchar", true), isUnique: true }, + { name: "name", ...getTypeByORM("varchar") }, + { name: "location", ...getTypeByORM("varchar") }, + { name: "commissioned", ...getTypeByORM("date") }, + { name: "decommissioned", ...getTypeByORM("date", true) }, + { name: "wearableTypeId", ...getTypeByORM("uuid") }, + { name: "wearerId", ...getTypeByORM("uuid", true) }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["wearableTypeId"], + referencedColumnNames: ["id"], + referencedTableName: "wearable_type", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["wearerId"], + referencedColumnNames: ["id"], + referencedTableName: "member", + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }), + ], +}); + +export const wearable_type_table = new Table({ + name: "wearable_type", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "type", ...getTypeByORM("varchar"), isUnique: true }, + { name: "description", ...getTypeByORM("text", true) }, + ], +}); diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts new file mode 100644 index 0000000..43bece7 --- /dev/null +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -0,0 +1,86 @@ +import { Table, TableForeignKey } from "typeorm"; +import { getTypeByORM, isUUIDPrimary, getDefaultByORM } from "../ormHelper"; + +export const damage_report_table = new Table({ + name: "damage_report", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "reportedAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, + { name: "status", ...getTypeByORM("varchar") }, + { name: "done", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "description", ...getTypeByORM("text") }, + { name: "reportedBy", ...getTypeByORM("varchar") }, + { name: "imageCount", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, + { name: "equipmentId", ...getTypeByORM("uuid", true) }, + { name: "vehicleId", ...getTypeByORM("uuid", true) }, + { name: "wearableId", ...getTypeByORM("uuid", true) }, + { name: "maintenanceId", ...getTypeByORM("uuid", true) }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["equipmentId"], + referencedColumnNames: ["id"], + referencedTableName: "equipment", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["vehicleId"], + referencedColumnNames: ["id"], + referencedTableName: "vehicle", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["wearableId"], + referencedColumnNames: ["id"], + referencedTableName: "wearable", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["maintenanceId"], + referencedColumnNames: ["id"], + referencedTableName: "maintenance", + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }), + ], +}); + +export const maintenance_table = new Table({ + name: "maintenance", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, + { name: "status", ...getTypeByORM("varchar") }, + { name: "done", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "description", ...getTypeByORM("text") }, + { name: "equipmentId", ...getTypeByORM("uuid", true) }, + { name: "vehicleId", ...getTypeByORM("uuid", true) }, + { name: "wearableId", ...getTypeByORM("uuid", true) }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["equipmentId"], + referencedColumnNames: ["id"], + referencedTableName: "equipment", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["vehicleId"], + referencedColumnNames: ["id"], + referencedTableName: "vehicle", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["wearableId"], + referencedColumnNames: ["id"], + referencedTableName: "wearable", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); diff --git a/src/routes/admin/index.ts b/src/routes/admin/index.ts index b306bfc..3a6edf2 100644 --- a/src/routes/admin/index.ts +++ b/src/routes/admin/index.ts @@ -2,6 +2,7 @@ import express from "express"; import PermissionHelper from "../../helpers/permissionHelper"; import preventWebapiAccess from "../../middleware/preventWebApiAccess"; +/** configuration */ import award from "./configuration/award"; import communicationType from "./configuration/communicationType"; import executivePosition from "./configuration/executivePosition"; @@ -15,6 +16,7 @@ import template from "./configuration/template"; import templateUsage from "./configuration/templateUsage"; import newsletterConfig from "./configuration/newsletterConfig"; +/** club */ import member from "./club/member"; import protocol from "./club/protocol"; import calendar from "./club/calendar"; @@ -22,6 +24,7 @@ import queryBuilder from "./club/queryBuilder"; import newsletter from "./club/newsletter"; import listprint from "./club/listprint"; +/** management */ import role from "./management/role"; import user from "./management/user"; import invite from "./management/invite"; @@ -29,8 +32,21 @@ import api from "./management/webapi"; import backup from "./management/backup"; import setting from "./management/setting"; +/** unit */ +import equipment from "./unit/equipment"; +import equipmentType from "./unit/equipmentType"; +import vehicle from "./unit/vehicle"; +import vehicleType from "./unit/vehicleType"; +import wearable from "./unit/wearable"; +import wearableType from "./unit/wearableType"; +import inspection from "./unit/inspection"; +import inspectionPlan from "./unit/inspectionPlan"; +import damageReport from "./unit/damageReport"; +import maintenance from "./unit/maintenance"; + var router = express.Router({ mergeParams: true }); +/** configuration */ router.use( "/award", PermissionHelper.passCheckSomeMiddleware([ @@ -121,6 +137,7 @@ router.use( newsletterConfig ); +/** club */ router.use("/member", PermissionHelper.passCheckMiddleware("read", "club", "member"), member); router.use( "/protocol", @@ -152,6 +169,7 @@ router.use( ); router.use("/listprint", PermissionHelper.passCheckMiddleware("read", "club", "listprint"), listprint); +/** management */ router.use("/role", PermissionHelper.passCheckMiddleware("read", "management", "role"), role); router.use( "/user", @@ -171,4 +189,99 @@ router.use( ); router.use("/setting", PermissionHelper.passCheckMiddleware("read", "management", "setting"), setting); +/** unit */ +router.use( + "/equipment", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "equipment" }, + { requiredPermission: "read", section: "unit", module: "inspection_plan" }, + ]), + equipment +); +router.use( + "/equipmenttype", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "equipment_type" }, + { requiredPermission: "read", section: "unit", module: "equipment" }, + { requiredPermission: "read", section: "unit", module: "inspection_plan" }, + ]), + equipmentType +); +router.use( + "/vehicle", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "vehicle" }, + { requiredPermission: "read", section: "unit", module: "inspection_plan" }, + ]), + vehicle +); +router.use( + "/vehicletype", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "vehicle_type" }, + { requiredPermission: "read", section: "unit", module: "vehicle" }, + { requiredPermission: "read", section: "unit", module: "inspection_plan" }, + ]), + vehicleType +); +router.use( + "/wearable", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "wearable" }, + { requiredPermission: "read", section: "unit", module: "inspection_plan" }, + ]), + wearable +); +router.use( + "/wearabletype", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "wearable_type" }, + { requiredPermission: "read", section: "unit", module: "wearable" }, + { requiredPermission: "read", section: "unit", module: "inspection_plan" }, + ]), + wearableType +); +router.use( + "/inspection", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "inspection" }, + { requiredPermission: "read", section: "unit", module: "equipment" }, + { requiredPermission: "read", section: "unit", module: "vehicle" }, + { requiredPermission: "read", section: "unit", module: "wearable" }, + ]), + inspection +); +router.use( + "/inspectionplan", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "inspection_plan" }, + { requiredPermission: "read", section: "unit", module: "inspection" }, + { requiredPermission: "read", section: "unit", module: "equipment_type" }, + { requiredPermission: "read", section: "unit", module: "vehicle_type" }, + { requiredPermission: "read", section: "unit", module: "wearable_type" }, + ]), + inspectionPlan +); +router.use( + "/damagereport", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "damage_report" }, + { requiredPermission: "read", section: "unit", module: "maintenance" }, + { requiredPermission: "read", section: "unit", module: "equipment" }, + { requiredPermission: "read", section: "unit", module: "vehicle" }, + { requiredPermission: "read", section: "unit", module: "wearable" }, + ]), + damageReport +); +router.use( + "/maintenance", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "maintenance" }, + { requiredPermission: "read", section: "unit", module: "equipment" }, + { requiredPermission: "read", section: "unit", module: "vehicle" }, + { requiredPermission: "read", section: "unit", module: "wearable" }, + ]), + maintenance +); + export default router; diff --git a/src/routes/admin/unit/damageReport.ts b/src/routes/admin/unit/damageReport.ts new file mode 100644 index 0000000..aee6261 --- /dev/null +++ b/src/routes/admin/unit/damageReport.ts @@ -0,0 +1,50 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createInspection, + deleteInspectionById, + getAllInspectionsForRelated, + getInspectionById, + updateInspectionById, +} from "../../../controller/admin/unit/inspectionController"; +import { + getAllDamageReportsByStatus, + getAllDamageReportsForRelated, + getDamageReportById, + updateDamageReportById, +} from "../../../controller/admin/unit/damageReportController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllDamageReportsByStatus(req, res); +}); + +router.get( + ["/vehicle/:relatedId", "/equipment/:relatedId", "/wearable/:relatedId"], + async (req: Request, res: Response) => { + if (req.path.startsWith("/vehicle")) { + req.params.related = "vehicle"; + } else if (req.path.startsWith("/equipment")) { + req.params.related = "equipment"; + } else { + req.params.related = "wearable"; + } + + await getAllDamageReportsForRelated(req, res); + } +); + +router.get("/:id", async (req: Request, res: Response) => { + await getDamageReportById(req, res); +}); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), + async (req: Request, res: Response) => { + await updateDamageReportById(req, res); + } +); + +export default router; diff --git a/src/routes/admin/unit/equipment.ts b/src/routes/admin/unit/equipment.ts new file mode 100644 index 0000000..4e45a52 --- /dev/null +++ b/src/routes/admin/unit/equipment.ts @@ -0,0 +1,50 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createEquipment, + deleteEquipmentById, + getAllEquipments, + getEquipmentById, + getEquipmentsByIds, + updateEquipmentById, +} from "../../../controller/admin/unit/equipmentController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllEquipments(req, res); +}); + +router.get("/:id", async (req: Request, res: Response) => { + await getEquipmentById(req, res); +}); + +router.post("/ids", async (req: Request, res: Response) => { + await getEquipmentsByIds(req, res); +}); + +router.post( + "/", + PermissionHelper.passCheckMiddleware("create", "unit", "equipment"), + async (req: Request, res: Response) => { + await createEquipment(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "equipment"), + async (req: Request, res: Response) => { + await updateEquipmentById(req, res); + } +); + +router.delete( + "/:id", + PermissionHelper.passCheckMiddleware("delete", "unit", "equipment"), + async (req: Request, res: Response) => { + await deleteEquipmentById(req, res); + } +); + +export default router; diff --git a/src/routes/admin/unit/equipmentType.ts b/src/routes/admin/unit/equipmentType.ts new file mode 100644 index 0000000..e197e34 --- /dev/null +++ b/src/routes/admin/unit/equipmentType.ts @@ -0,0 +1,45 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createEquipmentType, + deleteEquipmentTypeById, + getAllEquipmentTypes, + getEquipmentTypeById, + updateEquipmentTypeById, +} from "../../../controller/admin/unit/equipmentTypeController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllEquipmentTypes(req, res); +}); + +router.get("/:id", async (req: Request, res: Response) => { + await getEquipmentTypeById(req, res); +}); + +router.post( + "/", + PermissionHelper.passCheckMiddleware("create", "unit", "equipment_type"), + async (req: Request, res: Response) => { + await createEquipmentType(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "equipment_type"), + async (req: Request, res: Response) => { + await updateEquipmentTypeById(req, res); + } +); + +router.delete( + "/:id", + PermissionHelper.passCheckMiddleware("delete", "unit", "equipment_type"), + async (req: Request, res: Response) => { + await deleteEquipmentTypeById(req, res); + } +); + +export default router; diff --git a/src/routes/admin/unit/inspection.ts b/src/routes/admin/unit/inspection.ts new file mode 100644 index 0000000..6a43526 --- /dev/null +++ b/src/routes/admin/unit/inspection.ts @@ -0,0 +1,96 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createInspection, + deleteInspectionById, + getAllInspectionsSortedNotHavingNewer, + getAllInspectionsForRelated, + getInspectionById, + updateInspectionById, + getAllInspectionsRunning, + updateInspectionResults, + getInspectionPrintoutById, + finishInspection, + getInspectionPointUpload, +} from "../../../controller/admin/unit/inspectionController"; +import { inspectionFileUpload } from "../../../middleware/multer"; + +var router = express.Router({ mergeParams: true }); + +router.get("/next", async (req: Request, res: Response) => { + await getAllInspectionsSortedNotHavingNewer(req, res); +}); + +router.get("/running", async (req: Request, res: Response) => { + await getAllInspectionsRunning(req, res); +}); + +router.get( + ["/vehicle/:relatedId", "/equipment/:relatedId", "/wearable/:relatedId"], + async (req: Request, res: Response) => { + if (req.path.startsWith("/vehicle")) { + req.params.related = "vehicle"; + } else if (req.path.startsWith("/equipment")) { + req.params.related = "equipment"; + } else { + req.params.related = "wearable"; + } + + await getAllInspectionsForRelated(req, res); + } +); + +router.get("/:id", async (req: Request, res: Response) => { + await getInspectionById(req, res); +}); + +router.get("/:id/printout", async (req: Request, res: Response) => { + await getInspectionPrintoutById(req, res); +}); + +router.get("/:id/:pointId/upload", async (req: Request, res: Response) => { + await getInspectionPointUpload(req, res); +}); + +router.post( + "/", + PermissionHelper.passCheckMiddleware("create", "unit", "inspection"), + async (req: Request, res: Response) => { + await createInspection(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), + async (req: Request, res: Response) => { + await updateInspectionById(req, res); + } +); + +router.patch( + "/:id/results", + PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), + inspectionFileUpload, + async (req: Request, res: Response) => { + await updateInspectionResults(req, res); + } +); + +router.patch( + "/:id/finish", + PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), + async (req: Request, res: Response) => { + await finishInspection(req, res); + } +); + +router.delete( + "/:id", + PermissionHelper.passCheckMiddleware("delete", "unit", "inspection"), + async (req: Request, res: Response) => { + await deleteInspectionById(req, res); + } +); + +export default router; diff --git a/src/routes/admin/unit/inspectionPlan.ts b/src/routes/admin/unit/inspectionPlan.ts new file mode 100644 index 0000000..914d9d3 --- /dev/null +++ b/src/routes/admin/unit/inspectionPlan.ts @@ -0,0 +1,75 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createInspectionPlan, + deleteInspectionPlanById, + getAllInspectionPlans, + getAllInspectionPlansForRelated, + getInspectionPlanById, + getInspectionPointsByPlanId, + updateInspectionPlanById, + updateInspectionPointsByPlanId, +} from "../../../controller/admin/unit/inspectionPlanController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllInspectionPlans(req, res); +}); + +router.get("/:id/points", async (req: Request, res: Response) => { + await getInspectionPointsByPlanId(req, res); +}); + +router.get( + ["/vehicleType/:relatedTypeId", "/equipmentType/:relatedTypeId", "/wearableType/:relatedTypeId"], + async (req: Request, res: Response) => { + if (req.path.startsWith("/vehicleType")) { + req.params.related = "vehicleType"; + } else if (req.path.startsWith("/equipmentType")) { + req.params.related = "equipmentType"; + } else { + req.params.related = "wearableType"; + } + + await getAllInspectionPlansForRelated(req, res); + } +); + +router.get("/:id", async (req: Request, res: Response) => { + await getInspectionPlanById(req, res); +}); + +router.post( + "/", + PermissionHelper.passCheckMiddleware("create", "unit", "inspection_plan"), + async (req: Request, res: Response) => { + await createInspectionPlan(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "inspection_plan"), + async (req: Request, res: Response) => { + await updateInspectionPlanById(req, res); + } +); + +router.patch( + "/:id/points", + PermissionHelper.passCheckMiddleware("update", "unit", "inspection_plan"), + async (req: Request, res: Response) => { + await updateInspectionPointsByPlanId(req, res); + } +); + +router.delete( + "/:id", + PermissionHelper.passCheckMiddleware("delete", "unit", "inspection_plan"), + async (req: Request, res: Response) => { + await deleteInspectionPlanById(req, res); + } +); + +export default router; diff --git a/src/routes/admin/unit/maintenance.ts b/src/routes/admin/unit/maintenance.ts new file mode 100644 index 0000000..76c80b1 --- /dev/null +++ b/src/routes/admin/unit/maintenance.ts @@ -0,0 +1,43 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import { + getAllMaintenancesByStatus, + getAllMaintenancesForRelated, + getMaintenanceById, + updateMaintenanceById, +} from "../../../controller/admin/unit/maintenanceController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllMaintenancesByStatus(req, res); +}); + +router.get( + ["/vehicle/:relatedId", "/equipment/:relatedId", "/wearable/:relatedId"], + async (req: Request, res: Response) => { + if (req.path.startsWith("/vehicle")) { + req.params.related = "vehicle"; + } else if (req.path.startsWith("/equipment")) { + req.params.related = "equipment"; + } else { + req.params.related = "wearable"; + } + + await getAllMaintenancesForRelated(req, res); + } +); + +router.get("/:id", async (req: Request, res: Response) => { + await getMaintenanceById(req, res); +}); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), + async (req: Request, res: Response) => { + await updateMaintenanceById(req, res); + } +); + +export default router; diff --git a/src/routes/admin/unit/vehicle.ts b/src/routes/admin/unit/vehicle.ts new file mode 100644 index 0000000..7ca588c --- /dev/null +++ b/src/routes/admin/unit/vehicle.ts @@ -0,0 +1,50 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createVehicle, + deleteVehicleById, + getAllVehicles, + getVehicleById, + getVehiclesByIds, + updateVehicleById, +} from "../../../controller/admin/unit/vehicleController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllVehicles(req, res); +}); + +router.get("/:id", async (req: Request, res: Response) => { + await getVehicleById(req, res); +}); + +router.post("/ids", async (req: Request, res: Response) => { + await getVehiclesByIds(req, res); +}); + +router.post( + "/", + PermissionHelper.passCheckMiddleware("create", "unit", "vehicle"), + async (req: Request, res: Response) => { + await createVehicle(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "vehicle"), + async (req: Request, res: Response) => { + await updateVehicleById(req, res); + } +); + +router.delete( + "/:id", + PermissionHelper.passCheckMiddleware("delete", "unit", "vehicle"), + async (req: Request, res: Response) => { + await deleteVehicleById(req, res); + } +); + +export default router; diff --git a/src/routes/admin/unit/vehicleType.ts b/src/routes/admin/unit/vehicleType.ts new file mode 100644 index 0000000..03f2989 --- /dev/null +++ b/src/routes/admin/unit/vehicleType.ts @@ -0,0 +1,45 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createVehicleType, + deleteVehicleTypeById, + getAllVehicleTypes, + getVehicleTypeById, + updateVehicleTypeById, +} from "../../../controller/admin/unit/vehicleTypeController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllVehicleTypes(req, res); +}); + +router.get("/:id", async (req: Request, res: Response) => { + await getVehicleTypeById(req, res); +}); + +router.post( + "/", + PermissionHelper.passCheckMiddleware("create", "unit", "vehicle_type"), + async (req: Request, res: Response) => { + await createVehicleType(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "vehicle_type"), + async (req: Request, res: Response) => { + await updateVehicleTypeById(req, res); + } +); + +router.delete( + "/:id", + PermissionHelper.passCheckMiddleware("delete", "unit", "vehicle_type"), + async (req: Request, res: Response) => { + await deleteVehicleTypeById(req, res); + } +); + +export default router; diff --git a/src/routes/admin/unit/wearable.ts b/src/routes/admin/unit/wearable.ts new file mode 100644 index 0000000..e1c1730 --- /dev/null +++ b/src/routes/admin/unit/wearable.ts @@ -0,0 +1,50 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createWearable, + deleteWearableById, + getAllWearables, + getWearableById, + getWearablesByIds, + updateWearableById, +} from "../../../controller/admin/unit/wearableController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllWearables(req, res); +}); + +router.get("/:id", async (req: Request, res: Response) => { + await getWearableById(req, res); +}); + +router.post("/ids", async (req: Request, res: Response) => { + await getWearablesByIds(req, res); +}); + +router.post( + "/", + PermissionHelper.passCheckMiddleware("create", "unit", "wearable"), + async (req: Request, res: Response) => { + await createWearable(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "wearable"), + async (req: Request, res: Response) => { + await updateWearableById(req, res); + } +); + +router.delete( + "/:id", + PermissionHelper.passCheckMiddleware("delete", "unit", "wearable"), + async (req: Request, res: Response) => { + await deleteWearableById(req, res); + } +); + +export default router; diff --git a/src/routes/admin/unit/wearableType.ts b/src/routes/admin/unit/wearableType.ts new file mode 100644 index 0000000..983e401 --- /dev/null +++ b/src/routes/admin/unit/wearableType.ts @@ -0,0 +1,45 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createWearableType, + deleteWearableTypeById, + getAllWearableTypes, + getWearableTypeById, + updateWearableTypeById, +} from "../../../controller/admin/unit/wearableTypeController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllWearableTypes(req, res); +}); + +router.get("/:id", async (req: Request, res: Response) => { + await getWearableTypeById(req, res); +}); + +router.post( + "/", + PermissionHelper.passCheckMiddleware("create", "unit", "wearable_type"), + async (req: Request, res: Response) => { + await createWearableType(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "wearable_type"), + async (req: Request, res: Response) => { + await updateWearableTypeById(req, res); + } +); + +router.delete( + "/:id", + PermissionHelper.passCheckMiddleware("delete", "unit", "wearable_type"), + async (req: Request, res: Response) => { + await deleteWearableTypeById(req, res); + } +); + +export default router; diff --git a/src/routes/public.ts b/src/routes/public.ts index d557dd7..360f962 100644 --- a/src/routes/public.ts +++ b/src/routes/public.ts @@ -14,6 +14,10 @@ router.get("/calendar", async (req, res) => { await getCalendarItemsByTypes(req, res); }); +router.post("/reportdamage", async (req, res) => { + res.send("TODO"); +}); + router.get("/configuration", async (req, res) => { await getApplicationConfig(req, res); }); diff --git a/src/service/unit/damageReportService.ts b/src/service/unit/damageReportService.ts new file mode 100644 index 0000000..21072c7 --- /dev/null +++ b/src/service/unit/damageReportService.ts @@ -0,0 +1,96 @@ +import { dataSource } from "../../data-source"; +import { damageReport } from "../../entity/unit/damageReport"; +import DatabaseActionException from "../../exceptions/databaseActionException"; + +export default abstract class DamageReportService { + private static query = () => + dataSource + .getRepository(damageReport) + .createQueryBuilder("damageReport") + .leftJoinAndSelect("damageReport.equipment", "equipment") + .leftJoinAndSelect("damageReport.vehicle", "vehicle") + .leftJoinAndSelect("damageReport.wearable", "wearable") + .leftJoinAndSelect("damageReport.maintenance", "maintenance"); + + /** + * @description get all damageReports By done + * @returns {Promise<[Array, number]>} + */ + static async getAll( + done = false, + { + offset = 0, + count = 25, + noLimit = false, + }: { + offset?: number; + count?: number; + noLimit?: boolean; + } + ): Promise<[Array, number]> { + let query = this.query().where({ done }); + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("damageReport.reportedAt", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "damageReport", err); + }); + } + + /** + * @description get all damageReports By related + * @returns {Promise<[Array, number]>} + */ + static async getAllForRelated( + where: { equipmentId: string } | { vehicleId: string } | { wearableId: string }, + { + offset = 0, + count = 25, + noLimit = false, + }: { + offset?: number; + count?: number; + noLimit?: boolean; + } + ): Promise<[Array, number]> { + let query = this.query().where(where); + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("damageReport.reportedAt", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "damageReport", err); + }); + } + + /** + * @description get damageReport by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await this.query() + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "damageReport", err); + }); + } +} diff --git a/src/service/unit/equipment/equipmentService.ts b/src/service/unit/equipment/equipmentService.ts new file mode 100644 index 0000000..921f574 --- /dev/null +++ b/src/service/unit/equipment/equipmentService.ts @@ -0,0 +1,79 @@ +import { In, Like } from "typeorm"; +import { dataSource } from "../../../data-source"; +import { equipment } from "../../../entity/unit/equipment/equipment"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class EquipmentService { + /** + * @description get all equipment + * @returns {Promise<[Array, number]>} + */ + static async getAll({ + offset = 0, + count = 25, + search = "", + noLimit = false, + ids = [], + }: { + offset?: number; + count?: number; + search?: string; + noLimit?: boolean; + ids?: Array; + }): Promise<[Array, number]> { + let query = dataSource + .getRepository(equipment) + .createQueryBuilder("equipment") + .leftJoinAndSelect("equipment.equipmentType", "equipmenttype"); + + if (search != "") { + query = query + .where({ + code: Like(`%${search}%`), + }) + .orWhere({ + name: Like(`%${search}%`), + }) + .orWhere({ + location: Like(`%${search}%`), + }); + } + + if (ids.length != 0) { + query = query.where({ id: In(ids) }); + } + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("name", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "equipment", err); + }); + } + + /** + * @description get equipment by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await dataSource + .getRepository(equipment) + .createQueryBuilder("equipment") + .leftJoinAndSelect("equipment.equipmentType", "equipmenttype") + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "equipment", err); + }); + } +} diff --git a/src/service/unit/equipment/equipmentTypeService.ts b/src/service/unit/equipment/equipmentTypeService.ts new file mode 100644 index 0000000..16a9527 --- /dev/null +++ b/src/service/unit/equipment/equipmentTypeService.ts @@ -0,0 +1,65 @@ +import { Like, In } from "typeorm"; +import { dataSource } from "../../../data-source"; +import { equipmentType } from "../../../entity/unit/equipment/equipmentType"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class EquipmentTypeService { + /** + * @description get all equipmentTypes + * @returns {Promise<[Array, number]>} + */ + static async getAll({ + offset = 0, + count = 25, + search = "", + noLimit = false, + }: { + offset?: number; + count?: number; + search?: string; + noLimit?: boolean; + }): Promise<[Array, number]> { + let query = dataSource + .getRepository(equipmentType) + .createQueryBuilder("equipmentType") + .loadRelationCountAndMap("equipmentType.equipmentCount", "equipmentType.equipment"); + + if (search != "") { + query = query.where({ + type: Like(`%${search}%`), + }); + } + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("type", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "equipmentType", err); + }); + } + + /** + * @description get equipmentType by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await dataSource + .getRepository(equipmentType) + .createQueryBuilder("equipmentType") + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "equipmentType", err); + }); + } +} diff --git a/src/service/unit/inspection/inspectionPlanService.ts b/src/service/unit/inspection/inspectionPlanService.ts new file mode 100644 index 0000000..0ee2e89 --- /dev/null +++ b/src/service/unit/inspection/inspectionPlanService.ts @@ -0,0 +1,128 @@ +import { Like, In } from "typeorm"; +import { dataSource } from "../../../data-source"; +import { inspectionPlan } from "../../../entity/unit/inspection/inspectionPlan"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class InspectionPlanService { + private static query = () => + dataSource + .getRepository(inspectionPlan) + .createQueryBuilder("inspectionPlan") + .leftJoinAndMapOne( + "inspectionPlan.latestVersionedPlan", + "inspectionPlan.versionedPlans", + "latestVersionedPlan", + 'latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX("ivp"."version") FROM "inspection_versioned_plan" "ivp" WHERE "ivp"."inspectionPlanId" = "inspectionPlan"."id")' + ) + .leftJoinAndSelect("latestVersionedPlan.inspectionPoints", "inspectionPoints") + .leftJoinAndSelect("inspectionPlan.equipmentType", "equipmentType") + .leftJoinAndSelect("inspectionPlan.vehicleType", "vehicleType") + .leftJoinAndSelect("inspectionPlan.wearableType", "wearableType"); + + /** + * @description get all inspectionPlans for related + * @returns {Promise<[Array, number]>} + */ + static async getAll({ + offset = 0, + count = 25, + search = "", + noLimit = false, + ids = [], + }: { + offset?: number; + count?: number; + search?: string; + noLimit?: boolean; + ids?: Array; + }): Promise<[Array, number]> { + let query = this.query(); + + if (search != "") { + query = query.where({ + title: Like(`%${search}%`), + }); + } + + if (ids.length != 0) { + query = query.where({ id: In(ids) }); + } + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("inspectionPlan.title", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionPlan", err); + }); + } + + /** + * @description get all inspectionPlans for related + * @returns {Promise<[Array, number]>} + */ + static async getAllForRelated( + where: { equipmentTypeId: string } | { vehicleTypeId: string } | { wearableTypeId: string }, + { + offset = 0, + count = 25, + search = "", + noLimit = false, + ids = [], + }: { + offset?: number; + count?: number; + search?: string; + noLimit?: boolean; + ids?: Array; + } + ): Promise<[Array, number]> { + let query = this.query().where(where); + + if (search != "") { + query = query.andWhere({ + title: Like(`%${search}%`), + }); + } + + if (ids.length != 0) { + query = query.andWhere({ id: In(ids) }); + } + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("inspectionPlan.title", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionPlan", err); + }); + } + + /** + * @description get inspectionPlan by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await this.query() + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionPlan", err); + }); + } +} diff --git a/src/service/unit/inspection/inspectionPointResultService.ts b/src/service/unit/inspection/inspectionPointResultService.ts new file mode 100644 index 0000000..deb56a8 --- /dev/null +++ b/src/service/unit/inspection/inspectionPointResultService.ts @@ -0,0 +1,45 @@ +import { dataSource } from "../../../data-source"; +import { inspectionPointResult } from "../../../entity/unit/inspection/inspectionPointResult"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class InspectionPointResultService { + /** + * @description get all inspectionPointResults + * @returns {Promise>} + */ + static async getAllForInspection(inspectionId: string): Promise> { + return await dataSource + .getRepository(inspectionPointResult) + .createQueryBuilder("inspectionPointResult") + .leftJoinAndSelect("inspectionPointResult.inspectionPoint", "inspectionPoint") + .where({ inspectionId }) + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionPointResult", err); + }); + } + /** + * @description get inspectionPointResults by inspection and point + * @returns {Promise>} + */ + static async getForInspectionAndPoint( + inspectionId: string, + inspectionPointId: string + ): Promise { + return await dataSource + .getRepository(inspectionPointResult) + .createQueryBuilder("inspectionPointResult") + .leftJoinAndSelect("inspectionPointResult.inspectionPoint", "inspectionPoint") + .where({ inspectionId, inspectionPointId }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionPointResult", err); + }); + } +} diff --git a/src/service/unit/inspection/inspectionPointService.ts b/src/service/unit/inspection/inspectionPointService.ts new file mode 100644 index 0000000..0285c9f --- /dev/null +++ b/src/service/unit/inspection/inspectionPointService.ts @@ -0,0 +1,42 @@ +import { dataSource } from "../../../data-source"; +import { inspectionPoint } from "../../../entity/unit/inspection/inspectionPoint"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class InspectionPointService { + /** + * @description get all inspectionPoints + * @returns {Promise>} + */ + static async getAllForVersionedPlan(versionedPlanId: string): Promise> { + return await dataSource + .getRepository(inspectionPoint) + .createQueryBuilder("inspectionPoint") + .where({ versionedPlanId }) + .orderBy("sort", "ASC") + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionPoint", err); + }); + } + + /** + * @description get inspectionPoint by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await dataSource + .getRepository(inspectionPoint) + .createQueryBuilder("inspectionPoint") + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionPoint", err); + }); + } +} diff --git a/src/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts new file mode 100644 index 0000000..5e85f83 --- /dev/null +++ b/src/service/unit/inspection/inspectionService.ts @@ -0,0 +1,191 @@ +import { IsNull, Not } from "typeorm"; +import { dataSource } from "../../../data-source"; +import { inspection } from "../../../entity/unit/inspection/inspection"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class InspectionService { + private static query = () => + dataSource + .getRepository(inspection) + .createQueryBuilder("inspection") + .leftJoinAndSelect("inspection.inspectionPlan", "inspectionPlan") + .leftJoinAndSelect("inspectionPlan.equipmentType", "equipmentType") + .leftJoinAndSelect("inspectionPlan.vehicleType", "vehicleType") + .leftJoinAndSelect("inspectionPlan.wearableType", "wearableType") + .leftJoinAndSelect("inspection.inspectionVersionedPlan", "inspectionVersionedPlan") + .leftJoinAndSelect("inspectionVersionedPlan.inspectionPoints", "inspectionPoints") + .leftJoinAndSelect("inspection.pointResults", "pointResults") + .leftJoinAndSelect("pointResults.inspectionPoint", "inspectionPoint") + .leftJoinAndSelect("inspection.equipment", "equipment") + .leftJoinAndSelect("inspection.vehicle", "vehicle") + .leftJoinAndSelect("inspection.wearable", "wearable"); + + private static minifiedQuery = () => + dataSource + .getRepository(inspection) + .createQueryBuilder("inspection") + .leftJoinAndSelect("inspection.inspectionPlan", "inspectionPlan") + .leftJoinAndSelect("inspectionPlan.equipmentType", "equipmentType") + .leftJoinAndSelect("inspectionPlan.vehicleType", "vehicleType") + .leftJoinAndSelect("inspectionPlan.wearableType", "wearableType") + .leftJoinAndSelect("inspection.equipment", "equipment") + .leftJoinAndSelect("inspection.vehicle", "vehicle") + .leftJoinAndSelect("inspection.wearable", "wearable"); + + /** + * @description get all inspections sorted by next inspection not having newer + * @returns {Promise>} + */ + static async getAllSortedNotHavingNewer({ + offset = 0, + count = 25, + noLimit = false, + }: { + offset?: number; + count?: number; + noLimit?: boolean; + }): Promise<[Array, number]> { + let query = this.query().where({ hasNewer: false }); + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("inspection.nextInspection", "ASC", "NULLS LAST") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspection", err); + }); + } + + /** + * @description get all inspections running + * @returns {Promise>} + */ + static async getAllRunning({ + offset = 0, + count = 25, + noLimit = false, + }: { + offset?: number; + count?: number; + noLimit?: boolean; + }): Promise<[Array, number]> { + let query = this.minifiedQuery().where({ finishedAt: IsNull() }); + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("inspection.createdAt", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspection", err); + }); + } + + /** + * @description get all inspections for related + * @returns {Promise>} + */ + static async getAllForRelated( + where: { equipmentId: string } | { vehicleId: string } | { wearableId: string }, + { + offset = 0, + count = 25, + noLimit = false, + }: { + offset?: number; + count?: number; + noLimit?: boolean; + } + ): Promise<[Array, number]> { + let query = this.minifiedQuery().where(where); + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("inspection.createdAt", "DESC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspection", err); + }); + } + + /** + * @description get inspection by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await this.query() + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspection", err); + }); + } + + /** + * @description uses versionedPlan + * @returns {Promise} + */ + static async existsUnfinishedInspectionToPlan( + inspectionPlanId: string, + relation: "vehicle" | "equipment" | "wearable", + relationId: string + ): Promise { + let where: { equipmentId: string } | { vehicleId: string } | { wearableId: string }; + if (relation == "equipment") { + where = { equipmentId: relationId }; + } else if (relation == "vehicle") { + where = { vehicleId: relationId }; + } else { + where = { wearableId: relationId }; + } + return await dataSource + .getRepository(inspection) + .createQueryBuilder("inspection") + .where({ inspectionPlanId, finishedAt: IsNull(), ...where }) + .getExists() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "used inspection", err); + }); + } + + /** + * @description uses versionedPlan + * @returns {Promise} + */ + static async usesVersionedInspectionPlan(inspectionVersionedPlanId: string): Promise { + return await dataSource + .getRepository(inspection) + .createQueryBuilder("inspection") + .where({ inspectionVersionedPlanId }) + .getExists() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "used inspection", err); + }); + } +} diff --git a/src/service/unit/inspection/inspectionVersionedPlanService.ts b/src/service/unit/inspection/inspectionVersionedPlanService.ts new file mode 100644 index 0000000..00f7500 --- /dev/null +++ b/src/service/unit/inspection/inspectionVersionedPlanService.ts @@ -0,0 +1,83 @@ +import { dataSource } from "../../../data-source"; +import { inspectionVersionedPlan } from "../../../entity/unit/inspection/inspectionVersionedPlan"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class InspectionVersionedPlanService { + /** + * @description get all inspectionVersionedPlans + * @returns {Promise>} + */ + static async getAllForInspectionPlan(inspectionPlanId: string): Promise> { + return await dataSource + .getRepository(inspectionVersionedPlan) + .createQueryBuilder("inspectionVersionedPlan") + .leftJoinAndSelect("inspectionVersionedPlan.inspectionPoints", "inspectionPoints") + .where({ inspectionPlanId }) + .orderBy("version", "ASC") + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionVersionedPlan", err); + }); + } + + /** + * @description get latest inspectionVersionedPlan for plan + * @returns {Promise>} + */ + static async getLatestForInspectionPlan(inspectionPlanId: string): Promise { + return await dataSource + .getRepository(inspectionVersionedPlan) + .createQueryBuilder("inspectionVersionedPlan") + .leftJoinAndSelect("inspectionVersionedPlan.inspectionPoints", "inspectionPoints") + .where({ inspectionPlanId }) + .orderBy("inspectionVersionedPlan.version", "DESC") + .limit(1) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionVersionedPlan", err); + }); + } + + /** + * @description get inspectionVersionedPlan by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await dataSource + .getRepository(inspectionVersionedPlan) + .createQueryBuilder("inspectionVersionedPlan") + .leftJoinAndSelect("inspectionVersionedPlan.inspectionPoints", "inspectionPoints") + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionVersionedPlan", err); + }); + } + + /** + * @description count for plan id + * @returns {Promise} + */ + static async countForPlanId(planId: string): Promise { + return await dataSource + .getRepository(inspectionVersionedPlan) + .createQueryBuilder("inspectionVersionedPlan") + .where({ inspectionPlanId: planId }) + .getCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionVersionedPlan", err); + }); + } +} diff --git a/src/service/unit/maintenanceService.ts b/src/service/unit/maintenanceService.ts new file mode 100644 index 0000000..d9d997d --- /dev/null +++ b/src/service/unit/maintenanceService.ts @@ -0,0 +1,95 @@ +import { dataSource } from "../../data-source"; +import { maintenance } from "../../entity/unit/maintenance"; +import DatabaseActionException from "../../exceptions/databaseActionException"; + +export default abstract class MaintenanceService { + private static query = () => + dataSource + .getRepository(maintenance) + .createQueryBuilder("maintenance") + .leftJoinAndSelect("maintenance.equipment", "equipment") + .leftJoinAndSelect("maintenance.vehicle", "vehicle") + .leftJoinAndSelect("maintenance.wearable", "wearable") + .leftJoinAndSelect("maintenance.reports", "reports"); + + /** + * @description get all maintenances + * @returns {Promise<[Array, number]>} + */ + static async getAll( + done = false, + { + offset = 0, + count = 25, + noLimit = false, + }: { + offset?: number; + count?: number; + noLimit?: boolean; + } + ): Promise<[Array, number]> { + let query = this.query().where({ done }); + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("maintenance.createdAt", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "maintenance", err); + }); + } + + /** + * @description get all maintenances By related + * @returns {Promise<[Array, number]>} + */ + static async getAllForRelated( + where: { equipmentId: string } | { vehicleId: string } | { wearableId: string }, + { + offset = 0, + count = 25, + noLimit = false, + }: { + offset?: number; + count?: number; + noLimit?: boolean; + } + ): Promise<[Array, number]> { + let query = this.query().where(where); + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("maintenance.createdAt", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "maintenance", err); + }); + } + + /** + * @description get maintenance by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await this.query() + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "maintenance", err); + }); + } +} diff --git a/src/service/unit/vehicle/vehicleService.ts b/src/service/unit/vehicle/vehicleService.ts new file mode 100644 index 0000000..61c2b73 --- /dev/null +++ b/src/service/unit/vehicle/vehicleService.ts @@ -0,0 +1,79 @@ +import { Like, In } from "typeorm"; +import { dataSource } from "../../../data-source"; +import { vehicle } from "../../../entity/unit/vehicle/vehicle"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class VehicleService { + /** + * @description get all vehicles + * @returns {Promise<[Array, number]>} + */ + static async getAll({ + offset = 0, + count = 25, + search = "", + noLimit = false, + ids = [], + }: { + offset?: number; + count?: number; + search?: string; + noLimit?: boolean; + ids?: Array; + }): Promise<[Array, number]> { + let query = dataSource + .getRepository(vehicle) + .createQueryBuilder("vehicle") + .leftJoinAndSelect("vehicle.vehicleType", "vehicletype"); + + if (search != "") { + query = query + .where({ + code: Like(`%${search}%`), + }) + .orWhere({ + name: Like(`%${search}%`), + }) + .orWhere({ + location: Like(`%${search}%`), + }); + } + + if (ids.length != 0) { + query = query.where({ id: In(ids) }); + } + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("name", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "vehicle", err); + }); + } + + /** + * @description get vehicle by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await dataSource + .getRepository(vehicle) + .createQueryBuilder("vehicle") + .leftJoinAndSelect("vehicle.vehicleType", "vehicletype") + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "vehicle", err); + }); + } +} diff --git a/src/service/unit/vehicle/vehicleTypeService.ts b/src/service/unit/vehicle/vehicleTypeService.ts new file mode 100644 index 0000000..438d46b --- /dev/null +++ b/src/service/unit/vehicle/vehicleTypeService.ts @@ -0,0 +1,65 @@ +import { Like, In } from "typeorm"; +import { dataSource } from "../../../data-source"; +import { vehicleType } from "../../../entity/unit/vehicle/vehicleType"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class VehicleTypeService { + /** + * @description get all vehicleTypes + * @returns {Promise<[Array, number]>} + */ + static async getAll({ + offset = 0, + count = 25, + search = "", + noLimit = false, + }: { + offset?: number; + count?: number; + search?: string; + noLimit?: boolean; + }): Promise<[Array, number]> { + let query = dataSource + .getRepository(vehicleType) + .createQueryBuilder("vehicleType") + .loadRelationCountAndMap("vehicleType.vehicleCount", "vehicleType.vehicle"); + + if (search != "") { + query = query.where({ + type: Like(`%${search}%`), + }); + } + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("type", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "vehicleType", err); + }); + } + + /** + * @description get vehicleType by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await dataSource + .getRepository(vehicleType) + .createQueryBuilder("vehicleType") + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "vehicleType", err); + }); + } +} diff --git a/src/service/unit/wearable/wearableService.ts b/src/service/unit/wearable/wearableService.ts new file mode 100644 index 0000000..d016b0f --- /dev/null +++ b/src/service/unit/wearable/wearableService.ts @@ -0,0 +1,81 @@ +import { Like, In } from "typeorm"; +import { dataSource } from "../../../data-source"; +import { wearable } from "../../../entity/unit/wearable/wearable"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class WearableService { + /** + * @description get all wearables + * @returns {Promise<[Array, number]>} + */ + static async getAll({ + offset = 0, + count = 25, + search = "", + noLimit = false, + ids = [], + }: { + offset?: number; + count?: number; + search?: string; + noLimit?: boolean; + ids?: Array; + }): Promise<[Array, number]> { + let query = dataSource + .getRepository(wearable) + .createQueryBuilder("wearable") + .leftJoinAndSelect("wearable.wearableType", "wearabletype") + .leftJoinAndSelect("wearable.wearer", "wearer"); + + if (search != "") { + query = query + .where({ + code: Like(`%${search}%`), + }) + .orWhere({ + name: Like(`%${search}%`), + }) + .orWhere({ + location: Like(`%${search}%`), + }); + } + + if (ids.length != 0) { + query = query.where({ id: In(ids) }); + } + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("name", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "wearable", err); + }); + } + + /** + * @description get wearable by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await dataSource + .getRepository(wearable) + .createQueryBuilder("wearable") + .leftJoinAndSelect("wearable.wearableType", "wearabletype") + .leftJoinAndSelect("wearable.wearer", "wearer") + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "wearable", err); + }); + } +} diff --git a/src/service/unit/wearable/wearableTypeService.ts b/src/service/unit/wearable/wearableTypeService.ts new file mode 100644 index 0000000..a9f06f3 --- /dev/null +++ b/src/service/unit/wearable/wearableTypeService.ts @@ -0,0 +1,65 @@ +import { In, Like } from "typeorm"; +import { dataSource } from "../../../data-source"; +import { wearableType } from "../../../entity/unit/wearable/wearableType"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class WearableTypeService { + /** + * @description get all wearableTypes + * @returns {Promise<[Array, number]>} + */ + static async getAll({ + offset = 0, + count = 25, + search = "", + noLimit = false, + }: { + offset?: number; + count?: number; + search?: string; + noLimit?: boolean; + }): Promise<[Array, number]> { + let query = dataSource + .getRepository(wearableType) + .createQueryBuilder("wearableType") + .loadRelationCountAndMap("wearableType.wearableCount", "wearableType.wearable"); + + if (search != "") { + query = query.where({ + type: Like(`%${search}%`), + }); + } + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("type", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "wearableType", err); + }); + } + + /** + * @description get wearableType by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await dataSource + .getRepository(wearableType) + .createQueryBuilder("wearableType") + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "wearableType", err); + }); + } +} diff --git a/src/templates/inspection.body.template.html b/src/templates/inspection.body.template.html new file mode 100644 index 0000000..cc2a6e5 --- /dev/null +++ b/src/templates/inspection.body.template.html @@ -0,0 +1,69 @@ + + + + + Prüfung + + +

{{planTitle}} {{related.name}} {{#if related.code}} {{related.code}}{{/if}}

+

Prüfer: {{inspector}}

+

durchgeführt am: {{longdate finishedAt}}

+ {{#if nextInspection}} +

nächste Prüfung am: {{longdate nextInspection}}

+ {{/if}} +
+

{{related.name}} in Betrieb genommen am: {{date related.commissioned}}

+
+

Kontext: {{context}}

+
+ +

Prüfpunkte:

+ + {{#each checks}} + + + + + {{/each}} +
+ {{this.title}} +
+ {{#if this.description}} > {{this.description}} +
+ {{/if}} + (Typ: {{this.type}}) +
+ {{#eq this.type "file"}} {{#eq this.others "img"}} + + {{else}} siehe Anhang {{/eq}} {{else}} {{this.value}} {{/eq}} +
+ + + diff --git a/src/type/permissionTypes.ts b/src/type/permissionTypes.ts index 013615c..2442d9f 100644 --- a/src/type/permissionTypes.ts +++ b/src/type/permissionTypes.ts @@ -1,28 +1,45 @@ -export type PermissionSection = "club" | "configuration" | "management"; +export type PermissionSection = "club" | "unit" | "configuration" | "management"; export type PermissionModule = + // club | "member" | "calendar" | "newsletter" - | "newsletter_config" | "protocol" + | "query" | "listprint" + // unit + | "equipment" + | "equipment_type" + | "vehicle" + | "vehicle_type" + | "wearable" + | "wearable_type" + | "inspection" + | "inspection_plan" + | "respiratory_gear" + | "respiratory_wearer" + | "respiratory_mission" + | "damage_report" + | "maintenance" + // configuration | "qualification" | "award" | "executive_position" | "communication_type" | "membership_status" + | "newsletter_config" | "salutation" | "education" | "calendar_type" - | "user" - | "role" - | "webapi" - | "query" | "query_store" | "template" | "template_usage" | "backup" + // management + | "user" + | "role" + | "webapi" | "setting"; export type PermissionType = "read" | "create" | "update" | "delete"; @@ -57,35 +74,67 @@ export type SectionsAndModulesObject = { }>; }; -export const permissionSections: Array = ["club", "configuration", "management"]; +export const permissionSections: Array = ["club", "unit", "configuration", "management"]; export const permissionModules: Array = [ + // club "member", "calendar", "newsletter", - "newsletter_config", "protocol", + "query", "listprint", + // unit + "equipment", + "equipment_type", + "vehicle", + "vehicle_type", + "wearable", + "wearable_type", + "inspection", + "inspection_plan", + "respiratory_gear", + "respiratory_wearer", + "respiratory_mission", + "damage_report", + "maintenance", + // configuration "qualification", "award", "executive_position", "communication_type", "membership_status", + "newsletter_config", "salutation", "education", "calendar_type", - "user", - "role", - "webapi", - "query", "query_store", "template", "template_usage", "backup", + // management + "user", + "role", + "webapi", "setting", ]; export const permissionTypes: Array = ["read", "create", "update", "delete"]; export const sectionsAndModules: SectionsAndModulesObject = { club: ["member", "calendar", "newsletter", "protocol", "query", "listprint"], + unit: [ + "equipment", + "equipment_type", + "vehicle", + "vehicle_type", + "wearable", + "wearable_type", + "inspection", + "inspection_plan", + "respiratory_gear", + "respiratory_wearer", + "respiratory_mission", + "damage_report", + "maintenance", + ], configuration: [ "qualification", "award", diff --git a/src/type/templateTypes.ts b/src/type/templateTypes.ts index cede2bb..2f260e7 100644 --- a/src/type/templateTypes.ts +++ b/src/type/templateTypes.ts @@ -8,4 +8,5 @@ export const availableTemplates: Array = [ "listprint.member", "newsletter", "protocol", + "inspection", ]; diff --git a/src/viewmodel/admin/unit/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport.models.ts new file mode 100644 index 0000000..9427d8a --- /dev/null +++ b/src/viewmodel/admin/unit/damageReport.models.ts @@ -0,0 +1,32 @@ +import { EquipmentViewModel } from "./equipment/equipment.models"; +import { MaintenanceViewModel } from "./maintenance.models"; +import { VehicleViewModel } from "./vehicle/vehicle.models"; +import { WearableViewModel } from "./wearable/wearable.models"; + +export type DamageReportAssigned = { + relatedId: string; +} & ( + | { + assigned: "equipment"; + related: EquipmentViewModel; + } + | { + assigned: "vehicle"; + related: VehicleViewModel; + } + | { + assigned: "wearable"; + related: WearableViewModel; + } +); + +export type DamageReportViewModel = { + id: string; + reportedAt: Date; + status: string; + done: boolean; + description: string; + imageCount: number; + reportedBy: string; + maintenance?: MaintenanceViewModel; +} & DamageReportAssigned; diff --git a/src/viewmodel/admin/unit/equipment/equipment.models.ts b/src/viewmodel/admin/unit/equipment/equipment.models.ts new file mode 100644 index 0000000..2990c1d --- /dev/null +++ b/src/viewmodel/admin/unit/equipment/equipment.models.ts @@ -0,0 +1,12 @@ +import type { EquipmentTypeViewModel } from "./equipmentType.models"; + +export interface EquipmentViewModel { + id: string; + code?: string; + name: string; + location: string; + commissioned: Date; + decommissioned?: Date; + equipmentTypeId: string; + equipmentType: EquipmentTypeViewModel; +} diff --git a/src/viewmodel/admin/unit/equipment/equipmentType.models.ts b/src/viewmodel/admin/unit/equipment/equipmentType.models.ts new file mode 100644 index 0000000..b7a1648 --- /dev/null +++ b/src/viewmodel/admin/unit/equipment/equipmentType.models.ts @@ -0,0 +1,6 @@ +export interface EquipmentTypeViewModel { + id: string; + type: string; + description: string; + equipmentCount: number; +} diff --git a/src/viewmodel/admin/unit/inspection/inspection.models.ts b/src/viewmodel/admin/unit/inspection/inspection.models.ts new file mode 100644 index 0000000..87af590 --- /dev/null +++ b/src/viewmodel/admin/unit/inspection/inspection.models.ts @@ -0,0 +1,66 @@ +import type { EquipmentViewModel } from "../equipment/equipment.models"; +import type { + InspectionPlanViewModel, + InspectionPointViewModel, + InspectionVersionedPlanViewModel, +} from "./inspectionPlan.models"; +import type { VehicleViewModel } from "../vehicle/vehicle.models"; +import { WearableViewModel } from "../wearable/wearable.models"; + +export type InspectionRelated = { + relatedId: string; +} & ( + | { + assigned: "equipment"; + related: EquipmentViewModel; + } + | { + assigned: "vehicle"; + related: VehicleViewModel; + } + | { + assigned: "wearable"; + related: WearableViewModel; + } +); + +export type InspectionViewModel = { + id: string; + inspectionPlanId: string; + inspectionPlan: InspectionPlanViewModel; + inspectionVersionedPlanId: string; + inspectionVersionedPlan: InspectionVersionedPlanViewModel; + context: string; + created: Date; + finished?: Date; + isOpen: boolean; + nextInspection?: Date; + checks: Array; +} & InspectionRelated; + +export type MinifiedInspectionViewModel = { + id: string; + inspectionPlanId: string; + inspectionPlan: InspectionPlanViewModel; + context: string; + created: Date; + finished?: Date; + isOpen: boolean; + nextInspection?: Date; + relatedId: string; +} & InspectionRelated; + +export type InspectionNextViewModel = { + id: string; + inspectionPlanId: string; + inspectionPlan: InspectionPlanViewModel; + dueDate: Date; + relatedId: string; +} & InspectionRelated; + +export interface InspectionPointResultViewModel { + inspectionId: string; + inspectionPointId: string; + inspectionPoint: InspectionPointViewModel; + value: string; +} diff --git a/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts new file mode 100644 index 0000000..67d7957 --- /dev/null +++ b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts @@ -0,0 +1,53 @@ +import { InspectionPointEnum } from "../../../../enums/inspectionEnum"; +import type { EquipmentViewModel } from "../equipment/equipment.models"; +import { EquipmentTypeViewModel } from "../equipment/equipmentType.models"; +import type { VehicleViewModel } from "../vehicle/vehicle.models"; +import { VehicleTypeViewModel } from "../vehicle/vehicleType.models"; +import { WearableTypeViewModel } from "../wearable/wearableType.models"; + +export type PlanTimeDefinition = `${number}-${"d" | "m" | "y"}` | `${number}/${number | "*"}`; + +export type InspectionPlanRelated = { + relatedId: string; +} & ( + | { + assigned: "equipment"; + related: EquipmentTypeViewModel; + } + | { + assigned: "vehicle"; + related: VehicleTypeViewModel; + } + | { + assigned: "wearable"; + related: WearableTypeViewModel; + } +); + +export type InspectionPlanViewModel = { + id: string; + title: string; + inspectionInterval: PlanTimeDefinition; + remindTime: PlanTimeDefinition; + version: number; + created: Date; + inspectionPoints: InspectionPointViewModel[]; +} & InspectionPlanRelated; + +export interface InspectionVersionedPlanViewModel { + id: string; + version: number; + created: Date; + inspectionPoints: InspectionPointViewModel[]; +} + +export interface InspectionPointViewModel { + id: string; + title: string; + description: string; + type: InspectionPointEnum; + min?: number; + max?: number; + others?: string; + sort: number; +} diff --git a/src/viewmodel/admin/unit/maintenance.models.ts b/src/viewmodel/admin/unit/maintenance.models.ts new file mode 100644 index 0000000..98f22f2 --- /dev/null +++ b/src/viewmodel/admin/unit/maintenance.models.ts @@ -0,0 +1,30 @@ +import { DamageReportViewModel } from "./damageReport.models"; +import { EquipmentViewModel } from "./equipment/equipment.models"; +import { VehicleViewModel } from "./vehicle/vehicle.models"; +import { WearableViewModel } from "./wearable/wearable.models"; + +export type MaintenanceAssigned = { + relatedId: string; +} & ( + | { + assigned: "equipment"; + related: EquipmentViewModel; + } + | { + assigned: "vehicle"; + related: VehicleViewModel; + } + | { + assigned: "wearable"; + related: WearableViewModel; + } +); + +export type MaintenanceViewModel = { + id: string; + createdAt: Date; + status: string; + done: boolean; + description: string; + reports: DamageReportViewModel[]; +} & MaintenanceAssigned; diff --git a/src/viewmodel/admin/unit/respiratory/respiratoryGear.models.ts b/src/viewmodel/admin/unit/respiratory/respiratoryGear.models.ts new file mode 100644 index 0000000..07f4ec8 --- /dev/null +++ b/src/viewmodel/admin/unit/respiratory/respiratoryGear.models.ts @@ -0,0 +1,16 @@ +import type { EquipmentViewModel } from "../equipment/equipment.models"; + +export interface RespiratoryGearViewModel { + id: string; + equipmentId: string; + equipment: EquipmentViewModel; +} + +export interface CreateRespiratoryGearViewModel { + equipmentId: string; +} + +export interface UpdateRespiratoryGearViewModel { + id: string; + equipmentId: string; +} diff --git a/src/viewmodel/admin/unit/respiratory/respiratoryMission.models.ts b/src/viewmodel/admin/unit/respiratory/respiratoryMission.models.ts new file mode 100644 index 0000000..49c6e7f --- /dev/null +++ b/src/viewmodel/admin/unit/respiratory/respiratoryMission.models.ts @@ -0,0 +1,20 @@ +export interface RespiratoryMissionViewModel { + id: string; + date: Date; + title: string; + description: string; + // refs to used respiratory gear and wearers +} + +export interface CreateRespiratoryMissionViewModel { + date: Date; + title: string; + description: string; +} + +export interface UpdateRespiratoryMissionViewModel { + id: string; + date: Date; + title: string; + description: string; +} diff --git a/src/viewmodel/admin/unit/respiratory/respiratoryWearer.models.ts b/src/viewmodel/admin/unit/respiratory/respiratoryWearer.models.ts new file mode 100644 index 0000000..2957408 --- /dev/null +++ b/src/viewmodel/admin/unit/respiratory/respiratoryWearer.models.ts @@ -0,0 +1,16 @@ +import { MemberViewModel } from "../../club/member/member.models"; + +export interface RespiratoryWearerViewModel { + id: string; + memberId: string; + member: MemberViewModel; +} + +export interface CreateRespiratoryWearerViewModel { + memberId: string; +} + +export interface UpdateRespiratoryWearerViewModel { + id: string; + memberId: string; +} diff --git a/src/viewmodel/admin/unit/vehicle/vehicle.models.ts b/src/viewmodel/admin/unit/vehicle/vehicle.models.ts new file mode 100644 index 0000000..b25ab0c --- /dev/null +++ b/src/viewmodel/admin/unit/vehicle/vehicle.models.ts @@ -0,0 +1,12 @@ +import type { VehicleTypeViewModel } from "./vehicleType.models"; + +export interface VehicleViewModel { + id: string; + code?: string; + name: string; + location: string; + commissioned: Date; + decommissioned?: Date; + vehicleTypeId: string; + vehicleType: VehicleTypeViewModel; +} diff --git a/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts b/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts new file mode 100644 index 0000000..a005853 --- /dev/null +++ b/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts @@ -0,0 +1,6 @@ +export interface VehicleTypeViewModel { + id: string; + type: string; + description: string; + vehicleCount: number; +} diff --git a/src/viewmodel/admin/unit/wearable/wearable.models.ts b/src/viewmodel/admin/unit/wearable/wearable.models.ts new file mode 100644 index 0000000..e3bacac --- /dev/null +++ b/src/viewmodel/admin/unit/wearable/wearable.models.ts @@ -0,0 +1,15 @@ +import { MemberViewModel } from "../../club/member/member.models"; +import type { WearableTypeViewModel } from "./wearableType.models"; + +export interface WearableViewModel { + id: string; + code?: string; + name: string; + location: string; + commissioned: Date; + decommissioned?: Date; + wearerId?: string; + wearer?: MemberViewModel; + wearableTypeId: string; + wearableType: WearableTypeViewModel; +} diff --git a/src/viewmodel/admin/unit/wearable/wearableType.models.ts b/src/viewmodel/admin/unit/wearable/wearableType.models.ts new file mode 100644 index 0000000..0c15a26 --- /dev/null +++ b/src/viewmodel/admin/unit/wearable/wearableType.models.ts @@ -0,0 +1,6 @@ +export interface WearableTypeViewModel { + id: string; + type: string; + description: string; + wearableCount: number; +}