From 15a511f942bbd846913ff7552c9a1c3ac2e266fd Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 24 May 2025 11:30:58 +0200 Subject: [PATCH 01/55] add viewmodels from frontend --- .../unit/damageReport/damageReport.models.ts | 27 ++++++++++++ .../admin/unit/equipment/equipment.models.ts | 31 ++++++++++++++ .../equipmentType/equipmentType.models.ts | 19 +++++++++ .../unit/inspection/inspection.models.ts | 37 +++++++++++++++++ .../inspectionPlan/inspectionPlan.models.ts | 41 +++++++++++++++++++ .../respiratoryGear/respiratoryGear.models.ts | 16 ++++++++ .../respiratoryMission.models.ts | 20 +++++++++ .../respiratoryWearer.models.ts | 16 ++++++++ .../admin/unit/vehicle/vehicle.models.ts | 31 ++++++++++++++ .../unit/vehicleType/vehicleType.models.ts | 19 +++++++++ .../admin/unit/wearable/wearable.models.ts | 34 +++++++++++++++ .../unit/wearableType/wearableType.models.ts | 16 ++++++++ 12 files changed, 307 insertions(+) create mode 100644 src/viewmodel/admin/unit/damageReport/damageReport.models.ts create mode 100644 src/viewmodel/admin/unit/equipment/equipment.models.ts create mode 100644 src/viewmodel/admin/unit/equipmentType/equipmentType.models.ts create mode 100644 src/viewmodel/admin/unit/inspection/inspection.models.ts create mode 100644 src/viewmodel/admin/unit/inspectionPlan/inspectionPlan.models.ts create mode 100644 src/viewmodel/admin/unit/respiratoryGear/respiratoryGear.models.ts create mode 100644 src/viewmodel/admin/unit/respiratoryMission/respiratoryMission.models.ts create mode 100644 src/viewmodel/admin/unit/respiratoryWearer/respiratoryWearer.models.ts create mode 100644 src/viewmodel/admin/unit/vehicle/vehicle.models.ts create mode 100644 src/viewmodel/admin/unit/vehicleType/vehicleType.models.ts create mode 100644 src/viewmodel/admin/unit/wearable/wearable.models.ts create mode 100644 src/viewmodel/admin/unit/wearableType/wearableType.models.ts diff --git a/src/viewmodel/admin/unit/damageReport/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport/damageReport.models.ts new file mode 100644 index 0000000..b95ee33 --- /dev/null +++ b/src/viewmodel/admin/unit/damageReport/damageReport.models.ts @@ -0,0 +1,27 @@ +import type { EquipmentViewModel } from "../equipment/equipment.models"; +import type { VehicleViewModel } from "../vehicle/vehicle.models"; +import type { WearableViewModel } from "../wearable/wearable.models"; + +export interface DamageReportViewModel { + id: string; + reported: Date; + status: string; + done: boolean; + description: string; + providedImage: Array; + relatedId: string; + related: EquipmentViewModel | VehicleViewModel | WearableViewModel; + affected: "equipment" | "vehicle" | "wearable"; +} + +export interface CreateDamageReportViewModel { + description: string; + affectedId: string; + affected: "equipment" | "vehicle" | "wearable"; +} + +export interface UpdateDamageReportViewModel { + id: string; + status: string; + done: boolean; +} 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..dcd4cea --- /dev/null +++ b/src/viewmodel/admin/unit/equipment/equipment.models.ts @@ -0,0 +1,31 @@ +import type { EquipmentTypeViewModel } from "../equipmentType/equipmentType.models"; +import type { InspectionViewModel } from "../inspection/inspection.models"; + +export interface EquipmentViewModel { + id: string; + code?: string; + name: string; + location: string; + commissioned: Date; + decommissioned?: Date; + equipmentTypeId: string; + equipmentType: EquipmentTypeViewModel; + inspections: Array; +} + +export interface CreateEquipmentViewModel { + code?: string; + name: string; + location: string; + commissioned: Date; + equipmentTypeId: string; +} + +export interface UpdateEquipmentViewModel { + id: string; + code?: string; + name: string; + location: string; + commissioned: Date; + decommissioned?: Date; +} diff --git a/src/viewmodel/admin/unit/equipmentType/equipmentType.models.ts b/src/viewmodel/admin/unit/equipmentType/equipmentType.models.ts new file mode 100644 index 0000000..e3116c9 --- /dev/null +++ b/src/viewmodel/admin/unit/equipmentType/equipmentType.models.ts @@ -0,0 +1,19 @@ +import type { InspectionPlanViewModel } from "../inspectionPlan/inspectionPlan.models"; + +export interface EquipmentTypeViewModel { + id: string; + type: string; + description: string; + inspectionPlans: Array; +} + +export interface CreateEquipmentTypeViewModel { + type: string; + description: string; +} + +export interface UpdateEquipmentTypeViewModel { + id: string; + type: string; + description: string; +} 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..a25c279 --- /dev/null +++ b/src/viewmodel/admin/unit/inspection/inspection.models.ts @@ -0,0 +1,37 @@ +import type { EquipmentViewModel } from "../equipment/equipment.models"; +import type { + InspectionPlanViewModel, + InspectionVersionedPlanViewModel, +} from "../inspectionPlan/inspectionPlan.models"; +import type { VehicleViewModel } from "../vehicle/vehicle.models"; + +export interface InspectionViewModel { + id: string; + inspectionPlanId: string; + inspectionPlan: InspectionPlanViewModel; + inspectionVersionedPlanId: string; + inspectionVersionedPlan: InspectionVersionedPlanViewModel; + context: string; + created: Date; + finished?: Date; + isOpen: boolean; + nextInspection?: Date; + checks: Array; + relatedId: string; + related: EquipmentViewModel | VehicleViewModel; +} + +export interface InspectionPointViewModel { + id: string; + title: string; + description: string; + type: "iO-niO" | "text" | "number"; + min?: number; +} + +export interface InspectionPointResultViewModel { + inspectionId: string; + inspectionVersionedPlanId: string; + inspectionPointId: string; + value: string; +} diff --git a/src/viewmodel/admin/unit/inspectionPlan/inspectionPlan.models.ts b/src/viewmodel/admin/unit/inspectionPlan/inspectionPlan.models.ts new file mode 100644 index 0000000..cdfb8b2 --- /dev/null +++ b/src/viewmodel/admin/unit/inspectionPlan/inspectionPlan.models.ts @@ -0,0 +1,41 @@ +import type { EquipmentViewModel } from "../equipment/equipment.models"; +import type { EquipmentTypeViewModel } from "../equipmentType/equipmentType.models"; +import type { InspectionPointViewModel } from "../inspection/inspection.models"; +import type { VehicleViewModel } from "../vehicle/vehicle.models"; +import type { VehicleTypeViewModel } from "../vehicleType/vehicleType.models"; + +export type PlanTimeDefinition = `${number}-${"d" | "m" | "y"}` | `${number}/${number | "*"}`; + +export interface InspectionPlanViewModel { + id: string; + title: string; + inspectionInterval: PlanTimeDefinition; + remindTime: PlanTimeDefinition; + version: number; + created: Date; + inspectionPoints: InspectionPointViewModel[]; + relatedId: string; + related: EquipmentTypeViewModel | VehicleTypeViewModel; +} + +export interface InspectionVersionedPlanViewModel { + id: string; + version: number; + created: Date; + inspectionPoints: InspectionPointViewModel[]; +} + +export interface CreateInspectionPlanViewModel { + title: string; + inspectionInterval: PlanTimeDefinition; + remindTime: PlanTimeDefinition; + relatedId: string; + assigned: "vehicle" | "equipment"; +} + +export interface UpdateInspectionPlanViewModel { + id: string; + title: string; + inspectionInterval: PlanTimeDefinition; + remindTime?: PlanTimeDefinition; +} diff --git a/src/viewmodel/admin/unit/respiratoryGear/respiratoryGear.models.ts b/src/viewmodel/admin/unit/respiratoryGear/respiratoryGear.models.ts new file mode 100644 index 0000000..07f4ec8 --- /dev/null +++ b/src/viewmodel/admin/unit/respiratoryGear/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/respiratoryMission/respiratoryMission.models.ts b/src/viewmodel/admin/unit/respiratoryMission/respiratoryMission.models.ts new file mode 100644 index 0000000..49c6e7f --- /dev/null +++ b/src/viewmodel/admin/unit/respiratoryMission/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/respiratoryWearer/respiratoryWearer.models.ts b/src/viewmodel/admin/unit/respiratoryWearer/respiratoryWearer.models.ts new file mode 100644 index 0000000..2957408 --- /dev/null +++ b/src/viewmodel/admin/unit/respiratoryWearer/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..0bfccb6 --- /dev/null +++ b/src/viewmodel/admin/unit/vehicle/vehicle.models.ts @@ -0,0 +1,31 @@ +import type { InspectionViewModel } from "../inspection/inspection.models"; +import type { VehicleTypeViewModel } from "../vehicleType/vehicleType.models"; + +export interface VehicleViewModel { + id: string; + code?: string; + name: string; + location: string; + commissioned: Date; + decommissioned?: Date; + vehicleTypeId: string; + vehicleType: VehicleTypeViewModel; + inspections: Array; +} + +export interface CreateVehicleViewModel { + code?: string; + name: string; + location: string; + commissioned: Date; + vehicleTypeId: string; +} + +export interface UpdateVehicleViewModel { + id: string; + code?: string; + name: string; + location: string; + commissioned: Date; + decommissioned?: Date; +} diff --git a/src/viewmodel/admin/unit/vehicleType/vehicleType.models.ts b/src/viewmodel/admin/unit/vehicleType/vehicleType.models.ts new file mode 100644 index 0000000..2de497f --- /dev/null +++ b/src/viewmodel/admin/unit/vehicleType/vehicleType.models.ts @@ -0,0 +1,19 @@ +import type { InspectionPlanViewModel } from "../inspectionPlan/inspectionPlan.models"; + +export interface VehicleTypeViewModel { + id: string; + type: string; + description: string; + inspectionPlans: Array; +} + +export interface CreateVehicleTypeViewModel { + type: string; + description: string; +} + +export interface UpdateVehicleTypeViewModel { + id: string; + type: string; + description: string; +} 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..5e4fccc --- /dev/null +++ b/src/viewmodel/admin/unit/wearable/wearable.models.ts @@ -0,0 +1,34 @@ +import { MemberViewModel } from "../../club/member/member.models"; +import type { WearableTypeViewModel } from "../wearableType/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; +} + +export interface CreateWearableViewModel { + code?: string; + name: string; + wearerId?: string; + location?: string; + commissioned: Date; + wearableTypeId: string; +} + +export interface UpdateWearableViewModel { + id: string; + code?: string; + name: string; + location?: string; + commissioned: Date; + decommissioned?: Date; + wearerId?: string; +} diff --git a/src/viewmodel/admin/unit/wearableType/wearableType.models.ts b/src/viewmodel/admin/unit/wearableType/wearableType.models.ts new file mode 100644 index 0000000..b3f49fc --- /dev/null +++ b/src/viewmodel/admin/unit/wearableType/wearableType.models.ts @@ -0,0 +1,16 @@ +export interface WearableTypeViewModel { + id: string; + type: string; + description: string; +} + +export interface CreateWearableTypeViewModel { + type: string; + description: string; +} + +export interface UpdateWearableTypeViewModel { + id: string; + type: string; + description: string; +} From 95d1113ff93002d51f4f203e3c52e6fe4d086329 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 24 May 2025 11:31:15 +0200 Subject: [PATCH 02/55] base unit data --- src/entity/unit/damageReport.ts | 52 ++++++++++++++++++++++ src/entity/unit/equipment/equipment.ts | 38 ++++++++++++++++ src/entity/unit/equipment/equipmentType.ts | 19 ++++++++ src/entity/unit/vehicle/vehicle.ts | 38 ++++++++++++++++ src/entity/unit/vehicle/vehicleType.ts | 19 ++++++++ src/entity/unit/wearable/wearable.ts | 49 ++++++++++++++++++++ src/entity/unit/wearable/wearableType.ts | 17 +++++++ 7 files changed, 232 insertions(+) create mode 100644 src/entity/unit/damageReport.ts create mode 100644 src/entity/unit/equipment/equipment.ts create mode 100644 src/entity/unit/equipment/equipmentType.ts create mode 100644 src/entity/unit/vehicle/vehicle.ts create mode 100644 src/entity/unit/vehicle/vehicleType.ts create mode 100644 src/entity/unit/wearable/wearable.ts create mode 100644 src/entity/unit/wearable/wearableType.ts diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts new file mode 100644 index 0000000..b8feefe --- /dev/null +++ b/src/entity/unit/damageReport.ts @@ -0,0 +1,52 @@ +import { Check, Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; +import { Equipment } from "./equipment/equipment"; +import { Wearable } from "./wearable/wearable"; +import { Vehicle } from "./vehicle/vehicle"; + +@Entity() +export class DamageReport { + @PrimaryGeneratedColumn("uuid") + id: string; + + @CreateDateColumn() + reported: Date; + + @Column({ type: "varchar", length: 255 }) + status: string; + + @Column({ type: "boolean", default: false }) + done: boolean; + + @Column({ type: "text" }) + description: string; + + @Column({ nullable: true }) + equipmentId: string; + + @Column({ nullable: true }) + vehicleId: string; + + @Column({ nullable: true }) + wearableId: string; + + @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; +} diff --git a/src/entity/unit/equipment/equipment.ts b/src/entity/unit/equipment/equipment.ts new file mode 100644 index 0000000..9748cb1 --- /dev/null +++ b/src/entity/unit/equipment/equipment.ts @@ -0,0 +1,38 @@ +import { Column, ColumnType, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { getTypeByORM } from "../../../migrations/ormHelper"; +import { EquipmentType } from "./equipmentType"; +import { DamageReport } from "../damageReport"; + +@Entity() +export class Equipment { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255, nullable: true, unique: true }) + code?: string; + + @Column({ type: "varchar", length: 255 }) + name: string; + + @Column({ type: "varchar", length: 255 }) + location: string; + + @Column({ type: getTypeByORM("datetime").type as ColumnType }) + commissioned: Date; + + @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true }) + decommissioned?: Date; + + @Column() + equipmentTypeId: string; + + @ManyToOne(() => EquipmentType, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) + equipmentType: EquipmentType; + + @OneToMany(() => DamageReport, (d) => d.equipment, { cascade: ["insert"] }) + reports: DamageReport[]; +} diff --git a/src/entity/unit/equipment/equipmentType.ts b/src/entity/unit/equipment/equipmentType.ts new file mode 100644 index 0000000..1bd25bd --- /dev/null +++ b/src/entity/unit/equipment/equipmentType.ts @@ -0,0 +1,19 @@ +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { Equipment } from "./equipment"; + +@Entity() +export class EquipmentType { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255 }) + type: string; + + @Column({ type: "text", nullable: true }) + description: string; + + @OneToMany(() => Equipment, (e) => e.equipmentType, { cascade: ["insert"] }) + equipment: Equipment[]; + + inspectionPlans: Array; +} diff --git a/src/entity/unit/vehicle/vehicle.ts b/src/entity/unit/vehicle/vehicle.ts new file mode 100644 index 0000000..cff687e --- /dev/null +++ b/src/entity/unit/vehicle/vehicle.ts @@ -0,0 +1,38 @@ +import { Column, ColumnType, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { getTypeByORM } from "../../../migrations/ormHelper"; +import { VehicleType } from "./vehicleType"; +import { DamageReport } from "../damageReport"; + +@Entity() +export class Vehicle { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255, nullable: true, unique: true }) + code?: string; + + @Column({ type: "varchar", length: 255 }) + name: string; + + @Column({ type: "varchar", length: 255 }) + location: string; + + @Column({ type: getTypeByORM("datetime").type as ColumnType }) + commissioned: Date; + + @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true }) + decommissioned?: Date; + + @Column() + vehicleTypeId: string; + + @ManyToOne(() => VehicleType, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) + vehicleType: VehicleType; + + @OneToMany(() => DamageReport, (d) => d.vehicle, { cascade: ["insert"] }) + reports: DamageReport[]; +} diff --git a/src/entity/unit/vehicle/vehicleType.ts b/src/entity/unit/vehicle/vehicleType.ts new file mode 100644 index 0000000..7f43510 --- /dev/null +++ b/src/entity/unit/vehicle/vehicleType.ts @@ -0,0 +1,19 @@ +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { Vehicle } from "./vehicle"; + +@Entity() +export class VehicleType { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255 }) + type: string; + + @Column({ type: "text", nullable: true }) + description: string; + + @OneToMany(() => Vehicle, (e) => e.vehicleType, { cascade: ["insert"] }) + equipment: Vehicle[]; + + inspectionPlans: Array; +} diff --git a/src/entity/unit/wearable/wearable.ts b/src/entity/unit/wearable/wearable.ts new file mode 100644 index 0000000..439025b --- /dev/null +++ b/src/entity/unit/wearable/wearable.ts @@ -0,0 +1,49 @@ +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"; + +@Entity() +export class Wearable { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255, nullable: true, unique: true }) + code?: string; + + @Column({ type: "varchar", length: 255 }) + name: string; + + @Column({ type: "varchar", length: 255 }) + location: string; + + @Column({ type: getTypeByORM("datetime").type as ColumnType }) + commissioned: Date; + + @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true }) + decommissioned?: Date; + + @Column() + equipmentTypeId: string; + + @Column() + wearerId: string; + + @ManyToOne(() => WearableType, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) + wearableType: WearableType; + + @ManyToOne(() => member, { + nullable: false, + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }) + wearer: member; + + @OneToMany(() => DamageReport, (d) => d.wearable, { cascade: ["insert"] }) + reports: DamageReport[]; +} diff --git a/src/entity/unit/wearable/wearableType.ts b/src/entity/unit/wearable/wearableType.ts new file mode 100644 index 0000000..f900b84 --- /dev/null +++ b/src/entity/unit/wearable/wearableType.ts @@ -0,0 +1,17 @@ +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { Wearable } from "./wearable"; + +@Entity() +export class WearableType { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255 }) + type: string; + + @Column({ type: "text", nullable: true }) + description: string; + + @OneToMany(() => Wearable, (e) => e.wearableType, { cascade: ["insert"] }) + equipment: Wearable[]; +} From 3ff44f7370294a43fc6d1e3c6cef8f52552cf9ae Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 24 May 2025 13:51:38 +0200 Subject: [PATCH 03/55] inspection data model --- src/entity/unit/inspection/inspection.ts | 36 ++++++++++++++++ src/entity/unit/inspection/inspectionPlan.ts | 42 +++++++++++++++++++ src/entity/unit/inspection/inspectionPoint.ts | 39 +++++++++++++++++ .../unit/inspection/inspectionPointResult.ts | 19 +++++++++ .../inspection/inspectionVersionedPlan.ts | 35 ++++++++++++++++ src/enums/inspectionEnum.ts | 5 +++ .../unit/damageReport/damageReport.models.ts | 19 +++++++-- .../unit/inspection/inspection.models.ts | 22 +++++----- .../inspectionPlan/inspectionPlan.models.ts | 26 +++++++++--- 9 files changed, 222 insertions(+), 21 deletions(-) create mode 100644 src/entity/unit/inspection/inspection.ts create mode 100644 src/entity/unit/inspection/inspectionPlan.ts create mode 100644 src/entity/unit/inspection/inspectionPoint.ts create mode 100644 src/entity/unit/inspection/inspectionPointResult.ts create mode 100644 src/entity/unit/inspection/inspectionVersionedPlan.ts create mode 100644 src/enums/inspectionEnum.ts diff --git a/src/entity/unit/inspection/inspection.ts b/src/entity/unit/inspection/inspection.ts new file mode 100644 index 0000000..1537492 --- /dev/null +++ b/src/entity/unit/inspection/inspection.ts @@ -0,0 +1,36 @@ +import { Column, ColumnType, CreateDateColumn, Entity, ManyToOne, 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"; + +@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 }) + finished?: Date; + + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true }) + nextInspection?: Date; + + @ManyToOne(() => inspectionPlan) + inspectionPlan: inspectionPlan; + + @ManyToOne(() => inspectionVersionedPlan) + inspectionVersionedPlan: inspectionVersionedPlan; + + @ManyToOne(() => Equipment) + equipment: Equipment; + + @ManyToOne(() => Vehicle) + vehicle: Vehicle; +} diff --git a/src/entity/unit/inspection/inspectionPlan.ts b/src/entity/unit/inspection/inspectionPlan.ts new file mode 100644 index 0000000..491a1a3 --- /dev/null +++ b/src/entity/unit/inspection/inspectionPlan.ts @@ -0,0 +1,42 @@ +import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { Equipment } from "../equipment/equipment"; +import { Vehicle } from "../vehicle/vehicle"; +import { PlanTimeDefinition } from "../../../viewmodel/admin/unit/inspectionPlan/inspectionPlan.models"; +import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; + +@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() + created: Date; + + @Column() + equipmentId: string; + + @Column() + vehicleId: string; + + @ManyToOne(() => Equipment) + equipment: Equipment; + + @ManyToOne(() => Vehicle) + vehicle: Vehicle; + + @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..28be6e0 --- /dev/null +++ b/src/entity/unit/inspection/inspectionPoint.ts @@ -0,0 +1,39 @@ +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } 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", default: 0 }) + min: number; + + @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..2460516 --- /dev/null +++ b/src/entity/unit/inspection/inspectionPointResult.ts @@ -0,0 +1,19 @@ +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; +import { inspection } from "./inspection"; +import { inspectionPoint } from "./inspectionPoint"; +import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; + +@Entity() +export class inspectionPointResult { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "text" }) + value: string; + + @ManyToOne(() => inspection) + inspection: inspection; + + @ManyToOne(() => inspectionPoint) + inspectionPoint: inspectionPoint; +} diff --git a/src/entity/unit/inspection/inspectionVersionedPlan.ts b/src/entity/unit/inspection/inspectionVersionedPlan.ts new file mode 100644 index 0000000..2e8195f --- /dev/null +++ b/src/entity/unit/inspection/inspectionVersionedPlan.ts @@ -0,0 +1,35 @@ +import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, Unique } from "typeorm"; +import { Equipment } from "../equipment/equipment"; +import { Vehicle } from "../vehicle/vehicle"; +import { PlanTimeDefinition } from "../../../viewmodel/admin/unit/inspectionPlan/inspectionPlan.models"; +import { inspectionPlan } from "./inspectionPlan"; +import { getTypeByORM } from "../../../migrations/ormHelper"; +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() + created: 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/enums/inspectionEnum.ts b/src/enums/inspectionEnum.ts new file mode 100644 index 0000000..5c87e55 --- /dev/null +++ b/src/enums/inspectionEnum.ts @@ -0,0 +1,5 @@ +export enum InspectionPointEnum { + oknok = "oknok", + text = "text", + number = "number", +} diff --git a/src/viewmodel/admin/unit/damageReport/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport/damageReport.models.ts index b95ee33..e1d7075 100644 --- a/src/viewmodel/admin/unit/damageReport/damageReport.models.ts +++ b/src/viewmodel/admin/unit/damageReport/damageReport.models.ts @@ -2,7 +2,7 @@ import type { EquipmentViewModel } from "../equipment/equipment.models"; import type { VehicleViewModel } from "../vehicle/vehicle.models"; import type { WearableViewModel } from "../wearable/wearable.models"; -export interface DamageReportViewModel { +export type DamageReportViewModel = { id: string; reported: Date; status: string; @@ -10,9 +10,20 @@ export interface DamageReportViewModel { description: string; providedImage: Array; relatedId: string; - related: EquipmentViewModel | VehicleViewModel | WearableViewModel; - affected: "equipment" | "vehicle" | "wearable"; -} +} & ( + | { + assigned: "equipment"; + related: EquipmentViewModel; + } + | { + assigned: "vehicle"; + related: VehicleViewModel; + } + | { + assigned: "wearable"; + related: WearableViewModel; + } +); export interface CreateDamageReportViewModel { description: string; diff --git a/src/viewmodel/admin/unit/inspection/inspection.models.ts b/src/viewmodel/admin/unit/inspection/inspection.models.ts index a25c279..8cd2e5f 100644 --- a/src/viewmodel/admin/unit/inspection/inspection.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspection.models.ts @@ -5,7 +5,7 @@ import type { } from "../inspectionPlan/inspectionPlan.models"; import type { VehicleViewModel } from "../vehicle/vehicle.models"; -export interface InspectionViewModel { +export type InspectionViewModel = { id: string; inspectionPlanId: string; inspectionPlan: InspectionPlanViewModel; @@ -18,16 +18,16 @@ export interface InspectionViewModel { nextInspection?: Date; checks: Array; relatedId: string; - related: EquipmentViewModel | VehicleViewModel; -} - -export interface InspectionPointViewModel { - id: string; - title: string; - description: string; - type: "iO-niO" | "text" | "number"; - min?: number; -} +} & ( + | { + assigned: "equipment"; + related: EquipmentViewModel; + } + | { + assigned: "vehicle"; + related: VehicleViewModel; + } +); export interface InspectionPointResultViewModel { inspectionId: string; diff --git a/src/viewmodel/admin/unit/inspectionPlan/inspectionPlan.models.ts b/src/viewmodel/admin/unit/inspectionPlan/inspectionPlan.models.ts index cdfb8b2..31384d7 100644 --- a/src/viewmodel/admin/unit/inspectionPlan/inspectionPlan.models.ts +++ b/src/viewmodel/admin/unit/inspectionPlan/inspectionPlan.models.ts @@ -1,12 +1,10 @@ +import { InspectionPointEnum } from "../../../../enums/inspectionEnum"; import type { EquipmentViewModel } from "../equipment/equipment.models"; -import type { EquipmentTypeViewModel } from "../equipmentType/equipmentType.models"; -import type { InspectionPointViewModel } from "../inspection/inspection.models"; import type { VehicleViewModel } from "../vehicle/vehicle.models"; -import type { VehicleTypeViewModel } from "../vehicleType/vehicleType.models"; export type PlanTimeDefinition = `${number}-${"d" | "m" | "y"}` | `${number}/${number | "*"}`; -export interface InspectionPlanViewModel { +export type InspectionPlanViewModel = { id: string; title: string; inspectionInterval: PlanTimeDefinition; @@ -15,8 +13,16 @@ export interface InspectionPlanViewModel { created: Date; inspectionPoints: InspectionPointViewModel[]; relatedId: string; - related: EquipmentTypeViewModel | VehicleTypeViewModel; -} +} & ( + | { + assigned: "equipment"; + related: EquipmentViewModel; + } + | { + assigned: "vehicle"; + related: VehicleViewModel; + } +); export interface InspectionVersionedPlanViewModel { id: string; @@ -25,6 +31,14 @@ export interface InspectionVersionedPlanViewModel { inspectionPoints: InspectionPointViewModel[]; } +export interface InspectionPointViewModel { + id: string; + title: string; + description: string; + type: InspectionPointEnum; + min?: number; +} + export interface CreateInspectionPlanViewModel { title: string; inspectionInterval: PlanTimeDefinition; From 0d8499b8281bded19eb8a5ab0b90e403815d773c Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sun, 25 May 2025 07:01:13 +0200 Subject: [PATCH 04/55] model clean and consistent naming --- src/entity/unit/damageReport.ts | 22 +++++++++---------- src/entity/unit/equipment/equipment.ts | 14 ++++++------ src/entity/unit/equipment/equipmentType.ts | 8 +++---- src/entity/unit/inspection/inspection.ts | 14 ++++++------ src/entity/unit/inspection/inspectionPlan.ts | 12 +++++----- .../unit/inspection/inspectionPointResult.ts | 1 - .../inspection/inspectionVersionedPlan.ts | 6 +---- src/entity/unit/vehicle/vehicle.ts | 14 ++++++------ src/entity/unit/vehicle/vehicleType.ts | 8 +++---- src/entity/unit/wearable/wearable.ts | 16 +++++++------- src/entity/unit/wearable/wearableType.ts | 8 +++---- 11 files changed, 59 insertions(+), 64 deletions(-) diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts index b8feefe..746a25f 100644 --- a/src/entity/unit/damageReport.ts +++ b/src/entity/unit/damageReport.ts @@ -1,15 +1,15 @@ import { Check, Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; -import { Equipment } from "./equipment/equipment"; -import { Wearable } from "./wearable/wearable"; -import { Vehicle } from "./vehicle/vehicle"; +import { equipment } from "./equipment/equipment"; +import { wearable } from "./wearable/wearable"; +import { vehicle } from "./vehicle/vehicle"; @Entity() -export class DamageReport { +export class damageReport { @PrimaryGeneratedColumn("uuid") id: string; @CreateDateColumn() - reported: Date; + reportedAt: Date; @Column({ type: "varchar", length: 255 }) status: string; @@ -29,24 +29,24 @@ export class DamageReport { @Column({ nullable: true }) wearableId: string; - @ManyToOne(() => Equipment, { + @ManyToOne(() => equipment, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) - equipment: Equipment; + equipment: equipment; - @ManyToOne(() => Vehicle, { + @ManyToOne(() => vehicle, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) - vehicle: Vehicle; + vehicle: vehicle; - @ManyToOne(() => Wearable, { + @ManyToOne(() => wearable, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) - wearable: Wearable; + wearable: wearable; } diff --git a/src/entity/unit/equipment/equipment.ts b/src/entity/unit/equipment/equipment.ts index 9748cb1..252c035 100644 --- a/src/entity/unit/equipment/equipment.ts +++ b/src/entity/unit/equipment/equipment.ts @@ -1,10 +1,10 @@ import { Column, ColumnType, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { getTypeByORM } from "../../../migrations/ormHelper"; -import { EquipmentType } from "./equipmentType"; -import { DamageReport } from "../damageReport"; +import { equipmentType } from "./equipmentType"; +import { damageReport } from "../damageReport"; @Entity() -export class Equipment { +export class equipment { @PrimaryGeneratedColumn("uuid") id: string; @@ -26,13 +26,13 @@ export class Equipment { @Column() equipmentTypeId: string; - @ManyToOne(() => EquipmentType, { + @ManyToOne(() => equipmentType, { nullable: false, onDelete: "RESTRICT", onUpdate: "RESTRICT", }) - equipmentType: EquipmentType; + equipmentType: equipmentType; - @OneToMany(() => DamageReport, (d) => d.equipment, { cascade: ["insert"] }) - reports: DamageReport[]; + @OneToMany(() => damageReport, (d) => d.equipment, { cascade: ["insert"] }) + reports: damageReport[]; } diff --git a/src/entity/unit/equipment/equipmentType.ts b/src/entity/unit/equipment/equipmentType.ts index 1bd25bd..9aa6366 100644 --- a/src/entity/unit/equipment/equipmentType.ts +++ b/src/entity/unit/equipment/equipmentType.ts @@ -1,8 +1,8 @@ import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; -import { Equipment } from "./equipment"; +import { equipment } from "./equipment"; @Entity() -export class EquipmentType { +export class equipmentType { @PrimaryGeneratedColumn("uuid") id: string; @@ -12,8 +12,8 @@ export class EquipmentType { @Column({ type: "text", nullable: true }) description: string; - @OneToMany(() => Equipment, (e) => e.equipmentType, { cascade: ["insert"] }) - equipment: Equipment[]; + @OneToMany(() => equipment, (e) => e.equipmentType, { cascade: ["insert"] }) + equipment: equipment[]; inspectionPlans: Array; } diff --git a/src/entity/unit/inspection/inspection.ts b/src/entity/unit/inspection/inspection.ts index 1537492..825caee 100644 --- a/src/entity/unit/inspection/inspection.ts +++ b/src/entity/unit/inspection/inspection.ts @@ -2,8 +2,8 @@ import { Column, ColumnType, CreateDateColumn, Entity, ManyToOne, PrimaryGenerat import { inspectionPlan } from "./inspectionPlan"; import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; import { getTypeByORM } from "../../../migrations/ormHelper"; -import { Vehicle } from "../vehicle/vehicle"; -import { Equipment } from "../equipment/equipment"; +import { vehicle } from "../vehicle/vehicle"; +import { equipment } from "../equipment/equipment"; @Entity() export class inspection { @@ -17,7 +17,7 @@ export class inspection { createdAt: Date; @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true }) - finished?: Date; + finishedAt?: Date; @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true }) nextInspection?: Date; @@ -28,9 +28,9 @@ export class inspection { @ManyToOne(() => inspectionVersionedPlan) inspectionVersionedPlan: inspectionVersionedPlan; - @ManyToOne(() => Equipment) - equipment: Equipment; + @ManyToOne(() => equipment) + equipment: equipment; - @ManyToOne(() => Vehicle) - vehicle: Vehicle; + @ManyToOne(() => vehicle) + vehicle: vehicle; } diff --git a/src/entity/unit/inspection/inspectionPlan.ts b/src/entity/unit/inspection/inspectionPlan.ts index 491a1a3..b2c4358 100644 --- a/src/entity/unit/inspection/inspectionPlan.ts +++ b/src/entity/unit/inspection/inspectionPlan.ts @@ -1,6 +1,6 @@ import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; -import { Equipment } from "../equipment/equipment"; -import { Vehicle } from "../vehicle/vehicle"; +import { equipment } from "../equipment/equipment"; +import { vehicle } from "../vehicle/vehicle"; import { PlanTimeDefinition } from "../../../viewmodel/admin/unit/inspectionPlan/inspectionPlan.models"; import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; @@ -27,11 +27,11 @@ export class inspectionPlan { @Column() vehicleId: string; - @ManyToOne(() => Equipment) - equipment: Equipment; + @ManyToOne(() => equipment) + equipment: equipment; - @ManyToOne(() => Vehicle) - vehicle: Vehicle; + @ManyToOne(() => vehicle) + vehicle: vehicle; @OneToMany(() => inspectionVersionedPlan, (ivp) => ivp.inspectionPlan, { cascade: ["insert"], diff --git a/src/entity/unit/inspection/inspectionPointResult.ts b/src/entity/unit/inspection/inspectionPointResult.ts index 2460516..018649b 100644 --- a/src/entity/unit/inspection/inspectionPointResult.ts +++ b/src/entity/unit/inspection/inspectionPointResult.ts @@ -1,7 +1,6 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; import { inspection } from "./inspection"; import { inspectionPoint } from "./inspectionPoint"; -import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; @Entity() export class inspectionPointResult { diff --git a/src/entity/unit/inspection/inspectionVersionedPlan.ts b/src/entity/unit/inspection/inspectionVersionedPlan.ts index 2e8195f..59d8d4a 100644 --- a/src/entity/unit/inspection/inspectionVersionedPlan.ts +++ b/src/entity/unit/inspection/inspectionVersionedPlan.ts @@ -1,9 +1,5 @@ import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, Unique } from "typeorm"; -import { Equipment } from "../equipment/equipment"; -import { Vehicle } from "../vehicle/vehicle"; -import { PlanTimeDefinition } from "../../../viewmodel/admin/unit/inspectionPlan/inspectionPlan.models"; import { inspectionPlan } from "./inspectionPlan"; -import { getTypeByORM } from "../../../migrations/ormHelper"; import { inspectionPoint } from "./inspectionPoint"; @Entity() @@ -16,7 +12,7 @@ export class inspectionVersionedPlan { version: number; @CreateDateColumn() - created: Date; + createdAt: Date; @Column() inspectionPlanId: string; diff --git a/src/entity/unit/vehicle/vehicle.ts b/src/entity/unit/vehicle/vehicle.ts index cff687e..a2cd129 100644 --- a/src/entity/unit/vehicle/vehicle.ts +++ b/src/entity/unit/vehicle/vehicle.ts @@ -1,10 +1,10 @@ import { Column, ColumnType, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { getTypeByORM } from "../../../migrations/ormHelper"; -import { VehicleType } from "./vehicleType"; -import { DamageReport } from "../damageReport"; +import { vehicleType } from "./vehicleType"; +import { damageReport } from "../damageReport"; @Entity() -export class Vehicle { +export class vehicle { @PrimaryGeneratedColumn("uuid") id: string; @@ -26,13 +26,13 @@ export class Vehicle { @Column() vehicleTypeId: string; - @ManyToOne(() => VehicleType, { + @ManyToOne(() => vehicleType, { nullable: false, onDelete: "RESTRICT", onUpdate: "RESTRICT", }) - vehicleType: VehicleType; + vehicleType: vehicleType; - @OneToMany(() => DamageReport, (d) => d.vehicle, { cascade: ["insert"] }) - reports: DamageReport[]; + @OneToMany(() => damageReport, (d) => d.vehicle, { cascade: ["insert"] }) + reports: damageReport[]; } diff --git a/src/entity/unit/vehicle/vehicleType.ts b/src/entity/unit/vehicle/vehicleType.ts index 7f43510..fba4177 100644 --- a/src/entity/unit/vehicle/vehicleType.ts +++ b/src/entity/unit/vehicle/vehicleType.ts @@ -1,8 +1,8 @@ import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; -import { Vehicle } from "./vehicle"; +import { vehicle } from "./vehicle"; @Entity() -export class VehicleType { +export class vehicleType { @PrimaryGeneratedColumn("uuid") id: string; @@ -12,8 +12,8 @@ export class VehicleType { @Column({ type: "text", nullable: true }) description: string; - @OneToMany(() => Vehicle, (e) => e.vehicleType, { cascade: ["insert"] }) - equipment: Vehicle[]; + @OneToMany(() => vehicle, (e) => e.vehicleType, { cascade: ["insert"] }) + vehicle: vehicle[]; inspectionPlans: Array; } diff --git a/src/entity/unit/wearable/wearable.ts b/src/entity/unit/wearable/wearable.ts index 439025b..509732d 100644 --- a/src/entity/unit/wearable/wearable.ts +++ b/src/entity/unit/wearable/wearable.ts @@ -1,11 +1,11 @@ import { Column, ColumnType, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { getTypeByORM } from "../../../migrations/ormHelper"; -import { WearableType } from "./wearableType"; -import { DamageReport } from "../damageReport"; +import { wearableType } from "./wearableType"; +import { damageReport } from "../damageReport"; import { member } from "../../club/member/member"; @Entity() -export class Wearable { +export class wearable { @PrimaryGeneratedColumn("uuid") id: string; @@ -25,17 +25,17 @@ export class Wearable { decommissioned?: Date; @Column() - equipmentTypeId: string; + wearableTypeId: string; @Column() wearerId: string; - @ManyToOne(() => WearableType, { + @ManyToOne(() => wearableType, { nullable: false, onDelete: "RESTRICT", onUpdate: "RESTRICT", }) - wearableType: WearableType; + wearableType: wearableType; @ManyToOne(() => member, { nullable: false, @@ -44,6 +44,6 @@ export class Wearable { }) wearer: member; - @OneToMany(() => DamageReport, (d) => d.wearable, { cascade: ["insert"] }) - reports: DamageReport[]; + @OneToMany(() => damageReport, (d) => d.wearable, { cascade: ["insert"] }) + reports: damageReport[]; } diff --git a/src/entity/unit/wearable/wearableType.ts b/src/entity/unit/wearable/wearableType.ts index f900b84..71e553e 100644 --- a/src/entity/unit/wearable/wearableType.ts +++ b/src/entity/unit/wearable/wearableType.ts @@ -1,8 +1,8 @@ import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; -import { Wearable } from "./wearable"; +import { wearable as wearable } from "./wearable"; @Entity() -export class WearableType { +export class wearableType { @PrimaryGeneratedColumn("uuid") id: string; @@ -12,6 +12,6 @@ export class WearableType { @Column({ type: "text", nullable: true }) description: string; - @OneToMany(() => Wearable, (e) => e.wearableType, { cascade: ["insert"] }) - equipment: Wearable[]; + @OneToMany(() => wearable, (e) => e.wearableType, { cascade: ["insert"] }) + wearable: wearable[]; } From d156cc6ad6f2aa6992a91be7de907dd644f24fdb Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Mon, 26 May 2025 14:53:25 +0200 Subject: [PATCH 05/55] base migration and schema optimization --- src/data-source.ts | 26 +++ src/entity/unit/damageReport.ts | 6 +- src/entity/unit/equipment/equipment.ts | 4 +- src/entity/unit/equipment/equipmentType.ts | 2 +- src/entity/unit/inspection/inspection.ts | 28 +++- src/entity/unit/inspection/inspectionPlan.ts | 2 +- src/entity/unit/inspection/inspectionPoint.ts | 8 +- .../unit/inspection/inspectionPointResult.ts | 12 +- src/entity/unit/vehicle/vehicle.ts | 4 +- src/entity/unit/vehicle/vehicleType.ts | 2 +- src/entity/unit/wearable/wearable.ts | 4 +- src/entity/unit/wearable/wearableType.ts | 2 +- src/migrations/1748261477410-unitBase.ts | 57 +++++++ src/migrations/baseSchemaTables/inspection.ts | 149 ++++++++++++++++++ src/migrations/baseSchemaTables/unit.ts | 103 ++++++++++++ .../baseSchemaTables/unit_extend.ts | 39 +++++ 16 files changed, 426 insertions(+), 22 deletions(-) create mode 100644 src/migrations/1748261477410-unitBase.ts create mode 100644 src/migrations/baseSchemaTables/inspection.ts create mode 100644 src/migrations/baseSchemaTables/unit.ts create mode 100644 src/migrations/baseSchemaTables/unit_extend.ts diff --git a/src/data-source.ts b/src/data-source.ts index 63de0f8..85fe595 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -56,6 +56,19 @@ import { SettingsFromEnv1745059495807 } from "./migrations/1745059495807-setting import { MemberCreatedAt1746006549262 } from "./migrations/1746006549262-memberCreatedAt"; import { UserLoginRoutine1746252454922 } from "./migrations/1746252454922-UserLoginRoutine"; import { SettingsFromEnv_SET1745059495808 } from "./migrations/1745059495808-settingsFromEnv_set"; +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 { UnitBase1748261477410 } from "./migrations/1748261477410-unitBase"; configCheck(); @@ -111,6 +124,18 @@ const dataSource = new DataSource({ webapi, webapiPermission, setting, + equipmentType, + equipment, + vehicleType, + vehicle, + wearableType, + wearable, + damageReport, + inspectionPlan, + inspectionVersionedPlan, + inspectionPoint, + inspection, + inspectionPointResult, ], migrations: [ BackupAndResetDatabase1738166124200, @@ -123,6 +148,7 @@ const dataSource = new DataSource({ SettingsFromEnv_SET1745059495808, MemberCreatedAt1746006549262, UserLoginRoutine1746252454922, + UnitBase1748261477410, ], migrationsRun: true, migrationsTransactionMode: "each", diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts index 746a25f..54bd219 100644 --- a/src/entity/unit/damageReport.ts +++ b/src/entity/unit/damageReport.ts @@ -20,13 +20,13 @@ export class damageReport { @Column({ type: "text" }) description: string; - @Column({ nullable: true }) + @Column({ nullable: true, default: null }) equipmentId: string; - @Column({ nullable: true }) + @Column({ nullable: true, default: null }) vehicleId: string; - @Column({ nullable: true }) + @Column({ nullable: true, default: null }) wearableId: string; @ManyToOne(() => equipment, { diff --git a/src/entity/unit/equipment/equipment.ts b/src/entity/unit/equipment/equipment.ts index 252c035..22df384 100644 --- a/src/entity/unit/equipment/equipment.ts +++ b/src/entity/unit/equipment/equipment.ts @@ -8,7 +8,7 @@ export class equipment { @PrimaryGeneratedColumn("uuid") id: string; - @Column({ type: "varchar", length: 255, nullable: true, unique: true }) + @Column({ type: "varchar", length: 255, nullable: true, default: null, unique: true }) code?: string; @Column({ type: "varchar", length: 255 }) @@ -20,7 +20,7 @@ export class equipment { @Column({ type: getTypeByORM("datetime").type as ColumnType }) commissioned: Date; - @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true }) + @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true, default: null }) decommissioned?: Date; @Column() diff --git a/src/entity/unit/equipment/equipmentType.ts b/src/entity/unit/equipment/equipmentType.ts index 9aa6366..d28fb72 100644 --- a/src/entity/unit/equipment/equipmentType.ts +++ b/src/entity/unit/equipment/equipmentType.ts @@ -6,7 +6,7 @@ export class equipmentType { @PrimaryGeneratedColumn("uuid") id: string; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) type: string; @Column({ type: "text", nullable: true }) diff --git a/src/entity/unit/inspection/inspection.ts b/src/entity/unit/inspection/inspection.ts index 825caee..23460f6 100644 --- a/src/entity/unit/inspection/inspection.ts +++ b/src/entity/unit/inspection/inspection.ts @@ -16,21 +16,37 @@ export class inspection { @CreateDateColumn() createdAt: Date; - @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true }) + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) finishedAt?: Date; - @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true }) + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) nextInspection?: Date; - @ManyToOne(() => inspectionPlan) + @ManyToOne(() => inspectionPlan, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) inspectionPlan: inspectionPlan; - @ManyToOne(() => inspectionVersionedPlan) + @ManyToOne(() => inspectionVersionedPlan, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) inspectionVersionedPlan: inspectionVersionedPlan; - @ManyToOne(() => equipment) + @ManyToOne(() => equipment, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) equipment: equipment; - @ManyToOne(() => vehicle) + @ManyToOne(() => vehicle, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) vehicle: vehicle; } diff --git a/src/entity/unit/inspection/inspectionPlan.ts b/src/entity/unit/inspection/inspectionPlan.ts index b2c4358..9be6406 100644 --- a/src/entity/unit/inspection/inspectionPlan.ts +++ b/src/entity/unit/inspection/inspectionPlan.ts @@ -19,7 +19,7 @@ export class inspectionPlan { remindTime: PlanTimeDefinition; @CreateDateColumn() - created: Date; + createdAt: Date; @Column() equipmentId: string; diff --git a/src/entity/unit/inspection/inspectionPoint.ts b/src/entity/unit/inspection/inspectionPoint.ts index 28be6e0..5185355 100644 --- a/src/entity/unit/inspection/inspectionPoint.ts +++ b/src/entity/unit/inspection/inspectionPoint.ts @@ -27,9 +27,15 @@ export class inspectionPoint { }) type: InspectionPointEnum; - @Column({ type: "int", default: 0 }) + @Column({ type: "int", nullable: true, default: null }) min: number; + @Column({ type: "int", nullable: true, default: null }) + max: number; + + @Column() + versionedPlanId: string; + @ManyToOne(() => inspectionVersionedPlan, { nullable: false, onDelete: "CASCADE", diff --git a/src/entity/unit/inspection/inspectionPointResult.ts b/src/entity/unit/inspection/inspectionPointResult.ts index 018649b..4b44146 100644 --- a/src/entity/unit/inspection/inspectionPointResult.ts +++ b/src/entity/unit/inspection/inspectionPointResult.ts @@ -10,9 +10,17 @@ export class inspectionPointResult { @Column({ type: "text" }) value: string; - @ManyToOne(() => inspection) + @ManyToOne(() => inspection, { + nullable: false, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) inspection: inspection; - @ManyToOne(() => inspectionPoint) + @ManyToOne(() => inspectionPoint, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) inspectionPoint: inspectionPoint; } diff --git a/src/entity/unit/vehicle/vehicle.ts b/src/entity/unit/vehicle/vehicle.ts index a2cd129..0c30847 100644 --- a/src/entity/unit/vehicle/vehicle.ts +++ b/src/entity/unit/vehicle/vehicle.ts @@ -8,7 +8,7 @@ export class vehicle { @PrimaryGeneratedColumn("uuid") id: string; - @Column({ type: "varchar", length: 255, nullable: true, unique: true }) + @Column({ type: "varchar", length: 255, nullable: true, default: null, unique: true }) code?: string; @Column({ type: "varchar", length: 255 }) @@ -20,7 +20,7 @@ export class vehicle { @Column({ type: getTypeByORM("datetime").type as ColumnType }) commissioned: Date; - @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true }) + @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true, default: null }) decommissioned?: Date; @Column() diff --git a/src/entity/unit/vehicle/vehicleType.ts b/src/entity/unit/vehicle/vehicleType.ts index fba4177..5773709 100644 --- a/src/entity/unit/vehicle/vehicleType.ts +++ b/src/entity/unit/vehicle/vehicleType.ts @@ -6,7 +6,7 @@ export class vehicleType { @PrimaryGeneratedColumn("uuid") id: string; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) type: string; @Column({ type: "text", nullable: true }) diff --git a/src/entity/unit/wearable/wearable.ts b/src/entity/unit/wearable/wearable.ts index 509732d..75a64b7 100644 --- a/src/entity/unit/wearable/wearable.ts +++ b/src/entity/unit/wearable/wearable.ts @@ -9,7 +9,7 @@ export class wearable { @PrimaryGeneratedColumn("uuid") id: string; - @Column({ type: "varchar", length: 255, nullable: true, unique: true }) + @Column({ type: "varchar", length: 255, nullable: true, default: null, unique: true }) code?: string; @Column({ type: "varchar", length: 255 }) @@ -21,7 +21,7 @@ export class wearable { @Column({ type: getTypeByORM("datetime").type as ColumnType }) commissioned: Date; - @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true }) + @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true, default: null }) decommissioned?: Date; @Column() diff --git a/src/entity/unit/wearable/wearableType.ts b/src/entity/unit/wearable/wearableType.ts index 71e553e..0905a33 100644 --- a/src/entity/unit/wearable/wearableType.ts +++ b/src/entity/unit/wearable/wearableType.ts @@ -6,7 +6,7 @@ export class wearableType { @PrimaryGeneratedColumn("uuid") id: string; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) type: string; @Column({ type: "text", nullable: true }) diff --git a/src/migrations/1748261477410-unitBase.ts b/src/migrations/1748261477410-unitBase.ts new file mode 100644 index 0000000..d4951c6 --- /dev/null +++ b/src/migrations/1748261477410-unitBase.ts @@ -0,0 +1,57 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +import { + equipment_table, + equipment_type_table, + vehicle_table, + vehicle_type_table, + wearable_table, + wearable_type_table, +} from "./baseSchemaTables/unit"; +import { damage_report_table } from "./baseSchemaTables/unit_extend"; +import { + inspection_plan_table, + inspection_point_result_table, + inspection_point_table, + inspection_table, + inspection_versioned_plan_table, +} from "./baseSchemaTables/inspection"; + +export class UnitBase1748261477410 implements MigrationInterface { + name = "UnitBase1748261477410"; + + 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(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); + } + + 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(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); + } +} diff --git a/src/migrations/baseSchemaTables/inspection.ts b/src/migrations/baseSchemaTables/inspection.ts new file mode 100644 index 0000000..9048e95 --- /dev/null +++ b/src/migrations/baseSchemaTables/inspection.ts @@ -0,0 +1,149 @@ +import { Table, TableForeignKey, TableUnique, Unique } 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("date"), default: getDefaultByORM("currentTimestamp") }, + { name: "equipmentId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "vehicleId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + ], + 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", + }), + ], +}); + +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("date"), 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), default: getDefaultByORM("null") }, + { name: "max", ...getTypeByORM("int", true), default: getDefaultByORM("null") }, + { 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("date"), default: getDefaultByORM("currentTimestamp") }, + { name: "finishedAt", ...getTypeByORM("date", true) }, + { name: "nextInspection", ...getTypeByORM("date", true) }, + { name: "inspectionPlanId", ...getTypeByORM("uuid") }, + { name: "inspectionVersionedPlanId", ...getTypeByORM("uuid") }, + { name: "equipmentId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "vehicleId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + ], + 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", + }), + ], +}); + +export const inspection_point_result_table = new Table({ + name: "inspection_point_result", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "value", ...getTypeByORM("text") }, + { name: "inspectionId", ...getTypeByORM("uuid") }, + { name: "inspectionPointId", ...getTypeByORM("uuid") }, + ], + 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..aec48b3 --- /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), default: getDefaultByORM("null"), isUnique: true }, + { name: "name", ...getTypeByORM("varchar") }, + { name: "location", ...getTypeByORM("varchar") }, + { name: "commissioned", ...getTypeByORM("date") }, + { name: "decommissioned", ...getTypeByORM("date", true), default: getDefaultByORM("null") }, + { 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") }, + ], +}); + +export const vehicle_table = new Table({ + name: "vehicle", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "code", ...getTypeByORM("varchar", true), default: getDefaultByORM("null"), isUnique: true }, + { name: "name", ...getTypeByORM("varchar") }, + { name: "location", ...getTypeByORM("varchar") }, + { name: "commissioned", ...getTypeByORM("date") }, + { name: "decommissioned", ...getTypeByORM("date", true), default: getDefaultByORM("null") }, + { 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") }, + ], +}); + +export const wearable_table = new Table({ + name: "wearable", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "code", ...getTypeByORM("varchar", true), default: getDefaultByORM("null"), isUnique: true }, + { name: "name", ...getTypeByORM("varchar") }, + { name: "location", ...getTypeByORM("varchar") }, + { name: "commissioned", ...getTypeByORM("date") }, + { name: "decommissioned", ...getTypeByORM("date", true), default: getDefaultByORM("null") }, + { name: "wearableTypeId", ...getTypeByORM("uuid") }, + { name: "wearerId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + ], + 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") }, + ], +}); diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts new file mode 100644 index 0000000..49ce9e9 --- /dev/null +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -0,0 +1,39 @@ +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("date"), default: getDefaultByORM("currentTimestamp") }, + { name: "status", ...getTypeByORM("varchar") }, + { name: "done", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "description", ...getTypeByORM("text") }, + { name: "equipmentId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "vehicleId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "wearableId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + ], + 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", + }), + ], +}); From baa3b2cc8cf20605a3c715d732ba9ff35d6acc8c Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 27 May 2025 16:01:00 +0200 Subject: [PATCH 06/55] add reporter to damage report --- src/entity/unit/damageReport.ts | 3 +++ src/migrations/baseSchemaTables/unit_extend.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts index 54bd219..a59c9e2 100644 --- a/src/entity/unit/damageReport.ts +++ b/src/entity/unit/damageReport.ts @@ -20,6 +20,9 @@ export class damageReport { @Column({ type: "text" }) description: string; + @Column({ type: "varchar", length: 255 }) + reportedBy: string; + @Column({ nullable: true, default: null }) equipmentId: string; diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index 49ce9e9..a81f8ac 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -9,6 +9,7 @@ export const damage_report_table = new Table({ { name: "status", ...getTypeByORM("varchar") }, { name: "done", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, { name: "description", ...getTypeByORM("text") }, + { name: "reportedBy", ...getTypeByORM("varchar") }, { name: "equipmentId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, { name: "vehicleId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, { name: "wearableId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, From 8c81c8f336f22e7f97f9624fab14c38ba437230e Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 28 May 2025 17:06:45 +0200 Subject: [PATCH 07/55] db schema connects --- src/entity/unit/equipment/equipment.ts | 4 ++++ src/entity/unit/equipment/equipmentType.ts | 4 +++- src/entity/unit/inspection/inspection.ts | 12 ++++++++++++ src/entity/unit/inspection/inspectionPointResult.ts | 6 ++++++ src/entity/unit/vehicle/vehicle.ts | 4 ++++ src/entity/unit/vehicle/vehicleType.ts | 4 +++- src/entity/unit/wearable/wearable.ts | 1 + 7 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/entity/unit/equipment/equipment.ts b/src/entity/unit/equipment/equipment.ts index 22df384..d55ea0d 100644 --- a/src/entity/unit/equipment/equipment.ts +++ b/src/entity/unit/equipment/equipment.ts @@ -2,6 +2,7 @@ import { Column, ColumnType, Entity, ManyToOne, OneToMany, PrimaryGeneratedColum import { getTypeByORM } from "../../../migrations/ormHelper"; import { equipmentType } from "./equipmentType"; import { damageReport } from "../damageReport"; +import { inspection } from "../inspection/inspection"; @Entity() export class equipment { @@ -35,4 +36,7 @@ export class equipment { @OneToMany(() => damageReport, (d) => d.equipment, { cascade: ["insert"] }) reports: damageReport[]; + + @OneToMany(() => inspection, (i) => i.equipment) + inspections: inspection[]; } diff --git a/src/entity/unit/equipment/equipmentType.ts b/src/entity/unit/equipment/equipmentType.ts index d28fb72..3888ebe 100644 --- a/src/entity/unit/equipment/equipmentType.ts +++ b/src/entity/unit/equipment/equipmentType.ts @@ -1,5 +1,6 @@ import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { equipment } from "./equipment"; +import { inspectionPlan } from "../inspection/inspectionPlan"; @Entity() export class equipmentType { @@ -15,5 +16,6 @@ export class equipmentType { @OneToMany(() => equipment, (e) => e.equipmentType, { cascade: ["insert"] }) equipment: equipment[]; - inspectionPlans: Array; + @OneToMany(() => inspectionPlan, (ip) => ip.equipment) + inspectionPlans: inspectionPlan[]; } diff --git a/src/entity/unit/inspection/inspection.ts b/src/entity/unit/inspection/inspection.ts index 23460f6..27b85c1 100644 --- a/src/entity/unit/inspection/inspection.ts +++ b/src/entity/unit/inspection/inspection.ts @@ -22,6 +22,18 @@ export class inspection { @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) nextInspection?: Date; + @Column() + inspectionPlanId: string; + + @Column() + inspectionVersionedPlanId: string; + + @Column() + equipmentId: string; + + @Column() + vehicleId: string; + @ManyToOne(() => inspectionPlan, { nullable: false, onDelete: "RESTRICT", diff --git a/src/entity/unit/inspection/inspectionPointResult.ts b/src/entity/unit/inspection/inspectionPointResult.ts index 4b44146..5b3501d 100644 --- a/src/entity/unit/inspection/inspectionPointResult.ts +++ b/src/entity/unit/inspection/inspectionPointResult.ts @@ -10,6 +10,12 @@ export class inspectionPointResult { @Column({ type: "text" }) value: string; + @Column() + inspectionId: string; + + @Column() + inspectionPointId: string; + @ManyToOne(() => inspection, { nullable: false, onDelete: "CASCADE", diff --git a/src/entity/unit/vehicle/vehicle.ts b/src/entity/unit/vehicle/vehicle.ts index 0c30847..dc4a8d6 100644 --- a/src/entity/unit/vehicle/vehicle.ts +++ b/src/entity/unit/vehicle/vehicle.ts @@ -2,6 +2,7 @@ import { Column, ColumnType, Entity, ManyToOne, OneToMany, PrimaryGeneratedColum import { getTypeByORM } from "../../../migrations/ormHelper"; import { vehicleType } from "./vehicleType"; import { damageReport } from "../damageReport"; +import { inspection } from "../inspection/inspection"; @Entity() export class vehicle { @@ -35,4 +36,7 @@ export class vehicle { @OneToMany(() => damageReport, (d) => d.vehicle, { cascade: ["insert"] }) reports: damageReport[]; + + @OneToMany(() => inspection, (i) => i.vehicle) + inspections: inspection[]; } diff --git a/src/entity/unit/vehicle/vehicleType.ts b/src/entity/unit/vehicle/vehicleType.ts index 5773709..d58b478 100644 --- a/src/entity/unit/vehicle/vehicleType.ts +++ b/src/entity/unit/vehicle/vehicleType.ts @@ -1,5 +1,6 @@ import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { vehicle } from "./vehicle"; +import { inspectionPlan } from "../inspection/inspectionPlan"; @Entity() export class vehicleType { @@ -15,5 +16,6 @@ export class vehicleType { @OneToMany(() => vehicle, (e) => e.vehicleType, { cascade: ["insert"] }) vehicle: vehicle[]; - inspectionPlans: Array; + @OneToMany(() => inspectionPlan, (ip) => ip.vehicle) + inspectionPlans: inspectionPlan[]; } diff --git a/src/entity/unit/wearable/wearable.ts b/src/entity/unit/wearable/wearable.ts index 75a64b7..f532cb0 100644 --- a/src/entity/unit/wearable/wearable.ts +++ b/src/entity/unit/wearable/wearable.ts @@ -3,6 +3,7 @@ import { getTypeByORM } from "../../../migrations/ormHelper"; import { wearableType } from "./wearableType"; import { damageReport } from "../damageReport"; import { member } from "../../club/member/member"; +import { inspection } from "../inspection/inspection"; @Entity() export class wearable { From 2433120e26704f98e17a3deb6f7e9a572eed1a29 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 28 May 2025 17:06:56 +0200 Subject: [PATCH 08/55] service base --- src/service/unit/damageReport.ts | 41 +++++++++++++++++++ .../unit/equipment/equipmentService.ts | 41 +++++++++++++++++++ .../unit/equipment/equipmentTypeService.ts | 41 +++++++++++++++++++ .../unit/inspection/inspectionPlanService.ts | 41 +++++++++++++++++++ .../inspectionPointResultService.ts | 41 +++++++++++++++++++ .../unit/inspection/inspectionPointService.ts | 41 +++++++++++++++++++ .../unit/inspection/inspectionService.ts | 41 +++++++++++++++++++ .../inspectionVersionedPlanService.ts | 41 +++++++++++++++++++ src/service/unit/vehicle/vehicleService.ts | 41 +++++++++++++++++++ .../unit/vehicle/vehicleTypeService.ts | 41 +++++++++++++++++++ src/service/unit/wearable/wearableService.ts | 41 +++++++++++++++++++ .../unit/wearable/wearableTypeService.ts | 41 +++++++++++++++++++ .../unit/damageReport/damageReport.models.ts | 2 + 13 files changed, 494 insertions(+) create mode 100644 src/service/unit/damageReport.ts create mode 100644 src/service/unit/equipment/equipmentService.ts create mode 100644 src/service/unit/equipment/equipmentTypeService.ts create mode 100644 src/service/unit/inspection/inspectionPlanService.ts create mode 100644 src/service/unit/inspection/inspectionPointResultService.ts create mode 100644 src/service/unit/inspection/inspectionPointService.ts create mode 100644 src/service/unit/inspection/inspectionService.ts create mode 100644 src/service/unit/inspection/inspectionVersionedPlanService.ts create mode 100644 src/service/unit/vehicle/vehicleService.ts create mode 100644 src/service/unit/vehicle/vehicleTypeService.ts create mode 100644 src/service/unit/wearable/wearableService.ts create mode 100644 src/service/unit/wearable/wearableTypeService.ts diff --git a/src/service/unit/damageReport.ts b/src/service/unit/damageReport.ts new file mode 100644 index 0000000..0832766 --- /dev/null +++ b/src/service/unit/damageReport.ts @@ -0,0 +1,41 @@ +import { dataSource } from "../../data-source"; +import { damageReport } from "../../entity/unit/damageReport"; +import DatabaseActionException from "../../exceptions/databaseActionException"; + +export default abstract class DamageReportService { + /** + * @description get all damageReport types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(damageReport) + .createQueryBuilder("damageReport") + .orderBy("type", "ASC") + .getMany() + .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 dataSource + .getRepository(damageReport) + .createQueryBuilder("damageReport") + .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..8c28427 --- /dev/null +++ b/src/service/unit/equipment/equipmentService.ts @@ -0,0 +1,41 @@ +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 types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(equipment) + .createQueryBuilder("equipment") + .orderBy("type", "ASC") + .getMany() + .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") + .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..4617544 --- /dev/null +++ b/src/service/unit/equipment/equipmentTypeService.ts @@ -0,0 +1,41 @@ +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 equipment types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(equipmentType) + .createQueryBuilder("equipmentType") + .orderBy("type", "ASC") + .getMany() + .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..96fbab9 --- /dev/null +++ b/src/service/unit/inspection/inspectionPlanService.ts @@ -0,0 +1,41 @@ +import { dataSource } from "../../../data-source"; +import { inspectionPlan } from "../../../entity/unit/inspection/inspectionPlan"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class InspectionPlanService { + /** + * @description get all inspectionPlan types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(inspectionPlan) + .createQueryBuilder("inspectionPlan") + .orderBy("type", "ASC") + .getMany() + .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 dataSource + .getRepository(inspectionPlan) + .createQueryBuilder("inspectionPlan") + .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..4ac3450 --- /dev/null +++ b/src/service/unit/inspection/inspectionPointResultService.ts @@ -0,0 +1,41 @@ +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 inspectionPointResult types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(inspectionPointResult) + .createQueryBuilder("inspectionPointResult") + .orderBy("type", "ASC") + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionPointResult", err); + }); + } + + /** + * @description get inspectionPointResult by id + * @returns {Promise} + */ + static async getById(id: string): Promise { + return await dataSource + .getRepository(inspectionPointResult) + .createQueryBuilder("inspectionPointResult") + .where({ id }) + .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..4203550 --- /dev/null +++ b/src/service/unit/inspection/inspectionPointService.ts @@ -0,0 +1,41 @@ +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 inspectionPoint types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(inspectionPoint) + .createQueryBuilder("inspectionPoint") + .orderBy("type", "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..cd8ba7d --- /dev/null +++ b/src/service/unit/inspection/inspectionService.ts @@ -0,0 +1,41 @@ +import { dataSource } from "../../../data-source"; +import { inspection } from "../../../entity/unit/inspection/inspection"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; + +export default abstract class InspectionService { + /** + * @description get all inspection types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(inspection) + .createQueryBuilder("inspection") + .orderBy("type", "ASC") + .getMany() + .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 dataSource + .getRepository(inspection) + .createQueryBuilder("inspection") + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspection", err); + }); + } +} diff --git a/src/service/unit/inspection/inspectionVersionedPlanService.ts b/src/service/unit/inspection/inspectionVersionedPlanService.ts new file mode 100644 index 0000000..ebaa296 --- /dev/null +++ b/src/service/unit/inspection/inspectionVersionedPlanService.ts @@ -0,0 +1,41 @@ +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 inspectionVersionedPlan types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(inspectionVersionedPlan) + .createQueryBuilder("inspectionVersionedPlan") + .orderBy("type", "ASC") + .getMany() + .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") + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "inspectionVersionedPlan", err); + }); + } +} diff --git a/src/service/unit/vehicle/vehicleService.ts b/src/service/unit/vehicle/vehicleService.ts new file mode 100644 index 0000000..898911f --- /dev/null +++ b/src/service/unit/vehicle/vehicleService.ts @@ -0,0 +1,41 @@ +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 vehicle types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(vehicle) + .createQueryBuilder("vehicle") + .orderBy("type", "ASC") + .getMany() + .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") + .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..2af3acc --- /dev/null +++ b/src/service/unit/vehicle/vehicleTypeService.ts @@ -0,0 +1,41 @@ +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 vehicleType types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(vehicleType) + .createQueryBuilder("vehicleType") + .orderBy("type", "ASC") + .getMany() + .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..77c120a --- /dev/null +++ b/src/service/unit/wearable/wearableService.ts @@ -0,0 +1,41 @@ +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 wearable types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(wearable) + .createQueryBuilder("wearable") + .orderBy("type", "ASC") + .getMany() + .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") + .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..77f7a90 --- /dev/null +++ b/src/service/unit/wearable/wearableTypeService.ts @@ -0,0 +1,41 @@ +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 wearableType types + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(wearableType) + .createQueryBuilder("wearableType") + .orderBy("type", "ASC") + .getMany() + .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/viewmodel/admin/unit/damageReport/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport/damageReport.models.ts index e1d7075..a144114 100644 --- a/src/viewmodel/admin/unit/damageReport/damageReport.models.ts +++ b/src/viewmodel/admin/unit/damageReport/damageReport.models.ts @@ -9,6 +9,7 @@ export type DamageReportViewModel = { done: boolean; description: string; providedImage: Array; + reportedBy: string; relatedId: string; } & ( | { @@ -27,6 +28,7 @@ export type DamageReportViewModel = { export interface CreateDamageReportViewModel { description: string; + reportedBy: string; affectedId: string; affected: "equipment" | "vehicle" | "wearable"; } From e404989a2858866a8dad97c5bd3c5e17a525aedc Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 28 May 2025 17:13:39 +0200 Subject: [PATCH 09/55] add sort to inspection point --- src/entity/unit/inspection/inspectionPoint.ts | 5 ++++- src/migrations/baseSchemaTables/inspection.ts | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/entity/unit/inspection/inspectionPoint.ts b/src/entity/unit/inspection/inspectionPoint.ts index 5185355..2ff25b1 100644 --- a/src/entity/unit/inspection/inspectionPoint.ts +++ b/src/entity/unit/inspection/inspectionPoint.ts @@ -1,4 +1,4 @@ -import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, Unique } from "typeorm"; import { InspectionPointEnum } from "../../../enums/inspectionEnum"; import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; @@ -33,6 +33,9 @@ export class inspectionPoint { @Column({ type: "int", nullable: true, default: null }) max: number; + @Column({ type: "int", default: 0 }) + sort: number; + @Column() versionedPlanId: string; diff --git a/src/migrations/baseSchemaTables/inspection.ts b/src/migrations/baseSchemaTables/inspection.ts index 9048e95..83e856f 100644 --- a/src/migrations/baseSchemaTables/inspection.ts +++ b/src/migrations/baseSchemaTables/inspection.ts @@ -64,6 +64,7 @@ export const inspection_point_table = new Table({ { name: "type", ...getTypeByORM("varchar") }, { name: "min", ...getTypeByORM("int", true), default: getDefaultByORM("null") }, { name: "max", ...getTypeByORM("int", true), default: getDefaultByORM("null") }, + { name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, { name: "versionedPlanId", ...getTypeByORM("uuid") }, ], foreignKeys: [ From fcbfe560c3a347fd978f731be2414e01f9d2dcf1 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 28 May 2025 17:32:07 +0200 Subject: [PATCH 10/55] basic services joins --- src/entity/unit/inspection/inspection.ts | 6 +++- src/service/unit/damageReport.ts | 2 +- .../unit/equipment/equipmentService.ts | 6 ++-- .../unit/equipment/equipmentTypeService.ts | 2 +- .../unit/inspection/inspectionPlanService.ts | 28 +++++++++++++++++-- .../inspectionPointResultService.ts | 8 ++++-- .../unit/inspection/inspectionPointService.ts | 7 +++-- .../unit/inspection/inspectionService.ts | 17 +++++++++-- .../inspectionVersionedPlanService.ts | 9 ++++-- src/service/unit/vehicle/vehicleService.ts | 6 ++-- .../unit/vehicle/vehicleTypeService.ts | 2 +- src/service/unit/wearable/wearableService.ts | 6 ++-- .../unit/wearable/wearableTypeService.ts | 2 +- 13 files changed, 75 insertions(+), 26 deletions(-) diff --git a/src/entity/unit/inspection/inspection.ts b/src/entity/unit/inspection/inspection.ts index 27b85c1..77e32d0 100644 --- a/src/entity/unit/inspection/inspection.ts +++ b/src/entity/unit/inspection/inspection.ts @@ -1,9 +1,10 @@ -import { Column, ColumnType, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; +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"; @Entity() export class inspection { @@ -61,4 +62,7 @@ export class inspection { onUpdate: "RESTRICT", }) vehicle: vehicle; + + @OneToMany(() => inspectionPointResult, (ipr) => ipr.inspection) + pointResults: inspectionPointResult[]; } diff --git a/src/service/unit/damageReport.ts b/src/service/unit/damageReport.ts index 0832766..b6134d6 100644 --- a/src/service/unit/damageReport.ts +++ b/src/service/unit/damageReport.ts @@ -4,7 +4,7 @@ import DatabaseActionException from "../../exceptions/databaseActionException"; export default abstract class DamageReportService { /** - * @description get all damageReport types + * @description get all damageReports * @returns {Promise>} */ static async getAll(): Promise> { diff --git a/src/service/unit/equipment/equipmentService.ts b/src/service/unit/equipment/equipmentService.ts index 8c28427..a2e1823 100644 --- a/src/service/unit/equipment/equipmentService.ts +++ b/src/service/unit/equipment/equipmentService.ts @@ -4,14 +4,15 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class EquipmentService { /** - * @description get all equipment types + * @description get all equipment * @returns {Promise>} */ static async getAll(): Promise> { return await dataSource .getRepository(equipment) .createQueryBuilder("equipment") - .orderBy("type", "ASC") + .leftJoinAndSelect("equipment.equipmentType", "equipmenttype") + .orderBy("name", "ASC") .getMany() .then((res) => { return res; @@ -29,6 +30,7 @@ export default abstract class EquipmentService { return await dataSource .getRepository(equipment) .createQueryBuilder("equipment") + .leftJoinAndSelect("equipment.equipmentType", "equipmenttype") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/equipment/equipmentTypeService.ts b/src/service/unit/equipment/equipmentTypeService.ts index 4617544..237b0cd 100644 --- a/src/service/unit/equipment/equipmentTypeService.ts +++ b/src/service/unit/equipment/equipmentTypeService.ts @@ -4,7 +4,7 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class EquipmentTypeService { /** - * @description get all equipment types + * @description get all equipmentTypes * @returns {Promise>} */ static async getAll(): Promise> { diff --git a/src/service/unit/inspection/inspectionPlanService.ts b/src/service/unit/inspection/inspectionPlanService.ts index 96fbab9..0e2cc47 100644 --- a/src/service/unit/inspection/inspectionPlanService.ts +++ b/src/service/unit/inspection/inspectionPlanService.ts @@ -1,17 +1,30 @@ import { dataSource } from "../../../data-source"; import { inspectionPlan } from "../../../entity/unit/inspection/inspectionPlan"; +import { DB_TYPE } from "../../../env.defaults"; import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class InspectionPlanService { /** - * @description get all inspectionPlan types + * @description get all inspectionPlans for related * @returns {Promise>} */ - static async getAll(): Promise> { + static async getAllForRelated( + where: { equipmentId: string } | { vehicleId: string } + ): Promise> { return await dataSource .getRepository(inspectionPlan) .createQueryBuilder("inspectionPlan") - .orderBy("type", "ASC") + .leftJoinAndMapOne( + "inspectionPlan.latestVersionedPlan", + "inspectionPlan.versionedPlans", + "latestVersionedPlan", + DB_TYPE == "postgres" + ? 'latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX("ivp"."start") FROM "inspection_versioned_plan" "ivp" WHERE "ivp"."inspectionPlanId" = "inspectionPlan"."id")' + : "latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX(ivp.start) FROM inspection_versioned_plan ivp WHERE ivp.inspectionPlanId = inspectionPlan.id)" + ) + .leftJoinAndSelect("latestVersionedPlan.inspectionPoints", "inspectionPoints") + .where(where) + .orderBy("title", "ASC") .getMany() .then((res) => { return res; @@ -29,6 +42,15 @@ export default abstract class InspectionPlanService { return await dataSource .getRepository(inspectionPlan) .createQueryBuilder("inspectionPlan") + .leftJoinAndMapOne( + "inspectionPlan.latestVersionedPlan", + "inspectionPlan.versionedPlans", + "latestVersionedPlan", + DB_TYPE == "postgres" + ? 'latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX("ivp"."start") FROM "inspection_versioned_plan" "ivp" WHERE "ivp"."inspectionPlanId" = "inspectionPlan"."id")' + : "latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX(ivp.start) FROM inspection_versioned_plan ivp WHERE ivp.inspectionPlanId = inspectionPlan.id)" + ) + .leftJoinAndSelect("latestVersionedPlan.inspectionPoints", "inspectionPoints") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/inspection/inspectionPointResultService.ts b/src/service/unit/inspection/inspectionPointResultService.ts index 4ac3450..d538a63 100644 --- a/src/service/unit/inspection/inspectionPointResultService.ts +++ b/src/service/unit/inspection/inspectionPointResultService.ts @@ -4,14 +4,15 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class InspectionPointResultService { /** - * @description get all inspectionPointResult types + * @description get all inspectionPointResults * @returns {Promise>} */ - static async getAll(): Promise> { + static async getAllForInspection(inspectionId: string): Promise> { return await dataSource .getRepository(inspectionPointResult) .createQueryBuilder("inspectionPointResult") - .orderBy("type", "ASC") + .leftJoinAndSelect("inspectionPointResult.inspectionPoint", "inspectionPoint") + .where({ inspectionId }) .getMany() .then((res) => { return res; @@ -29,6 +30,7 @@ export default abstract class InspectionPointResultService { return await dataSource .getRepository(inspectionPointResult) .createQueryBuilder("inspectionPointResult") + .leftJoinAndSelect("inspectionPointResult.inspectionPoint", "inspectionPoint") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/inspection/inspectionPointService.ts b/src/service/unit/inspection/inspectionPointService.ts index 4203550..0285c9f 100644 --- a/src/service/unit/inspection/inspectionPointService.ts +++ b/src/service/unit/inspection/inspectionPointService.ts @@ -4,14 +4,15 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class InspectionPointService { /** - * @description get all inspectionPoint types + * @description get all inspectionPoints * @returns {Promise>} */ - static async getAll(): Promise> { + static async getAllForVersionedPlan(versionedPlanId: string): Promise> { return await dataSource .getRepository(inspectionPoint) .createQueryBuilder("inspectionPoint") - .orderBy("type", "ASC") + .where({ versionedPlanId }) + .orderBy("sort", "ASC") .getMany() .then((res) => { return res; diff --git a/src/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts index cd8ba7d..8262dd9 100644 --- a/src/service/unit/inspection/inspectionService.ts +++ b/src/service/unit/inspection/inspectionService.ts @@ -4,14 +4,20 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class InspectionService { /** - * @description get all inspection types + * @description get all inspections for related * @returns {Promise>} */ - static async getAll(): Promise> { + static async getAllForRelated(where: { equipmentId: string } | { vehicleId: string }): Promise> { return await dataSource .getRepository(inspection) .createQueryBuilder("inspection") - .orderBy("type", "ASC") + .leftJoinAndSelect("inspection.inspectionPlan", "inspectionPlan") + .leftJoinAndSelect("inspection.inspectionVersionedPlan", "inspectionVersionedPlan") + .leftJoinAndSelect("inspectionVersionedPlan.inspectionPoints", "inspectionPoints") + .leftJoinAndSelect("inspection.pointResults", "pointResults") + .leftJoinAndSelect("pointResults.inspectionPoint", "inspectionPoint") + .where(where) + .orderBy("createdAt", "DESC") .getMany() .then((res) => { return res; @@ -29,6 +35,11 @@ export default abstract class InspectionService { return await dataSource .getRepository(inspection) .createQueryBuilder("inspection") + .leftJoinAndSelect("inspection.inspectionPlan", "inspectionPlan") + .leftJoinAndSelect("inspection.inspectionVersionedPlan", "inspectionVersionedPlan") + .leftJoinAndSelect("inspectionVersionedPlan.inspectionPoints", "inspectionPoints") + .leftJoinAndSelect("inspection.pointResults", "pointResults") + .leftJoinAndSelect("pointResults.inspectionPoint", "inspectionPoint") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/inspection/inspectionVersionedPlanService.ts b/src/service/unit/inspection/inspectionVersionedPlanService.ts index ebaa296..9313979 100644 --- a/src/service/unit/inspection/inspectionVersionedPlanService.ts +++ b/src/service/unit/inspection/inspectionVersionedPlanService.ts @@ -4,14 +4,16 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class InspectionVersionedPlanService { /** - * @description get all inspectionVersionedPlan types + * @description get all inspectionVersionedPlans * @returns {Promise>} */ - static async getAll(): Promise> { + static async getAllForInspectionPlan(inspectionPlanId: string): Promise> { return await dataSource .getRepository(inspectionVersionedPlan) .createQueryBuilder("inspectionVersionedPlan") - .orderBy("type", "ASC") + .leftJoinAndSelect("inspectionVersionedPlan.inspectionPoints", "inspectionPoints") + .where({ inspectionPlanId }) + .orderBy("version", "ASC") .getMany() .then((res) => { return res; @@ -29,6 +31,7 @@ export default abstract class InspectionVersionedPlanService { return await dataSource .getRepository(inspectionVersionedPlan) .createQueryBuilder("inspectionVersionedPlan") + .leftJoinAndSelect("inspectionVersionedPlan.inspectionPoints", "inspectionPoints") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/vehicle/vehicleService.ts b/src/service/unit/vehicle/vehicleService.ts index 898911f..39d7b06 100644 --- a/src/service/unit/vehicle/vehicleService.ts +++ b/src/service/unit/vehicle/vehicleService.ts @@ -4,14 +4,15 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class VehicleService { /** - * @description get all vehicle types + * @description get all vehicles * @returns {Promise>} */ static async getAll(): Promise> { return await dataSource .getRepository(vehicle) .createQueryBuilder("vehicle") - .orderBy("type", "ASC") + .leftJoinAndSelect("vehicle.vehicleType", "vehicletype") + .orderBy("name", "ASC") .getMany() .then((res) => { return res; @@ -29,6 +30,7 @@ export default abstract class VehicleService { return await dataSource .getRepository(vehicle) .createQueryBuilder("vehicle") + .leftJoinAndSelect("vehicle.vehicleType", "vehicletype") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/vehicle/vehicleTypeService.ts b/src/service/unit/vehicle/vehicleTypeService.ts index 2af3acc..8ba8104 100644 --- a/src/service/unit/vehicle/vehicleTypeService.ts +++ b/src/service/unit/vehicle/vehicleTypeService.ts @@ -4,7 +4,7 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class VehicleTypeService { /** - * @description get all vehicleType types + * @description get all vehicleTypes * @returns {Promise>} */ static async getAll(): Promise> { diff --git a/src/service/unit/wearable/wearableService.ts b/src/service/unit/wearable/wearableService.ts index 77c120a..9fc7325 100644 --- a/src/service/unit/wearable/wearableService.ts +++ b/src/service/unit/wearable/wearableService.ts @@ -4,14 +4,15 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class WearableService { /** - * @description get all wearable types + * @description get all wearables * @returns {Promise>} */ static async getAll(): Promise> { return await dataSource .getRepository(wearable) .createQueryBuilder("wearable") - .orderBy("type", "ASC") + .leftJoinAndSelect("wearable.wearableType", "wearabletype") + .orderBy("name", "ASC") .getMany() .then((res) => { return res; @@ -29,6 +30,7 @@ export default abstract class WearableService { return await dataSource .getRepository(wearable) .createQueryBuilder("wearable") + .leftJoinAndSelect("wearable.wearableType", "wearabletype") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/wearable/wearableTypeService.ts b/src/service/unit/wearable/wearableTypeService.ts index 77f7a90..63a696b 100644 --- a/src/service/unit/wearable/wearableTypeService.ts +++ b/src/service/unit/wearable/wearableTypeService.ts @@ -4,7 +4,7 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class WearableTypeService { /** - * @description get all wearableType types + * @description get all wearableTypes * @returns {Promise>} */ static async getAll(): Promise> { From 117ced38ab297d0b53b8775223f1d2c5722445c7 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 28 May 2025 18:30:00 +0200 Subject: [PATCH 11/55] factory and restructure view models --- src/entity/unit/damageReport.ts | 17 +++--- src/entity/unit/inspection/inspection.ts | 4 +- src/entity/unit/inspection/inspectionPlan.ts | 22 +++++--- src/entity/unit/inspection/inspectionPoint.ts | 4 +- src/entity/unit/wearable/wearable.ts | 6 +- src/factory/admin/unit/damageReport.ts | 55 +++++++++++++++++++ src/factory/admin/unit/equipment/equipment.ts | 32 +++++++++++ .../admin/unit/equipment/equipmentType.ts | 26 +++++++++ .../admin/unit/inspection/inspection.ts | 50 +++++++++++++++++ .../admin/unit/inspection/inspectionPlan.ts | 44 +++++++++++++++ .../admin/unit/inspection/inspectionPoint.ts | 30 ++++++++++ .../unit/inspection/inspectionPointResult.ts | 29 ++++++++++ .../inspection/inspectionVersionedPlan.ts | 28 ++++++++++ src/factory/admin/unit/vehicle/vehicle.ts | 32 +++++++++++ src/factory/admin/unit/vehicle/vehicleType.ts | 26 +++++++++ src/factory/admin/unit/wearable/wearable.ts | 35 ++++++++++++ .../admin/unit/wearable/wearableType.ts | 26 +++++++++ src/service/unit/damageReport.ts | 3 + .../unit/inspection/inspectionPlanService.ts | 4 ++ .../unit/inspection/inspectionService.ts | 4 ++ src/service/unit/wearable/wearableService.ts | 1 + .../{damageReport => }/damageReport.models.ts | 25 +++++---- .../admin/unit/equipment/equipment.models.ts | 4 +- .../equipmentType.models.ts | 3 - .../unit/inspection/inspection.models.ts | 4 +- .../inspectionPlan.models.ts | 2 + .../respiratoryGear.models.ts | 0 .../respiratoryMission.models.ts | 0 .../respiratoryWearer.models.ts | 0 .../admin/unit/vehicle/vehicle.models.ts | 4 +- .../vehicleType.models.ts | 3 - .../admin/unit/wearable/wearable.models.ts | 2 +- .../wearableType.models.ts | 0 33 files changed, 479 insertions(+), 46 deletions(-) create mode 100644 src/factory/admin/unit/damageReport.ts create mode 100644 src/factory/admin/unit/equipment/equipment.ts create mode 100644 src/factory/admin/unit/equipment/equipmentType.ts create mode 100644 src/factory/admin/unit/inspection/inspection.ts create mode 100644 src/factory/admin/unit/inspection/inspectionPlan.ts create mode 100644 src/factory/admin/unit/inspection/inspectionPoint.ts create mode 100644 src/factory/admin/unit/inspection/inspectionPointResult.ts create mode 100644 src/factory/admin/unit/inspection/inspectionVersionedPlan.ts create mode 100644 src/factory/admin/unit/vehicle/vehicle.ts create mode 100644 src/factory/admin/unit/vehicle/vehicleType.ts create mode 100644 src/factory/admin/unit/wearable/wearable.ts create mode 100644 src/factory/admin/unit/wearable/wearableType.ts rename src/viewmodel/admin/unit/{damageReport => }/damageReport.models.ts (70%) rename src/viewmodel/admin/unit/{equipmentType => equipment}/equipmentType.models.ts (67%) rename src/viewmodel/admin/unit/{inspectionPlan => inspection}/inspectionPlan.models.ts (97%) rename src/viewmodel/admin/unit/{respiratoryGear => respiratory}/respiratoryGear.models.ts (100%) rename src/viewmodel/admin/unit/{respiratoryMission => respiratory}/respiratoryMission.models.ts (100%) rename src/viewmodel/admin/unit/{respiratoryWearer => respiratory}/respiratoryWearer.models.ts (100%) rename src/viewmodel/admin/unit/{vehicleType => vehicle}/vehicleType.models.ts (67%) rename src/viewmodel/admin/unit/{wearableType => wearable}/wearableType.models.ts (100%) diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts index a59c9e2..dd0955e 100644 --- a/src/entity/unit/damageReport.ts +++ b/src/entity/unit/damageReport.ts @@ -23,33 +23,36 @@ export class damageReport { @Column({ type: "varchar", length: 255 }) reportedBy: string; - @Column({ nullable: true, default: null }) - equipmentId: string; + @Column({ type: "int", default: 0 }) + imageCount: number; @Column({ nullable: true, default: null }) - vehicleId: string; + equipmentId?: string; @Column({ nullable: true, default: null }) - wearableId: string; + vehicleId?: string; + + @Column({ nullable: true, default: null }) + wearableId?: string; @ManyToOne(() => equipment, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) - equipment: equipment; + equipment?: equipment; @ManyToOne(() => vehicle, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) - vehicle: vehicle; + vehicle?: vehicle; @ManyToOne(() => wearable, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) - wearable: wearable; + wearable?: wearable; } diff --git a/src/entity/unit/inspection/inspection.ts b/src/entity/unit/inspection/inspection.ts index 77e32d0..00faf98 100644 --- a/src/entity/unit/inspection/inspection.ts +++ b/src/entity/unit/inspection/inspection.ts @@ -30,10 +30,10 @@ export class inspection { inspectionVersionedPlanId: string; @Column() - equipmentId: string; + equipmentId?: string; @Column() - vehicleId: string; + vehicleId?: string; @ManyToOne(() => inspectionPlan, { nullable: false, diff --git a/src/entity/unit/inspection/inspectionPlan.ts b/src/entity/unit/inspection/inspectionPlan.ts index 9be6406..f94bf5a 100644 --- a/src/entity/unit/inspection/inspectionPlan.ts +++ b/src/entity/unit/inspection/inspectionPlan.ts @@ -1,7 +1,7 @@ import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { equipment } from "../equipment/equipment"; import { vehicle } from "../vehicle/vehicle"; -import { PlanTimeDefinition } from "../../../viewmodel/admin/unit/inspectionPlan/inspectionPlan.models"; +import { PlanTimeDefinition } from "../../../viewmodel/admin/unit/inspection/inspectionPlan.models"; import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; @Entity() @@ -22,16 +22,24 @@ export class inspectionPlan { createdAt: Date; @Column() - equipmentId: string; + equipmentId?: string; @Column() - vehicleId: string; + vehicleId?: string; - @ManyToOne(() => equipment) - equipment: equipment; + @ManyToOne(() => equipment, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + equipment?: equipment; - @ManyToOne(() => vehicle) - vehicle: vehicle; + @ManyToOne(() => vehicle, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + vehicle?: vehicle; @OneToMany(() => inspectionVersionedPlan, (ivp) => ivp.inspectionPlan, { cascade: ["insert"], diff --git a/src/entity/unit/inspection/inspectionPoint.ts b/src/entity/unit/inspection/inspectionPoint.ts index 2ff25b1..4995bc8 100644 --- a/src/entity/unit/inspection/inspectionPoint.ts +++ b/src/entity/unit/inspection/inspectionPoint.ts @@ -28,10 +28,10 @@ export class inspectionPoint { type: InspectionPointEnum; @Column({ type: "int", nullable: true, default: null }) - min: number; + min?: number; @Column({ type: "int", nullable: true, default: null }) - max: number; + max?: number; @Column({ type: "int", default: 0 }) sort: number; diff --git a/src/entity/unit/wearable/wearable.ts b/src/entity/unit/wearable/wearable.ts index f532cb0..3b1ea39 100644 --- a/src/entity/unit/wearable/wearable.ts +++ b/src/entity/unit/wearable/wearable.ts @@ -29,7 +29,7 @@ export class wearable { wearableTypeId: string; @Column() - wearerId: string; + wearerId?: string; @ManyToOne(() => wearableType, { nullable: false, @@ -39,11 +39,11 @@ export class wearable { wearableType: wearableType; @ManyToOne(() => member, { - nullable: false, + nullable: true, onDelete: "SET NULL", onUpdate: "RESTRICT", }) - wearer: member; + wearer?: member; @OneToMany(() => damageReport, (d) => d.wearable, { cascade: ["insert"] }) reports: damageReport[]; diff --git a/src/factory/admin/unit/damageReport.ts b/src/factory/admin/unit/damageReport.ts new file mode 100644 index 0000000..908c0fe --- /dev/null +++ b/src/factory/admin/unit/damageReport.ts @@ -0,0 +1,55 @@ +import { damageReport } from "../../../entity/unit/damageReport"; +import { DamageReportAssigned, DamageReportViewModel } from "../../../viewmodel/admin/unit/damageReport.models"; +import EquipmentFactory from "./equipment/equipment"; +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, + reported: record.reportedAt, + status: record.status, + done: record.done, + description: record.description, + imageCount: record.imageCount, + reportedBy: record?.reportedBy, + ...assigned, + }; + } + + /** + * @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..6e84107 --- /dev/null +++ b/src/factory/admin/unit/equipment/equipmentType.ts @@ -0,0 +1,26 @@ +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, + }; + } + + /** + * @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..3505e11 --- /dev/null +++ b/src/factory/admin/unit/inspection/inspection.ts @@ -0,0 +1,50 @@ +import { inspection } from "../../../../entity/unit/inspection/inspection"; +import { InspectionViewModel } from "../../../../viewmodel/admin/unit/inspection/inspection.models"; +import EquipmentFactory from "../equipment/equipment"; +import VehicleFactory from "../vehicle/vehicle"; +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 mapToSingle(record: inspection): InspectionViewModel { + 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), + ...(record.equipmentId + ? { + relatedId: record.equipmentId, + assigned: "equipment", + related: EquipmentFactory.mapToSingle(record.equipment), + } + : { + relatedId: record.vehicleId, + assigned: "vehicle", + related: VehicleFactory.mapToSingle(record.vehicle), + }), + }; + } + + /** + * @description map records to inspection + * @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/inspectionPlan.ts b/src/factory/admin/unit/inspection/inspectionPlan.ts new file mode 100644 index 0000000..2e8fe5b --- /dev/null +++ b/src/factory/admin/unit/inspection/inspectionPlan.ts @@ -0,0 +1,44 @@ +import { inspectionPlan } from "../../../../entity/unit/inspection/inspectionPlan"; +import { InspectionPlanViewModel } from "../../../../viewmodel/admin/unit/inspection/inspectionPlan.models"; +import EquipmentFactory from "../equipment/equipment"; +import VehicleFactory from "../vehicle/vehicle"; +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 { + return { + id: record.id, + title: record.title, + inspectionInterval: record.inspectionInterval, + remindTime: record.remindTime, + version: record.latestVersionedPlan.version, + created: record.createdAt, + inspectionPoints: InspectionPointFactory.mapToBase(record.latestVersionedPlan.inspectionPoints), + ...(record.equipmentId + ? { + relatedId: record.equipmentId, + assigned: "equipment", + related: EquipmentFactory.mapToSingle(record.equipment), + } + : { + relatedId: record.vehicleId, + assigned: "vehicle", + related: VehicleFactory.mapToSingle(record.vehicle), + }), + }; + } + + /** + * @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..1f6150c --- /dev/null +++ b/src/factory/admin/unit/inspection/inspectionPoint.ts @@ -0,0 +1,30 @@ +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, + }; + } + + /** + * @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..b258a6a --- /dev/null +++ b/src/factory/admin/unit/inspection/inspectionPointResult.ts @@ -0,0 +1,29 @@ +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, + inspectionVersionedPlanId: record.inspection.inspectionVersionedPlanId, + 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/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..6155ca1 --- /dev/null +++ b/src/factory/admin/unit/vehicle/vehicleType.ts @@ -0,0 +1,26 @@ +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, + }; + } + + /** + * @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..b574bae --- /dev/null +++ b/src/factory/admin/unit/wearable/wearableType.ts @@ -0,0 +1,26 @@ +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, + }; + } + + /** + * @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/service/unit/damageReport.ts b/src/service/unit/damageReport.ts index b6134d6..ef8b999 100644 --- a/src/service/unit/damageReport.ts +++ b/src/service/unit/damageReport.ts @@ -11,6 +11,9 @@ export default abstract class DamageReportService { return await dataSource .getRepository(damageReport) .createQueryBuilder("damageReport") + .leftJoinAndSelect("damageReport.equipment", "equipment") + .leftJoinAndSelect("damageReport.vehicle", "vehicle") + .leftJoinAndSelect("damageReport.wearable", "wearable") .orderBy("type", "ASC") .getMany() .then((res) => { diff --git a/src/service/unit/inspection/inspectionPlanService.ts b/src/service/unit/inspection/inspectionPlanService.ts index 0e2cc47..e9495fe 100644 --- a/src/service/unit/inspection/inspectionPlanService.ts +++ b/src/service/unit/inspection/inspectionPlanService.ts @@ -23,6 +23,8 @@ export default abstract class InspectionPlanService { : "latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX(ivp.start) FROM inspection_versioned_plan ivp WHERE ivp.inspectionPlanId = inspectionPlan.id)" ) .leftJoinAndSelect("latestVersionedPlan.inspectionPoints", "inspectionPoints") + .leftJoinAndSelect("inspectionPlan.equipment", "equipment") + .leftJoinAndSelect("inspectionPlan.vehicle", "vehicle") .where(where) .orderBy("title", "ASC") .getMany() @@ -51,6 +53,8 @@ export default abstract class InspectionPlanService { : "latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX(ivp.start) FROM inspection_versioned_plan ivp WHERE ivp.inspectionPlanId = inspectionPlan.id)" ) .leftJoinAndSelect("latestVersionedPlan.inspectionPoints", "inspectionPoints") + .leftJoinAndSelect("inspectionPlan.equipment", "equipment") + .leftJoinAndSelect("inspectionPlan.vehicle", "vehicle") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts index 8262dd9..0fdf490 100644 --- a/src/service/unit/inspection/inspectionService.ts +++ b/src/service/unit/inspection/inspectionService.ts @@ -16,6 +16,8 @@ export default abstract class InspectionService { .leftJoinAndSelect("inspectionVersionedPlan.inspectionPoints", "inspectionPoints") .leftJoinAndSelect("inspection.pointResults", "pointResults") .leftJoinAndSelect("pointResults.inspectionPoint", "inspectionPoint") + .leftJoinAndSelect("inspection.equipment", "equipment") + .leftJoinAndSelect("inspection.vehicle", "vehicle") .where(where) .orderBy("createdAt", "DESC") .getMany() @@ -40,6 +42,8 @@ export default abstract class InspectionService { .leftJoinAndSelect("inspectionVersionedPlan.inspectionPoints", "inspectionPoints") .leftJoinAndSelect("inspection.pointResults", "pointResults") .leftJoinAndSelect("pointResults.inspectionPoint", "inspectionPoint") + .leftJoinAndSelect("inspection.equipment", "equipment") + .leftJoinAndSelect("inspection.vehicle", "vehicle") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/wearable/wearableService.ts b/src/service/unit/wearable/wearableService.ts index 9fc7325..7733d4a 100644 --- a/src/service/unit/wearable/wearableService.ts +++ b/src/service/unit/wearable/wearableService.ts @@ -12,6 +12,7 @@ export default abstract class WearableService { .getRepository(wearable) .createQueryBuilder("wearable") .leftJoinAndSelect("wearable.wearableType", "wearabletype") + .leftJoinAndSelect("wearable.wearer", "wearer") .orderBy("name", "ASC") .getMany() .then((res) => { diff --git a/src/viewmodel/admin/unit/damageReport/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport.models.ts similarity index 70% rename from src/viewmodel/admin/unit/damageReport/damageReport.models.ts rename to src/viewmodel/admin/unit/damageReport.models.ts index a144114..b0f5c20 100644 --- a/src/viewmodel/admin/unit/damageReport/damageReport.models.ts +++ b/src/viewmodel/admin/unit/damageReport.models.ts @@ -1,15 +1,8 @@ -import type { EquipmentViewModel } from "../equipment/equipment.models"; -import type { VehicleViewModel } from "../vehicle/vehicle.models"; -import type { WearableViewModel } from "../wearable/wearable.models"; +import { EquipmentViewModel } from "./equipment/equipment.models"; +import { VehicleViewModel } from "./vehicle/vehicle.models"; +import { WearableViewModel } from "./wearable/wearable.models"; -export type DamageReportViewModel = { - id: string; - reported: Date; - status: string; - done: boolean; - description: string; - providedImage: Array; - reportedBy: string; +export type DamageReportAssigned = { relatedId: string; } & ( | { @@ -26,6 +19,16 @@ export type DamageReportViewModel = { } ); +export type DamageReportViewModel = { + id: string; + reported: Date; + status: string; + done: boolean; + description: string; + imageCount: number; + reportedBy: string; +} & DamageReportAssigned; + export interface CreateDamageReportViewModel { description: string; reportedBy: string; diff --git a/src/viewmodel/admin/unit/equipment/equipment.models.ts b/src/viewmodel/admin/unit/equipment/equipment.models.ts index dcd4cea..abbdee7 100644 --- a/src/viewmodel/admin/unit/equipment/equipment.models.ts +++ b/src/viewmodel/admin/unit/equipment/equipment.models.ts @@ -1,5 +1,4 @@ -import type { EquipmentTypeViewModel } from "../equipmentType/equipmentType.models"; -import type { InspectionViewModel } from "../inspection/inspection.models"; +import type { EquipmentTypeViewModel } from "./equipmentType.models"; export interface EquipmentViewModel { id: string; @@ -10,7 +9,6 @@ export interface EquipmentViewModel { decommissioned?: Date; equipmentTypeId: string; equipmentType: EquipmentTypeViewModel; - inspections: Array; } export interface CreateEquipmentViewModel { diff --git a/src/viewmodel/admin/unit/equipmentType/equipmentType.models.ts b/src/viewmodel/admin/unit/equipment/equipmentType.models.ts similarity index 67% rename from src/viewmodel/admin/unit/equipmentType/equipmentType.models.ts rename to src/viewmodel/admin/unit/equipment/equipmentType.models.ts index e3116c9..fcf47ee 100644 --- a/src/viewmodel/admin/unit/equipmentType/equipmentType.models.ts +++ b/src/viewmodel/admin/unit/equipment/equipmentType.models.ts @@ -1,10 +1,7 @@ -import type { InspectionPlanViewModel } from "../inspectionPlan/inspectionPlan.models"; - export interface EquipmentTypeViewModel { id: string; type: string; description: string; - inspectionPlans: Array; } export interface CreateEquipmentTypeViewModel { diff --git a/src/viewmodel/admin/unit/inspection/inspection.models.ts b/src/viewmodel/admin/unit/inspection/inspection.models.ts index 8cd2e5f..6565377 100644 --- a/src/viewmodel/admin/unit/inspection/inspection.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspection.models.ts @@ -1,8 +1,9 @@ import type { EquipmentViewModel } from "../equipment/equipment.models"; import type { InspectionPlanViewModel, + InspectionPointViewModel, InspectionVersionedPlanViewModel, -} from "../inspectionPlan/inspectionPlan.models"; +} from "./inspectionPlan.models"; import type { VehicleViewModel } from "../vehicle/vehicle.models"; export type InspectionViewModel = { @@ -33,5 +34,6 @@ export interface InspectionPointResultViewModel { inspectionId: string; inspectionVersionedPlanId: string; inspectionPointId: string; + inspectionPoint: InspectionPointViewModel; value: string; } diff --git a/src/viewmodel/admin/unit/inspectionPlan/inspectionPlan.models.ts b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts similarity index 97% rename from src/viewmodel/admin/unit/inspectionPlan/inspectionPlan.models.ts rename to src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts index 31384d7..b2aecc0 100644 --- a/src/viewmodel/admin/unit/inspectionPlan/inspectionPlan.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts @@ -37,6 +37,8 @@ export interface InspectionPointViewModel { description: string; type: InspectionPointEnum; min?: number; + max?: number; + sort: number; } export interface CreateInspectionPlanViewModel { diff --git a/src/viewmodel/admin/unit/respiratoryGear/respiratoryGear.models.ts b/src/viewmodel/admin/unit/respiratory/respiratoryGear.models.ts similarity index 100% rename from src/viewmodel/admin/unit/respiratoryGear/respiratoryGear.models.ts rename to src/viewmodel/admin/unit/respiratory/respiratoryGear.models.ts diff --git a/src/viewmodel/admin/unit/respiratoryMission/respiratoryMission.models.ts b/src/viewmodel/admin/unit/respiratory/respiratoryMission.models.ts similarity index 100% rename from src/viewmodel/admin/unit/respiratoryMission/respiratoryMission.models.ts rename to src/viewmodel/admin/unit/respiratory/respiratoryMission.models.ts diff --git a/src/viewmodel/admin/unit/respiratoryWearer/respiratoryWearer.models.ts b/src/viewmodel/admin/unit/respiratory/respiratoryWearer.models.ts similarity index 100% rename from src/viewmodel/admin/unit/respiratoryWearer/respiratoryWearer.models.ts rename to src/viewmodel/admin/unit/respiratory/respiratoryWearer.models.ts diff --git a/src/viewmodel/admin/unit/vehicle/vehicle.models.ts b/src/viewmodel/admin/unit/vehicle/vehicle.models.ts index 0bfccb6..4e273f6 100644 --- a/src/viewmodel/admin/unit/vehicle/vehicle.models.ts +++ b/src/viewmodel/admin/unit/vehicle/vehicle.models.ts @@ -1,5 +1,4 @@ -import type { InspectionViewModel } from "../inspection/inspection.models"; -import type { VehicleTypeViewModel } from "../vehicleType/vehicleType.models"; +import type { VehicleTypeViewModel } from "./vehicleType.models"; export interface VehicleViewModel { id: string; @@ -10,7 +9,6 @@ export interface VehicleViewModel { decommissioned?: Date; vehicleTypeId: string; vehicleType: VehicleTypeViewModel; - inspections: Array; } export interface CreateVehicleViewModel { diff --git a/src/viewmodel/admin/unit/vehicleType/vehicleType.models.ts b/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts similarity index 67% rename from src/viewmodel/admin/unit/vehicleType/vehicleType.models.ts rename to src/viewmodel/admin/unit/vehicle/vehicleType.models.ts index 2de497f..ea6c7cf 100644 --- a/src/viewmodel/admin/unit/vehicleType/vehicleType.models.ts +++ b/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts @@ -1,10 +1,7 @@ -import type { InspectionPlanViewModel } from "../inspectionPlan/inspectionPlan.models"; - export interface VehicleTypeViewModel { id: string; type: string; description: string; - inspectionPlans: Array; } export interface CreateVehicleTypeViewModel { diff --git a/src/viewmodel/admin/unit/wearable/wearable.models.ts b/src/viewmodel/admin/unit/wearable/wearable.models.ts index 5e4fccc..e2c5bbc 100644 --- a/src/viewmodel/admin/unit/wearable/wearable.models.ts +++ b/src/viewmodel/admin/unit/wearable/wearable.models.ts @@ -1,5 +1,5 @@ import { MemberViewModel } from "../../club/member/member.models"; -import type { WearableTypeViewModel } from "../wearableType/wearableType.models"; +import type { WearableTypeViewModel } from "./wearableType.models"; export interface WearableViewModel { id: string; diff --git a/src/viewmodel/admin/unit/wearableType/wearableType.models.ts b/src/viewmodel/admin/unit/wearable/wearableType.models.ts similarity index 100% rename from src/viewmodel/admin/unit/wearableType/wearableType.models.ts rename to src/viewmodel/admin/unit/wearable/wearableType.models.ts From cb60d617735e7747ee4b7a2bd1c81e8b54dab9e7 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 28 May 2025 18:30:24 +0200 Subject: [PATCH 12/55] update migration to image count on damageReport --- src/migrations/baseSchemaTables/unit_extend.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index a81f8ac..9115540 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -10,6 +10,7 @@ export const damage_report_table = new Table({ { 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), default: getDefaultByORM("null") }, { name: "vehicleId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, { name: "wearableId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, From 0f6401953f1d4ea60d8500fe265c06a01859bb95 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 28 May 2025 22:51:17 +0200 Subject: [PATCH 13/55] add maintenance model, factory and service --- src/data-source.ts | 2 + src/entity/unit/damageReport.ts | 13 ++++- src/entity/unit/maintenance.ts | 56 +++++++++++++++++++ src/factory/admin/unit/damageReport.ts | 4 +- src/factory/admin/unit/maintenance.ts | 55 ++++++++++++++++++ src/migrations/1748261477410-unitBase.ts | 4 +- .../baseSchemaTables/unit_extend.ts | 45 +++++++++++++++ src/service/unit/damageReport.ts | 5 ++ src/service/unit/maintenance.ts | 49 ++++++++++++++++ .../admin/unit/damageReport.models.ts | 4 +- .../admin/unit/maintenance.models.ts | 43 ++++++++++++++ 11 files changed, 276 insertions(+), 4 deletions(-) create mode 100644 src/entity/unit/maintenance.ts create mode 100644 src/factory/admin/unit/maintenance.ts create mode 100644 src/service/unit/maintenance.ts create mode 100644 src/viewmodel/admin/unit/maintenance.models.ts diff --git a/src/data-source.ts b/src/data-source.ts index 85fe595..42e035a 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -69,6 +69,7 @@ import { inspectionPoint } from "./entity/unit/inspection/inspectionPoint"; import { inspection } from "./entity/unit/inspection/inspection"; import { inspectionPointResult } from "./entity/unit/inspection/inspectionPointResult"; import { UnitBase1748261477410 } from "./migrations/1748261477410-unitBase"; +import { maintenance } from "./entity/unit/maintenance"; configCheck(); @@ -131,6 +132,7 @@ const dataSource = new DataSource({ wearableType, wearable, damageReport, + maintenance, inspectionPlan, inspectionVersionedPlan, inspectionPoint, diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts index dd0955e..20a78c2 100644 --- a/src/entity/unit/damageReport.ts +++ b/src/entity/unit/damageReport.ts @@ -1,7 +1,8 @@ -import { Check, Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; +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 { @@ -29,6 +30,9 @@ export class damageReport { @Column({ nullable: true, default: null }) equipmentId?: string; + @Column({ nullable: true, default: null }) + maintenanceId: string; + @Column({ nullable: true, default: null }) vehicleId?: string; @@ -55,4 +59,11 @@ export class damageReport { onUpdate: "RESTRICT", }) wearable?: wearable; + + @ManyToOne(() => maintenance, { + nullable: true, + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }) + maintenance?: maintenance; } diff --git a/src/entity/unit/maintenance.ts b/src/entity/unit/maintenance.ts new file mode 100644 index 0000000..f3c9669 --- /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, { + 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(() => damageReport, (dr) => dr.maintenance) + reports: damageReport[]; +} diff --git a/src/factory/admin/unit/damageReport.ts b/src/factory/admin/unit/damageReport.ts index 908c0fe..fb9115e 100644 --- a/src/factory/admin/unit/damageReport.ts +++ b/src/factory/admin/unit/damageReport.ts @@ -1,6 +1,7 @@ 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"; @@ -34,13 +35,14 @@ export default abstract class DamageReportFactory { return { id: record.id, - reported: record.reportedAt, + 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, }; } 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/migrations/1748261477410-unitBase.ts b/src/migrations/1748261477410-unitBase.ts index d4951c6..cc6e201 100644 --- a/src/migrations/1748261477410-unitBase.ts +++ b/src/migrations/1748261477410-unitBase.ts @@ -7,7 +7,7 @@ import { wearable_table, wearable_type_table, } from "./baseSchemaTables/unit"; -import { damage_report_table } from "./baseSchemaTables/unit_extend"; +import { damage_report_table, maintenance_table } from "./baseSchemaTables/unit_extend"; import { inspection_plan_table, inspection_point_result_table, @@ -27,6 +27,7 @@ export class UnitBase1748261477410 implements MigrationInterface { 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); @@ -46,6 +47,7 @@ export class UnitBase1748261477410 implements MigrationInterface { 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); diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index 9115540..3f78910 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -14,6 +14,51 @@ export const damage_report_table = new Table({ { name: "equipmentId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, { name: "vehicleId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, { name: "wearableId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "maintenanceId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + ], + 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("date"), default: getDefaultByORM("currentTimestamp") }, + { name: "status", ...getTypeByORM("varchar") }, + { name: "done", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "description", ...getTypeByORM("text") }, + { name: "equipmentId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "vehicleId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "wearableId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, ], foreignKeys: [ new TableForeignKey({ diff --git a/src/service/unit/damageReport.ts b/src/service/unit/damageReport.ts index ef8b999..7821ea1 100644 --- a/src/service/unit/damageReport.ts +++ b/src/service/unit/damageReport.ts @@ -14,6 +14,7 @@ export default abstract class DamageReportService { .leftJoinAndSelect("damageReport.equipment", "equipment") .leftJoinAndSelect("damageReport.vehicle", "vehicle") .leftJoinAndSelect("damageReport.wearable", "wearable") + .leftJoinAndSelect("damageReport.maintenance", "maintenance") .orderBy("type", "ASC") .getMany() .then((res) => { @@ -32,6 +33,10 @@ export default abstract class DamageReportService { return await dataSource .getRepository(damageReport) .createQueryBuilder("damageReport") + .leftJoinAndSelect("damageReport.equipment", "equipment") + .leftJoinAndSelect("damageReport.vehicle", "vehicle") + .leftJoinAndSelect("damageReport.wearable", "wearable") + .leftJoinAndSelect("damageReport.maintenance", "maintenance") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/maintenance.ts b/src/service/unit/maintenance.ts new file mode 100644 index 0000000..d9e8519 --- /dev/null +++ b/src/service/unit/maintenance.ts @@ -0,0 +1,49 @@ +import { dataSource } from "../../data-source"; +import { maintenance } from "../../entity/unit/maintenance"; +import DatabaseActionException from "../../exceptions/databaseActionException"; + +export default abstract class MaintenanceService { + /** + * @description get all maintenances + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(maintenance) + .createQueryBuilder("maintenance") + .leftJoinAndSelect("maintenance.equipment", "equipment") + .leftJoinAndSelect("maintenance.vehicle", "vehicle") + .leftJoinAndSelect("maintenance.wearable", "wearable") + .leftJoinAndSelect("maintenance.reports", "reports") + .orderBy("type", "ASC") + .getMany() + .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 dataSource + .getRepository(maintenance) + .createQueryBuilder("maintenance") + .leftJoinAndSelect("maintenance.equipment", "equipment") + .leftJoinAndSelect("maintenance.vehicle", "vehicle") + .leftJoinAndSelect("maintenance.wearable", "wearable") + .leftJoinAndSelect("maintenance.reports", "reports") + .where({ id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "maintenance", err); + }); + } +} diff --git a/src/viewmodel/admin/unit/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport.models.ts index b0f5c20..e675c9c 100644 --- a/src/viewmodel/admin/unit/damageReport.models.ts +++ b/src/viewmodel/admin/unit/damageReport.models.ts @@ -1,4 +1,5 @@ import { EquipmentViewModel } from "./equipment/equipment.models"; +import { MaintenanceViewModel } from "./maintenance.models"; import { VehicleViewModel } from "./vehicle/vehicle.models"; import { WearableViewModel } from "./wearable/wearable.models"; @@ -21,12 +22,13 @@ export type DamageReportAssigned = { export type DamageReportViewModel = { id: string; - reported: Date; + reportedAt: Date; status: string; done: boolean; description: string; imageCount: number; reportedBy: string; + maintenance?: MaintenanceViewModel; } & DamageReportAssigned; export interface CreateDamageReportViewModel { diff --git a/src/viewmodel/admin/unit/maintenance.models.ts b/src/viewmodel/admin/unit/maintenance.models.ts new file mode 100644 index 0000000..ce5c0b9 --- /dev/null +++ b/src/viewmodel/admin/unit/maintenance.models.ts @@ -0,0 +1,43 @@ +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; + +export interface CreateMaintenanceViewModel { + description: string; + reportedBy: string; + affectedId: string; + affected: "equipment" | "vehicle" | "wearable"; +} + +export interface UpdateMaintenanceViewModel { + id: string; + status: string; + done: boolean; +} From 7883bb7d7f25ab6d74e632fc9d51afa77c5b7330 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Thu, 29 May 2025 10:31:40 +0200 Subject: [PATCH 14/55] command Handlers and schema update --- src/command/unit/damageReportCommand.ts | 22 ++++ .../unit/damageReportCommandHandler.ts | 100 ++++++++++++++++++ .../unit/equipment/equipmentCommand.ts | 20 ++++ .../unit/equipment/equipmentCommandHandler.ts | 74 +++++++++++++ .../unit/equipment/equipmentTypeCommand.ts | 14 +++ .../equipment/equipmentTypeCommandHandler.ts | 72 +++++++++++++ .../unit/inspection/inspectionCommand.ts | 17 +++ .../inspection/inspectionCommandHandler.ts | 76 +++++++++++++ .../unit/inspection/inspectionPlanCommand.ts | 20 ++++ .../inspectionPlanCommandHandler.ts | 76 +++++++++++++ .../unit/inspection/inspectionPointCommand.ts | 11 ++ .../inspectionPointCommandHandler.ts | 34 ++++++ .../inspectionPointResultCommand.ts | 5 + .../inspectionPointResultCommandHandler.ts | 31 ++++++ .../inspectionVersionedPlanCommand.ts | 3 + .../inspectionVersionedPlanCommandHandler.ts | 28 +++++ src/command/unit/maintenanceCommand.ts | 16 +++ src/command/unit/maintenanceCommandHandler.ts | 72 +++++++++++++ src/command/unit/vehicle/vehicleCommand.ts | 20 ++++ .../unit/vehicle/vehicleCommandHandler.ts | 73 +++++++++++++ .../unit/vehicle/vehicleTypeCommand.ts | 14 +++ .../unit/vehicle/vehicleTypeCommandHandler.ts | 68 ++++++++++++ src/command/unit/wearable/wearableCommand.ts | 22 ++++ .../unit/wearable/wearableCommandHandler.ts | 76 +++++++++++++ .../unit/wearable/wearableTypeCommand.ts | 14 +++ .../wearable/wearableTypeCommandHandler.ts | 68 ++++++++++++ src/entity/unit/damageReport.ts | 2 +- .../unit/inspection/inspectionPointResult.ts | 15 ++- src/migrations/baseSchemaTables/inspection.ts | 5 +- ...damageReport.ts => damageReportService.ts} | 0 .../inspectionPointResultService.ts | 19 ---- .../inspectionVersionedPlanService.ts | 21 ++++ .../{maintenance.ts => maintenanceService.ts} | 0 .../admin/unit/damageReport.models.ts | 13 --- .../admin/unit/equipment/equipment.models.ts | 17 --- .../unit/equipment/equipmentType.models.ts | 11 -- .../unit/inspection/inspectionPlan.models.ts | 15 --- .../admin/unit/maintenance.models.ts | 13 --- .../admin/unit/vehicle/vehicle.models.ts | 17 --- .../admin/unit/vehicle/vehicleType.models.ts | 11 -- .../admin/unit/wearable/wearable.models.ts | 19 ---- .../unit/wearable/wearableType.models.ts | 11 -- 42 files changed, 1076 insertions(+), 159 deletions(-) create mode 100644 src/command/unit/damageReportCommand.ts create mode 100644 src/command/unit/damageReportCommandHandler.ts create mode 100644 src/command/unit/equipment/equipmentCommand.ts create mode 100644 src/command/unit/equipment/equipmentCommandHandler.ts create mode 100644 src/command/unit/equipment/equipmentTypeCommand.ts create mode 100644 src/command/unit/equipment/equipmentTypeCommandHandler.ts create mode 100644 src/command/unit/inspection/inspectionCommand.ts create mode 100644 src/command/unit/inspection/inspectionCommandHandler.ts create mode 100644 src/command/unit/inspection/inspectionPlanCommand.ts create mode 100644 src/command/unit/inspection/inspectionPlanCommandHandler.ts create mode 100644 src/command/unit/inspection/inspectionPointCommand.ts create mode 100644 src/command/unit/inspection/inspectionPointCommandHandler.ts create mode 100644 src/command/unit/inspection/inspectionPointResultCommand.ts create mode 100644 src/command/unit/inspection/inspectionPointResultCommandHandler.ts create mode 100644 src/command/unit/inspection/inspectionVersionedPlanCommand.ts create mode 100644 src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts create mode 100644 src/command/unit/maintenanceCommand.ts create mode 100644 src/command/unit/maintenanceCommandHandler.ts create mode 100644 src/command/unit/vehicle/vehicleCommand.ts create mode 100644 src/command/unit/vehicle/vehicleCommandHandler.ts create mode 100644 src/command/unit/vehicle/vehicleTypeCommand.ts create mode 100644 src/command/unit/vehicle/vehicleTypeCommandHandler.ts create mode 100644 src/command/unit/wearable/wearableCommand.ts create mode 100644 src/command/unit/wearable/wearableCommandHandler.ts create mode 100644 src/command/unit/wearable/wearableTypeCommand.ts create mode 100644 src/command/unit/wearable/wearableTypeCommandHandler.ts rename src/service/unit/{damageReport.ts => damageReportService.ts} (100%) rename src/service/unit/{maintenance.ts => maintenanceService.ts} (100%) 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..1c020fb --- /dev/null +++ b/src/command/unit/inspection/inspectionCommand.ts @@ -0,0 +1,17 @@ +export interface CreateInspectionCommand { + context: string; + nextInspection?: Date; + inspectionPlanId: string; + relatedId: string; + assigned: "vehicle" | "equipment"; +} + +export interface UpdateInspectionCommand { + id: string; + context: string; + nextInspection?: Date; +} + +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..5f4a61d --- /dev/null +++ b/src/command/unit/inspection/inspectionCommandHandler.ts @@ -0,0 +1,76 @@ +import { dataSource } from "../../../data-source"; +import { inspection } from "../../../entity/unit/inspection/inspection"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import InspectionVersionedPlanService from "../../../service/unit/inspection/inspectionVersionedPlanService"; +import { CreateInspectionCommand, UpdateInspectionCommand, DeleteInspectionCommand } 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 + ); + return await dataSource + .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, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .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 delete inspection + * @param {DeleteInspectionCommand} deleteInspection + * @returns {Promise} + */ + static async delete(deleteInspection: DeleteInspectionCommand): Promise { + return await dataSource + .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..cf48ef7 --- /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, + equipmentId: createInspectionPlan.assigned == "equipment" ? createInspectionPlan.relatedId : null, + vehicleId: 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..5aa45a6 --- /dev/null +++ b/src/command/unit/inspection/inspectionPointCommand.ts @@ -0,0 +1,11 @@ +import { InspectionPointEnum } from "../../../enums/inspectionEnum"; + +export interface CreateInspectionPointCommand { + title: string; + description: string; + type: InspectionPointEnum; + min?: number; + max?: number; + 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..749f325 --- /dev/null +++ b/src/command/unit/inspection/inspectionPointCommandHandler.ts @@ -0,0 +1,34 @@ +import { dataSource } from "../../../data-source"; +import { inspectionPoint } from "../../../entity/unit/inspection/inspectionPoint"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +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); + }); + } +} diff --git a/src/command/unit/inspection/inspectionPointResultCommand.ts b/src/command/unit/inspection/inspectionPointResultCommand.ts new file mode 100644 index 0000000..9fb36d6 --- /dev/null +++ b/src/command/unit/inspection/inspectionPointResultCommand.ts @@ -0,0 +1,5 @@ +export interface CreateInspectionPointResultCommand { + 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..23916dc --- /dev/null +++ b/src/command/unit/inspection/inspectionPointResultCommandHandler.ts @@ -0,0 +1,31 @@ +import { dataSource } from "../../../data-source"; +import { inspectionPointResult } from "../../../entity/unit/inspection/inspectionPointResult"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import { CreateInspectionPointResultCommand } from "./inspectionPointResultCommand"; + +export default abstract class InspectionPointResultCommandHandler { + /** + * @description create inspectionPointResult + * @param {CreateInspectionPointResultCommand} createInspectionPointResult + * @returns {Promise} + */ + static async createOrUpdate(createInspectionPointResult: CreateInspectionPointResultCommand): 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", "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..9c9710b --- /dev/null +++ b/src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts @@ -0,0 +1,28 @@ +import { dataSource } from "../../../data-source"; +import { inspectionVersionedPlan } from "../../../entity/unit/inspection/inspectionVersionedPlan"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; +import { CreateInspectionVersionedPlanCommand } from "./inspectionVersionedPlanCommand"; + +export default abstract class InspectionVersionedPlanCommandHandler { + /** + * @description create inspectionVersionedPlan + * @param {CreateInspectionVersionedPlanCommand} createInspectionVersionedPlan + * @returns {Promise} + */ + static async create(createInspectionVersionedPlan: CreateInspectionVersionedPlanCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(inspectionVersionedPlan) + .values({ + inspectionPlanId: createInspectionVersionedPlan.inspectionPlanId, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .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/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts index 20a78c2..f080e38 100644 --- a/src/entity/unit/damageReport.ts +++ b/src/entity/unit/damageReport.ts @@ -31,7 +31,7 @@ export class damageReport { equipmentId?: string; @Column({ nullable: true, default: null }) - maintenanceId: string; + maintenanceId?: string; @Column({ nullable: true, default: null }) vehicleId?: string; diff --git a/src/entity/unit/inspection/inspectionPointResult.ts b/src/entity/unit/inspection/inspectionPointResult.ts index 5b3501d..4f98e74 100644 --- a/src/entity/unit/inspection/inspectionPointResult.ts +++ b/src/entity/unit/inspection/inspectionPointResult.ts @@ -1,21 +1,18 @@ -import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; +import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm"; import { inspection } from "./inspection"; import { inspectionPoint } from "./inspectionPoint"; @Entity() export class inspectionPointResult { - @PrimaryGeneratedColumn("uuid") - id: string; + @PrimaryColumn() + inspectionId: string; + + @PrimaryColumn() + inspectionPointId: string; @Column({ type: "text" }) value: string; - @Column() - inspectionId: string; - - @Column() - inspectionPointId: string; - @ManyToOne(() => inspection, { nullable: false, onDelete: "CASCADE", diff --git a/src/migrations/baseSchemaTables/inspection.ts b/src/migrations/baseSchemaTables/inspection.ts index 83e856f..b064cb6 100644 --- a/src/migrations/baseSchemaTables/inspection.ts +++ b/src/migrations/baseSchemaTables/inspection.ts @@ -126,10 +126,9 @@ export const inspection_table = new Table({ export const inspection_point_result_table = new Table({ name: "inspection_point_result", columns: [ - { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "inspectionId", ...getTypeByORM("uuid"), isPrimary: true }, + { name: "inspectionPointId", ...getTypeByORM("uuid"), isPrimary: true }, { name: "value", ...getTypeByORM("text") }, - { name: "inspectionId", ...getTypeByORM("uuid") }, - { name: "inspectionPointId", ...getTypeByORM("uuid") }, ], foreignKeys: [ new TableForeignKey({ diff --git a/src/service/unit/damageReport.ts b/src/service/unit/damageReportService.ts similarity index 100% rename from src/service/unit/damageReport.ts rename to src/service/unit/damageReportService.ts diff --git a/src/service/unit/inspection/inspectionPointResultService.ts b/src/service/unit/inspection/inspectionPointResultService.ts index d538a63..d294139 100644 --- a/src/service/unit/inspection/inspectionPointResultService.ts +++ b/src/service/unit/inspection/inspectionPointResultService.ts @@ -21,23 +21,4 @@ export default abstract class InspectionPointResultService { throw new DatabaseActionException("SELECT", "inspectionPointResult", err); }); } - - /** - * @description get inspectionPointResult by id - * @returns {Promise} - */ - static async getById(id: string): Promise { - return await dataSource - .getRepository(inspectionPointResult) - .createQueryBuilder("inspectionPointResult") - .leftJoinAndSelect("inspectionPointResult.inspectionPoint", "inspectionPoint") - .where({ id }) - .getOneOrFail() - .then((res) => { - return res; - }) - .catch((err) => { - throw new DatabaseActionException("SELECT", "inspectionPointResult", err); - }); - } } diff --git a/src/service/unit/inspection/inspectionVersionedPlanService.ts b/src/service/unit/inspection/inspectionVersionedPlanService.ts index 9313979..55eab31 100644 --- a/src/service/unit/inspection/inspectionVersionedPlanService.ts +++ b/src/service/unit/inspection/inspectionVersionedPlanService.ts @@ -23,6 +23,27 @@ export default abstract class InspectionVersionedPlanService { }); } + /** + * @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} diff --git a/src/service/unit/maintenance.ts b/src/service/unit/maintenanceService.ts similarity index 100% rename from src/service/unit/maintenance.ts rename to src/service/unit/maintenanceService.ts diff --git a/src/viewmodel/admin/unit/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport.models.ts index e675c9c..9427d8a 100644 --- a/src/viewmodel/admin/unit/damageReport.models.ts +++ b/src/viewmodel/admin/unit/damageReport.models.ts @@ -30,16 +30,3 @@ export type DamageReportViewModel = { reportedBy: string; maintenance?: MaintenanceViewModel; } & DamageReportAssigned; - -export interface CreateDamageReportViewModel { - description: string; - reportedBy: string; - affectedId: string; - affected: "equipment" | "vehicle" | "wearable"; -} - -export interface UpdateDamageReportViewModel { - id: string; - status: string; - done: boolean; -} diff --git a/src/viewmodel/admin/unit/equipment/equipment.models.ts b/src/viewmodel/admin/unit/equipment/equipment.models.ts index abbdee7..2990c1d 100644 --- a/src/viewmodel/admin/unit/equipment/equipment.models.ts +++ b/src/viewmodel/admin/unit/equipment/equipment.models.ts @@ -10,20 +10,3 @@ export interface EquipmentViewModel { equipmentTypeId: string; equipmentType: EquipmentTypeViewModel; } - -export interface CreateEquipmentViewModel { - code?: string; - name: string; - location: string; - commissioned: Date; - equipmentTypeId: string; -} - -export interface UpdateEquipmentViewModel { - id: string; - code?: string; - name: string; - location: string; - commissioned: Date; - decommissioned?: Date; -} diff --git a/src/viewmodel/admin/unit/equipment/equipmentType.models.ts b/src/viewmodel/admin/unit/equipment/equipmentType.models.ts index fcf47ee..05ad905 100644 --- a/src/viewmodel/admin/unit/equipment/equipmentType.models.ts +++ b/src/viewmodel/admin/unit/equipment/equipmentType.models.ts @@ -3,14 +3,3 @@ export interface EquipmentTypeViewModel { type: string; description: string; } - -export interface CreateEquipmentTypeViewModel { - type: string; - description: string; -} - -export interface UpdateEquipmentTypeViewModel { - id: string; - type: string; - description: string; -} diff --git a/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts index b2aecc0..cbbea41 100644 --- a/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts @@ -40,18 +40,3 @@ export interface InspectionPointViewModel { max?: number; sort: number; } - -export interface CreateInspectionPlanViewModel { - title: string; - inspectionInterval: PlanTimeDefinition; - remindTime: PlanTimeDefinition; - relatedId: string; - assigned: "vehicle" | "equipment"; -} - -export interface UpdateInspectionPlanViewModel { - id: string; - title: string; - inspectionInterval: PlanTimeDefinition; - remindTime?: PlanTimeDefinition; -} diff --git a/src/viewmodel/admin/unit/maintenance.models.ts b/src/viewmodel/admin/unit/maintenance.models.ts index ce5c0b9..98f22f2 100644 --- a/src/viewmodel/admin/unit/maintenance.models.ts +++ b/src/viewmodel/admin/unit/maintenance.models.ts @@ -28,16 +28,3 @@ export type MaintenanceViewModel = { description: string; reports: DamageReportViewModel[]; } & MaintenanceAssigned; - -export interface CreateMaintenanceViewModel { - description: string; - reportedBy: string; - affectedId: string; - affected: "equipment" | "vehicle" | "wearable"; -} - -export interface UpdateMaintenanceViewModel { - id: string; - status: string; - done: boolean; -} diff --git a/src/viewmodel/admin/unit/vehicle/vehicle.models.ts b/src/viewmodel/admin/unit/vehicle/vehicle.models.ts index 4e273f6..b25ab0c 100644 --- a/src/viewmodel/admin/unit/vehicle/vehicle.models.ts +++ b/src/viewmodel/admin/unit/vehicle/vehicle.models.ts @@ -10,20 +10,3 @@ export interface VehicleViewModel { vehicleTypeId: string; vehicleType: VehicleTypeViewModel; } - -export interface CreateVehicleViewModel { - code?: string; - name: string; - location: string; - commissioned: Date; - vehicleTypeId: string; -} - -export interface UpdateVehicleViewModel { - id: string; - code?: string; - name: string; - location: string; - commissioned: Date; - decommissioned?: Date; -} diff --git a/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts b/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts index ea6c7cf..726311e 100644 --- a/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts +++ b/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts @@ -3,14 +3,3 @@ export interface VehicleTypeViewModel { type: string; description: string; } - -export interface CreateVehicleTypeViewModel { - type: string; - description: string; -} - -export interface UpdateVehicleTypeViewModel { - id: string; - type: string; - description: string; -} diff --git a/src/viewmodel/admin/unit/wearable/wearable.models.ts b/src/viewmodel/admin/unit/wearable/wearable.models.ts index e2c5bbc..e3bacac 100644 --- a/src/viewmodel/admin/unit/wearable/wearable.models.ts +++ b/src/viewmodel/admin/unit/wearable/wearable.models.ts @@ -13,22 +13,3 @@ export interface WearableViewModel { wearableTypeId: string; wearableType: WearableTypeViewModel; } - -export interface CreateWearableViewModel { - code?: string; - name: string; - wearerId?: string; - location?: string; - commissioned: Date; - wearableTypeId: string; -} - -export interface UpdateWearableViewModel { - id: string; - code?: string; - name: string; - location?: string; - commissioned: Date; - decommissioned?: Date; - wearerId?: string; -} diff --git a/src/viewmodel/admin/unit/wearable/wearableType.models.ts b/src/viewmodel/admin/unit/wearable/wearableType.models.ts index b3f49fc..73fd7c5 100644 --- a/src/viewmodel/admin/unit/wearable/wearableType.models.ts +++ b/src/viewmodel/admin/unit/wearable/wearableType.models.ts @@ -3,14 +3,3 @@ export interface WearableTypeViewModel { type: string; description: string; } - -export interface CreateWearableTypeViewModel { - type: string; - description: string; -} - -export interface UpdateWearableTypeViewModel { - id: string; - type: string; - description: string; -} From 0684605093deee62b07fd8beb9fde4e1d590cd8d Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Thu, 29 May 2025 10:52:05 +0200 Subject: [PATCH 15/55] base router and permissions --- src/routes/admin/index.ts | 54 +++++++++++++++++++ src/routes/admin/unit/equipment.ts | 10 ++++ src/routes/admin/unit/equipmentType.ts | 10 ++++ src/routes/admin/unit/inspection.ts | 10 ++++ src/routes/admin/unit/inspectionPlan.ts | 10 ++++ src/routes/admin/unit/vehicle.ts | 10 ++++ src/routes/admin/unit/vehicleType.ts | 10 ++++ src/routes/admin/unit/wearable.ts | 10 ++++ src/routes/admin/unit/wearableType.ts | 10 ++++ src/routes/public.ts | 4 ++ src/type/permissionTypes.ts | 70 ++++++++++++++++++++----- 11 files changed, 196 insertions(+), 12 deletions(-) create mode 100644 src/routes/admin/unit/equipment.ts create mode 100644 src/routes/admin/unit/equipmentType.ts create mode 100644 src/routes/admin/unit/inspection.ts create mode 100644 src/routes/admin/unit/inspectionPlan.ts create mode 100644 src/routes/admin/unit/vehicle.ts create mode 100644 src/routes/admin/unit/vehicleType.ts create mode 100644 src/routes/admin/unit/wearable.ts create mode 100644 src/routes/admin/unit/wearableType.ts diff --git a/src/routes/admin/index.ts b/src/routes/admin/index.ts index 4e74012..13e48f5 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"; @@ -14,6 +15,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"; @@ -21,6 +23,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"; @@ -28,8 +31,19 @@ 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"; + var router = express.Router({ mergeParams: true }); +/** configuration */ router.use( "/award", PermissionHelper.passCheckSomeMiddleware([ @@ -112,6 +126,7 @@ router.use( newsletterConfig ); +/** club */ router.use("/member", PermissionHelper.passCheckMiddleware("read", "club", "member"), member); router.use( "/protocol", @@ -143,6 +158,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", @@ -162,4 +178,42 @@ router.use( ); router.use("/setting", PermissionHelper.passCheckMiddleware("read", "management", "setting"), setting); +/** unit */ +router.use("/equipment", PermissionHelper.passCheckMiddleware("read", "unit", "equipment"), equipment); +router.use( + "/equipmenttype", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "equipment_type" }, + { requiredPermission: "read", section: "unit", module: "equipment" }, + ]), + equipmentType +); +router.use("/vehicle", PermissionHelper.passCheckMiddleware("read", "unit", "vehicle"), vehicle); +router.use( + "/vehicletype", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "vehicle_type" }, + { requiredPermission: "read", section: "unit", module: "vehicle" }, + ]), + vehicleType +); +router.use("/wearable", PermissionHelper.passCheckMiddleware("read", "unit", "wearable"), wearable); +router.use( + "/wearabletype", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "wearable_type" }, + { requiredPermission: "read", section: "unit", module: "wearable" }, + ]), + wearableType +); +router.use("/inspection", PermissionHelper.passCheckMiddleware("read", "unit", "inspection"), inspection); +router.use( + "/inspectionplan", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "inspection_plan" }, + { requiredPermission: "read", section: "unit", module: "inspection" }, + ]), + inspectionPlan +); + 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..e13bc7d --- /dev/null +++ b/src/routes/admin/unit/equipment.ts @@ -0,0 +1,10 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + res.send("TODO"); +}); + +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..e13bc7d --- /dev/null +++ b/src/routes/admin/unit/equipmentType.ts @@ -0,0 +1,10 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + res.send("TODO"); +}); + +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..e13bc7d --- /dev/null +++ b/src/routes/admin/unit/inspection.ts @@ -0,0 +1,10 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + res.send("TODO"); +}); + +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..e13bc7d --- /dev/null +++ b/src/routes/admin/unit/inspectionPlan.ts @@ -0,0 +1,10 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + res.send("TODO"); +}); + +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..e13bc7d --- /dev/null +++ b/src/routes/admin/unit/vehicle.ts @@ -0,0 +1,10 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + res.send("TODO"); +}); + +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..e13bc7d --- /dev/null +++ b/src/routes/admin/unit/vehicleType.ts @@ -0,0 +1,10 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + res.send("TODO"); +}); + +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..e13bc7d --- /dev/null +++ b/src/routes/admin/unit/wearable.ts @@ -0,0 +1,10 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + res.send("TODO"); +}); + +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..e13bc7d --- /dev/null +++ b/src/routes/admin/unit/wearableType.ts @@ -0,0 +1,10 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + res.send("TODO"); +}); + +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/type/permissionTypes.ts b/src/type/permissionTypes.ts index eff7fd1..d6ec391 100644 --- a/src/type/permissionTypes.ts +++ b/src/type/permissionTypes.ts @@ -1,27 +1,43 @@ -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" + // configuration | "qualification" | "award" | "executive_position" | "communication_type" | "membership_status" + | "newsletter_config" | "salutation" | "calendar_type" - | "user" - | "role" - | "webapi" - | "query" | "query_store" | "template" | "template_usage" | "backup" + // management + | "user" + | "role" + | "webapi" | "setting"; export type PermissionType = "read" | "create" | "update" | "delete"; @@ -56,34 +72,64 @@ 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", + // configuration "qualification", "award", "executive_position", "communication_type", "membership_status", + "newsletter_config", "salutation", "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", + ], configuration: [ "qualification", "award", From 9a1e7e74ca57f935379c20deeca1ffe51ce95088 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Thu, 29 May 2025 11:00:23 +0200 Subject: [PATCH 16/55] base controller files --- src/controller/admin/unit/equipmentController.ts | 0 src/controller/admin/unit/equipmentTypeController.ts | 0 src/controller/admin/unit/inspectionController.ts | 0 src/controller/admin/unit/inspectionPlanController.ts | 0 src/controller/admin/unit/vehicleController.ts | 0 src/controller/admin/unit/vehicleTypeController.ts | 0 src/controller/admin/unit/wearableController.ts | 0 src/controller/admin/unit/wearableTypeController.ts | 0 8 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/controller/admin/unit/equipmentController.ts create mode 100644 src/controller/admin/unit/equipmentTypeController.ts create mode 100644 src/controller/admin/unit/inspectionController.ts create mode 100644 src/controller/admin/unit/inspectionPlanController.ts create mode 100644 src/controller/admin/unit/vehicleController.ts create mode 100644 src/controller/admin/unit/vehicleTypeController.ts create mode 100644 src/controller/admin/unit/wearableController.ts create mode 100644 src/controller/admin/unit/wearableTypeController.ts diff --git a/src/controller/admin/unit/equipmentController.ts b/src/controller/admin/unit/equipmentController.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/controller/admin/unit/equipmentTypeController.ts b/src/controller/admin/unit/equipmentTypeController.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/controller/admin/unit/inspectionPlanController.ts b/src/controller/admin/unit/inspectionPlanController.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/controller/admin/unit/vehicleController.ts b/src/controller/admin/unit/vehicleController.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/controller/admin/unit/vehicleTypeController.ts b/src/controller/admin/unit/vehicleTypeController.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/controller/admin/unit/wearableController.ts b/src/controller/admin/unit/wearableController.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/controller/admin/unit/wearableTypeController.ts b/src/controller/admin/unit/wearableTypeController.ts new file mode 100644 index 0000000..e69de29 From 9f2a08ccc974f919f8526f955f4b867878c1c812 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 31 May 2025 15:12:16 +0200 Subject: [PATCH 17/55] define routes --- .../admin/unit/equipmentController.ts | 104 +++++++++++++++++ .../admin/unit/equipmentTypeController.ts | 101 +++++++++++++++++ .../admin/unit/inspectionController.ts | 106 ++++++++++++++++++ .../admin/unit/inspectionPlanController.ts | 106 ++++++++++++++++++ .../admin/unit/vehicleController.ts | 104 +++++++++++++++++ .../admin/unit/vehicleTypeController.ts | 101 +++++++++++++++++ .../admin/unit/wearableController.ts | 104 +++++++++++++++++ .../admin/unit/wearableTypeController.ts | 101 +++++++++++++++++ src/routes/admin/unit/equipment.ts | 37 +++++- src/routes/admin/unit/equipmentType.ts | 37 +++++- src/routes/admin/unit/inspection.ts | 45 +++++++- src/routes/admin/unit/inspectionPlan.ts | 45 +++++++- src/routes/admin/unit/vehicle.ts | 37 +++++- src/routes/admin/unit/vehicleType.ts | 37 +++++- src/routes/admin/unit/wearable.ts | 37 +++++- src/routes/admin/unit/wearableType.ts | 37 +++++- 16 files changed, 1129 insertions(+), 10 deletions(-) diff --git a/src/controller/admin/unit/equipmentController.ts b/src/controller/admin/unit/equipmentController.ts index e69de29..8546df7 100644 --- a/src/controller/admin/unit/equipmentController.ts +++ b/src/controller/admin/unit/equipmentController.ts @@ -0,0 +1,104 @@ +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); + + //{ offset, count, search, noLimit, ids } + let [equipments, total] = await EquipmentService.getAll(); + + res.json({ + equipments: 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 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 salutationId = parseInt(req.body.salutationId); + + let createEquipment: CreateEquipmentCommand = { + name: "", + location: "", + commissioned: undefined, + 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 salutationId = parseInt(req.body.salutationId); + + let updateEquipment: UpdateEquipmentCommand = { + id: equipmentId, + name: "", + location: "", + commissioned: undefined, + }; + 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 index e69de29..27e387f 100644 --- a/src/controller/admin/unit/equipmentTypeController.ts +++ 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 ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); + + //{ offset, count, search, noLimit, ids } + let [equipmentTypes, total] = await EquipmentTypeService.getAll(); + + res.json({ + equipmentTypes: 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 salutationId = parseInt(req.body.salutationId); + + 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 salutationId = parseInt(req.body.salutationId); + + 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 index e69de29..c1f183b 100644 --- a/src/controller/admin/unit/inspectionController.ts +++ b/src/controller/admin/unit/inspectionController.ts @@ -0,0 +1,106 @@ +import { Request, Response } from "express"; +import InspectionService from "../../../service/unit/inspection/inspectionService"; +import InspectionFactory from "../../../factory/admin/unit/inspection/inspection"; +import { + CreateInspectionCommand, + DeleteInspectionCommand, + UpdateInspectionCommand, +} from "../../../command/unit/inspection/inspectionCommand"; +import InspectionCommandHandler from "../../../command/unit/inspection/inspectionCommandHandler"; + +/** + * @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"; + 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 search = (req.query.search as string) ?? ""; + let noLimit = req.query.noLimit === "true"; + let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); + + //{ offset, count, search, noLimit, ids } + let [inspections, total] = await InspectionService.getAllForRelated({ equipmentId: relationId }); + + res.json({ + inspections: 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 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 salutationId = parseInt(req.body.salutationId); + + let createInspection: CreateInspectionCommand = { + context: "", + inspectionPlanId: "", + relatedId: "", + assigned: "equipment", + }; + 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 salutationId = parseInt(req.body.salutationId); + + let updateInspection: UpdateInspectionCommand = { + id: inspectionId, + context: "", + }; + await InspectionCommandHandler.update(updateInspection); + + 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; + + // TODO finished inspection cannot be deleted + + 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 index e69de29..4b60588 100644 --- a/src/controller/admin/unit/inspectionPlanController.ts +++ b/src/controller/admin/unit/inspectionPlanController.ts @@ -0,0 +1,106 @@ +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"; + +/** + * @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 "vehicle" | "equipment"; + 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 search = (req.query.search as string) ?? ""; + let noLimit = req.query.noLimit === "true"; + let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); + + //{ offset, count, search, noLimit, ids } + let [inspectionPlans, total] = await InspectionPlanService.getAllForRelated({ equipmentId: relationId }); + + res.json({ + inspectionPlans: inspectionPlans, + total: total, + offset: offset, + count: count, + }); +} + +/** + * @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 salutationId = parseInt(req.body.salutationId); + + let createInspectionPlan: CreateInspectionPlanCommand = { + title: "", + inspectionInterval: "1-m", + remindTime: "1-m", + relatedId: "", + assigned: "equipment", + }; + 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 salutationId = parseInt(req.body.salutationId); + + let updateInspectionPlan: UpdateInspectionPlanCommand = { + id: inspectionPlanId, + title: "", + inspectionInterval: "1-m", + }; + await InspectionPlanCommandHandler.update(updateInspectionPlan); + + 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/vehicleController.ts b/src/controller/admin/unit/vehicleController.ts index e69de29..55c1902 100644 --- a/src/controller/admin/unit/vehicleController.ts +++ b/src/controller/admin/unit/vehicleController.ts @@ -0,0 +1,104 @@ +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); + + //{ offset, count, search, noLimit, ids } + let [vehicles, total] = await VehicleService.getAll(); + + res.json({ + vehicles: 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 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 salutationId = parseInt(req.body.salutationId); + + let createVehicle: CreateVehicleCommand = { + name: "", + location: "", + commissioned: undefined, + 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 salutationId = parseInt(req.body.salutationId); + + let updateVehicle: UpdateVehicleCommand = { + id: vehicleId, + name: "", + location: "", + commissioned: undefined, + }; + 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 index e69de29..c11d1f1 100644 --- a/src/controller/admin/unit/vehicleTypeController.ts +++ 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 ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); + + //{ offset, count, search, noLimit, ids } + let [vehicleTypes, total] = await VehicleTypeService.getAll(); + + res.json({ + vehicleTypes: 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 salutationId = parseInt(req.body.salutationId); + + 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 salutationId = parseInt(req.body.salutationId); + + 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 index e69de29..209eaa5 100644 --- a/src/controller/admin/unit/wearableController.ts +++ b/src/controller/admin/unit/wearableController.ts @@ -0,0 +1,104 @@ +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); + + //{ offset, count, search, noLimit, ids } + let [wearables, total] = await WearableService.getAll(); + + res.json({ + wearables: 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 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 salutationId = parseInt(req.body.salutationId); + + let createWearable: CreateWearableCommand = { + name: "", + location: "", + commissioned: undefined, + wearableTypeId: "", + }; + 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 salutationId = parseInt(req.body.salutationId); + + let updateWearable: UpdateWearableCommand = { + id: wearableId, + name: "", + location: "", + commissioned: undefined, + }; + 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 index e69de29..fffc969 100644 --- a/src/controller/admin/unit/wearableTypeController.ts +++ 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 ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); + + //{ offset, count, search, noLimit, ids } + let [wearableTypes, total] = await WearableTypeService.getAll(); + + res.json({ + wearableTypes: 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 salutationId = parseInt(req.body.salutationId); + + 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 salutationId = parseInt(req.body.salutationId); + + 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/routes/admin/unit/equipment.ts b/src/routes/admin/unit/equipment.ts index e13bc7d..96f432c 100644 --- a/src/routes/admin/unit/equipment.ts +++ b/src/routes/admin/unit/equipment.ts @@ -1,10 +1,45 @@ import express, { Request, Response } from "express"; import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createEquipment, + deleteEquipmentById, + getAllEquipments, + getEquipmentById, + updateEquipmentById, +} from "../../../controller/admin/unit/equipmentController"; var router = express.Router({ mergeParams: true }); router.get("/", async (req: Request, res: Response) => { - res.send("TODO"); + await getAllEquipments(req, res); }); +router.get("/:id", async (req: Request, res: Response) => { + await getEquipmentById(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 index e13bc7d..e197e34 100644 --- a/src/routes/admin/unit/equipmentType.ts +++ b/src/routes/admin/unit/equipmentType.ts @@ -1,10 +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) => { - res.send("TODO"); + 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 index e13bc7d..8c7a63d 100644 --- a/src/routes/admin/unit/inspection.ts +++ b/src/routes/admin/unit/inspection.ts @@ -1,10 +1,51 @@ import express, { Request, Response } from "express"; import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createInspection, + deleteInspectionById, + getAllInspectionsForRelated, + getInspectionById, + updateInspectionById, +} from "../../../controller/admin/unit/inspectionController"; var router = express.Router({ mergeParams: true }); -router.get("/", async (req: Request, res: Response) => { - res.send("TODO"); +router.get(["/vehicle/:relatedId", "/equipment/:relatedId"], async (req: Request, res: Response) => { + if (req.path.startsWith("/vehicle")) { + req.params.related = "vehicle"; + } else { + req.params.related = "equipment"; + } + + await getAllInspectionsForRelated(req, res); }); +router.get("/:id", async (req: Request, res: Response) => { + await getInspectionById(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.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 index e13bc7d..0e33d5c 100644 --- a/src/routes/admin/unit/inspectionPlan.ts +++ b/src/routes/admin/unit/inspectionPlan.ts @@ -1,10 +1,51 @@ import express, { Request, Response } from "express"; import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createInspectionPlan, + deleteInspectionPlanById, + getAllInspectionPlansForRelated, + getInspectionPlanById, + updateInspectionPlanById, +} from "../../../controller/admin/unit/inspectionPlanController"; var router = express.Router({ mergeParams: true }); -router.get("/", async (req: Request, res: Response) => { - res.send("TODO"); +router.get(["/vehicle/:relatedId", "/equipment/:relatedId"], async (req: Request, res: Response) => { + if (req.path.startsWith("/vehicle")) { + req.params.related = "vehicle"; + } else { + req.params.related = "equipment"; + } + + 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.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/vehicle.ts b/src/routes/admin/unit/vehicle.ts index e13bc7d..832e248 100644 --- a/src/routes/admin/unit/vehicle.ts +++ b/src/routes/admin/unit/vehicle.ts @@ -1,10 +1,45 @@ import express, { Request, Response } from "express"; import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createVehicle, + deleteVehicleById, + getAllVehicles, + getVehicleById, + updateVehicleById, +} from "../../../controller/admin/unit/vehicleController"; var router = express.Router({ mergeParams: true }); router.get("/", async (req: Request, res: Response) => { - res.send("TODO"); + await getAllVehicles(req, res); }); +router.get("/:id", async (req: Request, res: Response) => { + await getVehicleById(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 index e13bc7d..03f2989 100644 --- a/src/routes/admin/unit/vehicleType.ts +++ b/src/routes/admin/unit/vehicleType.ts @@ -1,10 +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) => { - res.send("TODO"); + 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 index e13bc7d..c25226a 100644 --- a/src/routes/admin/unit/wearable.ts +++ b/src/routes/admin/unit/wearable.ts @@ -1,10 +1,45 @@ import express, { Request, Response } from "express"; import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createWearable, + deleteWearableById, + getAllWearables, + getWearableById, + updateWearableById, +} from "../../../controller/admin/unit/wearableController"; var router = express.Router({ mergeParams: true }); router.get("/", async (req: Request, res: Response) => { - res.send("TODO"); + await getAllWearables(req, res); }); +router.get("/:id", async (req: Request, res: Response) => { + await getWearableById(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 index e13bc7d..b61584f 100644 --- a/src/routes/admin/unit/wearableType.ts +++ b/src/routes/admin/unit/wearableType.ts @@ -1,10 +1,45 @@ import express, { Request, Response } from "express"; import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createWearableType, + getAllWearableTypes, + getWearableTypeById, + updateWearableTypeById, +} from "../../../controller/admin/unit/wearableTypeController"; +import { deleteWearableById } from "../../../controller/admin/unit/wearableController"; var router = express.Router({ mergeParams: true }); router.get("/", async (req: Request, res: Response) => { - res.send("TODO"); + 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 deleteWearableById(req, res); + } +); + export default router; From 2609ecc1bf3f38ef5375ec38ab7349d6ff4bb573 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Mon, 2 Jun 2025 13:14:09 +0200 Subject: [PATCH 18/55] controller --- .../admin/unit/equipmentController.ts | 22 +++++- .../admin/unit/equipmentTypeController.ts | 3 +- .../admin/unit/inspectionController.ts | 6 +- .../admin/unit/inspectionPlanController.ts | 10 ++- .../admin/unit/vehicleController.ts | 3 +- .../admin/unit/vehicleTypeController.ts | 3 +- .../admin/unit/wearableController.ts | 3 +- .../admin/unit/wearableTypeController.ts | 3 +- src/routes/admin/unit/equipment.ts | 5 ++ .../unit/equipment/equipmentService.ts | 41 +++++++++-- .../unit/equipment/equipmentTypeService.ts | 39 +++++++++-- .../unit/inspection/inspectionPlanService.ts | 69 ++++++++++++------- .../unit/inspection/inspectionService.ts | 48 ++++++++----- src/service/unit/vehicle/vehicleService.ts | 41 +++++++++-- .../unit/vehicle/vehicleTypeService.ts | 39 +++++++++-- src/service/unit/wearable/wearableService.ts | 41 +++++++++-- .../unit/wearable/wearableTypeService.ts | 39 +++++++++-- 17 files changed, 320 insertions(+), 95 deletions(-) diff --git a/src/controller/admin/unit/equipmentController.ts b/src/controller/admin/unit/equipmentController.ts index 8546df7..8d7e200 100644 --- a/src/controller/admin/unit/equipmentController.ts +++ b/src/controller/admin/unit/equipmentController.ts @@ -21,8 +21,7 @@ export async function getAllEquipments(req: Request, res: Response): Promise i); - //{ offset, count, search, noLimit, ids } - let [equipments, total] = await EquipmentService.getAll(); + let [equipments, total] = await EquipmentService.getAll({ offset, count, search, noLimit, ids }); res.json({ equipments: equipments, @@ -45,6 +44,25 @@ export async function getEquipmentById(req: Request, res: Response): 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 diff --git a/src/controller/admin/unit/equipmentTypeController.ts b/src/controller/admin/unit/equipmentTypeController.ts index 27e387f..dce7b77 100644 --- a/src/controller/admin/unit/equipmentTypeController.ts +++ b/src/controller/admin/unit/equipmentTypeController.ts @@ -21,8 +21,7 @@ export async function getAllEquipmentTypes(req: Request, res: Response): Promise let noLimit = req.query.noLimit === "true"; let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); - //{ offset, count, search, noLimit, ids } - let [equipmentTypes, total] = await EquipmentTypeService.getAll(); + let [equipmentTypes, total] = await EquipmentTypeService.getAll({ offset, count, search, noLimit, ids }); res.json({ equipmentTypes: equipmentTypes, diff --git a/src/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts index c1f183b..8c4b782 100644 --- a/src/controller/admin/unit/inspectionController.ts +++ b/src/controller/admin/unit/inspectionController.ts @@ -19,12 +19,10 @@ export async function getAllInspectionsForRelated(req: Request, res: Response): 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 search = (req.query.search as string) ?? ""; let noLimit = req.query.noLimit === "true"; - let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); - //{ offset, count, search, noLimit, ids } - let [inspections, total] = await InspectionService.getAllForRelated({ equipmentId: relationId }); + let where = relation === "equipment" ? { equipmentId: relationId } : { vehicleId: relationId }; + let [inspections, total] = await InspectionService.getAllForRelated(where, { offset, count, noLimit }); res.json({ inspections: inspections, diff --git a/src/controller/admin/unit/inspectionPlanController.ts b/src/controller/admin/unit/inspectionPlanController.ts index 4b60588..358ac66 100644 --- a/src/controller/admin/unit/inspectionPlanController.ts +++ b/src/controller/admin/unit/inspectionPlanController.ts @@ -23,8 +23,14 @@ export async function getAllInspectionPlansForRelated(req: Request, res: Respons let noLimit = req.query.noLimit === "true"; let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); - //{ offset, count, search, noLimit, ids } - let [inspectionPlans, total] = await InspectionPlanService.getAllForRelated({ equipmentId: relationId }); + let where = relation === "equipment" ? { equipmentId: relationId } : { vehicleId: relationId }; + let [inspectionPlans, total] = await InspectionPlanService.getAllForRelated(where, { + offset, + count, + search, + noLimit, + ids, + }); res.json({ inspectionPlans: inspectionPlans, diff --git a/src/controller/admin/unit/vehicleController.ts b/src/controller/admin/unit/vehicleController.ts index 55c1902..02b3606 100644 --- a/src/controller/admin/unit/vehicleController.ts +++ b/src/controller/admin/unit/vehicleController.ts @@ -21,8 +21,7 @@ export async function getAllVehicles(req: Request, res: Response): Promise let noLimit = req.query.noLimit === "true"; let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); - //{ offset, count, search, noLimit, ids } - let [vehicles, total] = await VehicleService.getAll(); + let [vehicles, total] = await VehicleService.getAll({ offset, count, search, noLimit, ids }); res.json({ vehicles: vehicles, diff --git a/src/controller/admin/unit/vehicleTypeController.ts b/src/controller/admin/unit/vehicleTypeController.ts index c11d1f1..7cf5552 100644 --- a/src/controller/admin/unit/vehicleTypeController.ts +++ b/src/controller/admin/unit/vehicleTypeController.ts @@ -21,8 +21,7 @@ export async function getAllVehicleTypes(req: Request, res: Response): Promise i); - //{ offset, count, search, noLimit, ids } - let [vehicleTypes, total] = await VehicleTypeService.getAll(); + let [vehicleTypes, total] = await VehicleTypeService.getAll({ offset, count, search, noLimit, ids }); res.json({ vehicleTypes: vehicleTypes, diff --git a/src/controller/admin/unit/wearableController.ts b/src/controller/admin/unit/wearableController.ts index 209eaa5..7775c36 100644 --- a/src/controller/admin/unit/wearableController.ts +++ b/src/controller/admin/unit/wearableController.ts @@ -21,8 +21,7 @@ export async function getAllWearables(req: Request, res: Response): Promise let noLimit = req.query.noLimit === "true"; let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); - //{ offset, count, search, noLimit, ids } - let [wearables, total] = await WearableService.getAll(); + let [wearables, total] = await WearableService.getAll({ offset, count, search, noLimit, ids }); res.json({ wearables: wearables, diff --git a/src/controller/admin/unit/wearableTypeController.ts b/src/controller/admin/unit/wearableTypeController.ts index fffc969..40def8b 100644 --- a/src/controller/admin/unit/wearableTypeController.ts +++ b/src/controller/admin/unit/wearableTypeController.ts @@ -21,8 +21,7 @@ export async function getAllWearableTypes(req: Request, res: Response): Promise< let noLimit = req.query.noLimit === "true"; let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); - //{ offset, count, search, noLimit, ids } - let [wearableTypes, total] = await WearableTypeService.getAll(); + let [wearableTypes, total] = await WearableTypeService.getAll({ offset, count, search, noLimit, ids }); res.json({ wearableTypes: wearableTypes, diff --git a/src/routes/admin/unit/equipment.ts b/src/routes/admin/unit/equipment.ts index 96f432c..4e45a52 100644 --- a/src/routes/admin/unit/equipment.ts +++ b/src/routes/admin/unit/equipment.ts @@ -5,6 +5,7 @@ import { deleteEquipmentById, getAllEquipments, getEquipmentById, + getEquipmentsByIds, updateEquipmentById, } from "../../../controller/admin/unit/equipmentController"; @@ -18,6 +19,10 @@ 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"), diff --git a/src/service/unit/equipment/equipmentService.ts b/src/service/unit/equipment/equipmentService.ts index a2e1823..7e2267b 100644 --- a/src/service/unit/equipment/equipmentService.ts +++ b/src/service/unit/equipment/equipmentService.ts @@ -1,3 +1,4 @@ +import { In, Like } from "typeorm"; import { dataSource } from "../../../data-source"; import { equipment } from "../../../entity/unit/equipment/equipment"; import DatabaseActionException from "../../../exceptions/databaseActionException"; @@ -5,15 +6,45 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class EquipmentService { /** * @description get all equipment - * @returns {Promise>} + * @returns {Promise<[Array, number]>} */ - static async getAll(): Promise> { - return await dataSource + 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") + .leftJoinAndSelect("equipment.equipmentType", "equipmenttype"); + + if (search != "") { + query = query.where({ + code: Like(search), + name: Like(search), + 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") - .getMany() + .getManyAndCount() .then((res) => { return res; }) diff --git a/src/service/unit/equipment/equipmentTypeService.ts b/src/service/unit/equipment/equipmentTypeService.ts index 237b0cd..8789fe7 100644 --- a/src/service/unit/equipment/equipmentTypeService.ts +++ b/src/service/unit/equipment/equipmentTypeService.ts @@ -1,3 +1,4 @@ +import { Like, In } from "typeorm"; import { dataSource } from "../../../data-source"; import { equipmentType } from "../../../entity/unit/equipment/equipmentType"; import DatabaseActionException from "../../../exceptions/databaseActionException"; @@ -5,14 +6,40 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class EquipmentTypeService { /** * @description get all equipmentTypes - * @returns {Promise>} + * @returns {Promise<[Array, number]>} */ - static async getAll(): Promise> { - return await dataSource - .getRepository(equipmentType) - .createQueryBuilder("equipmentType") + 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(equipmentType).createQueryBuilder("equipmentType"); + + if (search != "") { + query = query.where({ + type: Like(search), + }); + } + + if (ids.length != 0) { + query = query.where({ id: In(ids) }); + } + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query .orderBy("type", "ASC") - .getMany() + .getManyAndCount() .then((res) => { return res; }) diff --git a/src/service/unit/inspection/inspectionPlanService.ts b/src/service/unit/inspection/inspectionPlanService.ts index e9495fe..2514ad1 100644 --- a/src/service/unit/inspection/inspectionPlanService.ts +++ b/src/service/unit/inspection/inspectionPlanService.ts @@ -1,17 +1,12 @@ +import { Like, In } from "typeorm"; import { dataSource } from "../../../data-source"; import { inspectionPlan } from "../../../entity/unit/inspection/inspectionPlan"; import { DB_TYPE } from "../../../env.defaults"; import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class InspectionPlanService { - /** - * @description get all inspectionPlans for related - * @returns {Promise>} - */ - static async getAllForRelated( - where: { equipmentId: string } | { vehicleId: string } - ): Promise> { - return await dataSource + private static query = () => + dataSource .getRepository(inspectionPlan) .createQueryBuilder("inspectionPlan") .leftJoinAndMapOne( @@ -24,10 +19,47 @@ export default abstract class InspectionPlanService { ) .leftJoinAndSelect("latestVersionedPlan.inspectionPoints", "inspectionPoints") .leftJoinAndSelect("inspectionPlan.equipment", "equipment") - .leftJoinAndSelect("inspectionPlan.vehicle", "vehicle") - .where(where) + .leftJoinAndSelect("inspectionPlan.vehicle", "vehicle"); + + /** + * @description get all inspectionPlans for related + * @returns {Promise<[Array, number]>} + */ + static async getAllForRelated( + where: { equipmentId: string } | { vehicleId: 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.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("title", "ASC") - .getMany() + .getManyAndCount() .then((res) => { return res; }) @@ -41,20 +73,7 @@ export default abstract class InspectionPlanService { * @returns {Promise} */ static async getById(id: string): Promise { - return await dataSource - .getRepository(inspectionPlan) - .createQueryBuilder("inspectionPlan") - .leftJoinAndMapOne( - "inspectionPlan.latestVersionedPlan", - "inspectionPlan.versionedPlans", - "latestVersionedPlan", - DB_TYPE == "postgres" - ? 'latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX("ivp"."start") FROM "inspection_versioned_plan" "ivp" WHERE "ivp"."inspectionPlanId" = "inspectionPlan"."id")' - : "latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX(ivp.start) FROM inspection_versioned_plan ivp WHERE ivp.inspectionPlanId = inspectionPlan.id)" - ) - .leftJoinAndSelect("latestVersionedPlan.inspectionPoints", "inspectionPoints") - .leftJoinAndSelect("inspectionPlan.equipment", "equipment") - .leftJoinAndSelect("inspectionPlan.vehicle", "vehicle") + return await this.query() .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts index 0fdf490..709b335 100644 --- a/src/service/unit/inspection/inspectionService.ts +++ b/src/service/unit/inspection/inspectionService.ts @@ -3,12 +3,8 @@ import { inspection } from "../../../entity/unit/inspection/inspection"; import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class InspectionService { - /** - * @description get all inspections for related - * @returns {Promise>} - */ - static async getAllForRelated(where: { equipmentId: string } | { vehicleId: string }): Promise> { - return await dataSource + private static query = () => + dataSource .getRepository(inspection) .createQueryBuilder("inspection") .leftJoinAndSelect("inspection.inspectionPlan", "inspectionPlan") @@ -17,10 +13,33 @@ export default abstract class InspectionService { .leftJoinAndSelect("inspection.pointResults", "pointResults") .leftJoinAndSelect("pointResults.inspectionPoint", "inspectionPoint") .leftJoinAndSelect("inspection.equipment", "equipment") - .leftJoinAndSelect("inspection.vehicle", "vehicle") - .where(where) + .leftJoinAndSelect("inspection.vehicle", "vehicle"); + + /** + * @description get all inspections for related + * @returns {Promise>} + */ + static async getAllForRelated( + where: { equipmentId: string } | { vehicleId: 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("createdAt", "DESC") - .getMany() + .getManyAndCount() .then((res) => { return res; }) @@ -34,16 +53,7 @@ export default abstract class InspectionService { * @returns {Promise} */ static async getById(id: string): Promise { - return await dataSource - .getRepository(inspection) - .createQueryBuilder("inspection") - .leftJoinAndSelect("inspection.inspectionPlan", "inspectionPlan") - .leftJoinAndSelect("inspection.inspectionVersionedPlan", "inspectionVersionedPlan") - .leftJoinAndSelect("inspectionVersionedPlan.inspectionPoints", "inspectionPoints") - .leftJoinAndSelect("inspection.pointResults", "pointResults") - .leftJoinAndSelect("pointResults.inspectionPoint", "inspectionPoint") - .leftJoinAndSelect("inspection.equipment", "equipment") - .leftJoinAndSelect("inspection.vehicle", "vehicle") + return await this.query() .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/vehicle/vehicleService.ts b/src/service/unit/vehicle/vehicleService.ts index 39d7b06..921a981 100644 --- a/src/service/unit/vehicle/vehicleService.ts +++ b/src/service/unit/vehicle/vehicleService.ts @@ -1,3 +1,4 @@ +import { Like, In } from "typeorm"; import { dataSource } from "../../../data-source"; import { vehicle } from "../../../entity/unit/vehicle/vehicle"; import DatabaseActionException from "../../../exceptions/databaseActionException"; @@ -5,15 +6,45 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class VehicleService { /** * @description get all vehicles - * @returns {Promise>} + * @returns {Promise<[Array, number]>} */ - static async getAll(): Promise> { - return await dataSource + 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") + .leftJoinAndSelect("vehicle.vehicleType", "vehicletype"); + + if (search != "") { + query = query.where({ + code: Like(search), + name: Like(search), + 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") - .getMany() + .getManyAndCount() .then((res) => { return res; }) diff --git a/src/service/unit/vehicle/vehicleTypeService.ts b/src/service/unit/vehicle/vehicleTypeService.ts index 8ba8104..6ecb703 100644 --- a/src/service/unit/vehicle/vehicleTypeService.ts +++ b/src/service/unit/vehicle/vehicleTypeService.ts @@ -1,3 +1,4 @@ +import { Like, In } from "typeorm"; import { dataSource } from "../../../data-source"; import { vehicleType } from "../../../entity/unit/vehicle/vehicleType"; import DatabaseActionException from "../../../exceptions/databaseActionException"; @@ -5,14 +6,40 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class VehicleTypeService { /** * @description get all vehicleTypes - * @returns {Promise>} + * @returns {Promise<[Array, number]>} */ - static async getAll(): Promise> { - return await dataSource - .getRepository(vehicleType) - .createQueryBuilder("vehicleType") + 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(vehicleType).createQueryBuilder("vehicleType"); + + if (search != "") { + query = query.where({ + type: Like(search), + }); + } + + if (ids.length != 0) { + query = query.where({ id: In(ids) }); + } + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query .orderBy("type", "ASC") - .getMany() + .getManyAndCount() .then((res) => { return res; }) diff --git a/src/service/unit/wearable/wearableService.ts b/src/service/unit/wearable/wearableService.ts index 7733d4a..b369f47 100644 --- a/src/service/unit/wearable/wearableService.ts +++ b/src/service/unit/wearable/wearableService.ts @@ -1,3 +1,4 @@ +import { Like, In } from "typeorm"; import { dataSource } from "../../../data-source"; import { wearable } from "../../../entity/unit/wearable/wearable"; import DatabaseActionException from "../../../exceptions/databaseActionException"; @@ -5,16 +6,46 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class WearableService { /** * @description get all wearables - * @returns {Promise>} + * @returns {Promise<[Array, number]>} */ - static async getAll(): Promise> { - return await dataSource + 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") + .leftJoinAndSelect("wearable.wearer", "wearer"); + + if (search != "") { + query = query.where({ + code: Like(search), + name: Like(search), + 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") - .getMany() + .getManyAndCount() .then((res) => { return res; }) diff --git a/src/service/unit/wearable/wearableTypeService.ts b/src/service/unit/wearable/wearableTypeService.ts index 63a696b..d6f167f 100644 --- a/src/service/unit/wearable/wearableTypeService.ts +++ b/src/service/unit/wearable/wearableTypeService.ts @@ -1,3 +1,4 @@ +import { In, Like } from "typeorm"; import { dataSource } from "../../../data-source"; import { wearableType } from "../../../entity/unit/wearable/wearableType"; import DatabaseActionException from "../../../exceptions/databaseActionException"; @@ -5,14 +6,40 @@ import DatabaseActionException from "../../../exceptions/databaseActionException export default abstract class WearableTypeService { /** * @description get all wearableTypes - * @returns {Promise>} + * @returns {Promise<[Array, number]>} */ - static async getAll(): Promise> { - return await dataSource - .getRepository(wearableType) - .createQueryBuilder("wearableType") + 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(wearableType).createQueryBuilder("wearableType"); + + if (search != "") { + query = query.where({ + type: Like(search), + }); + } + + if (ids.length != 0) { + query = query.where({ id: In(ids) }); + } + + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query .orderBy("type", "ASC") - .getMany() + .getManyAndCount() .then((res) => { return res; }) From e3db523a0e314a90a3832a045ead8a3d30cf7221 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 4 Jun 2025 14:30:57 +0200 Subject: [PATCH 19/55] change according to connection from frontend --- .../inspectionPlanCommandHandler.ts | 4 +- .../admin/unit/damageReportController.ts | 113 ++++++++++++++++++ .../admin/unit/equipmentController.ts | 31 +++-- .../admin/unit/equipmentTypeController.ts | 19 +-- .../admin/unit/inspectionController.ts | 27 +++-- .../admin/unit/inspectionPlanController.ts | 67 +++++++++-- .../admin/unit/vehicleController.ts | 50 ++++++-- .../admin/unit/vehicleTypeController.ts | 19 +-- .../admin/unit/wearableController.ts | 54 +++++++-- .../admin/unit/wearableTypeController.ts | 19 +-- src/entity/unit/equipment/equipment.ts | 4 +- src/entity/unit/equipment/equipmentType.ts | 2 +- src/entity/unit/inspection/inspectionPlan.ts | 16 +-- src/entity/unit/vehicle/vehicle.ts | 4 +- src/entity/unit/vehicle/vehicleType.ts | 2 +- src/entity/unit/wearable/wearable.ts | 4 +- src/factory/admin/club/member/member.ts | 2 +- .../admin/unit/inspection/inspectionPlan.ts | 18 +-- src/helpers/typeTester.ts | 18 +++ src/migrations/baseSchemaTables/inspection.ts | 12 +- src/routes/admin/index.ts | 11 ++ src/routes/admin/unit/damageReport.ts | 50 ++++++++ src/routes/admin/unit/inspectionPlan.ts | 5 + src/routes/admin/unit/vehicle.ts | 5 + src/routes/admin/unit/wearable.ts | 5 + src/routes/admin/unit/wearableType.ts | 4 +- src/service/unit/damageReportService.ts | 79 +++++++++--- .../unit/equipment/equipmentService.ts | 15 ++- .../unit/equipment/equipmentTypeService.ts | 8 +- .../unit/inspection/inspectionPlanService.ts | 62 ++++++++-- .../unit/inspection/inspectionService.ts | 2 +- src/service/unit/vehicle/vehicleService.ts | 15 ++- .../unit/vehicle/vehicleTypeService.ts | 8 +- src/service/unit/wearable/wearableService.ts | 16 ++- .../unit/wearable/wearableTypeService.ts | 8 +- .../unit/inspection/inspectionPlan.models.ts | 6 +- 36 files changed, 611 insertions(+), 173 deletions(-) create mode 100644 src/controller/admin/unit/damageReportController.ts create mode 100644 src/helpers/typeTester.ts create mode 100644 src/routes/admin/unit/damageReport.ts diff --git a/src/command/unit/inspection/inspectionPlanCommandHandler.ts b/src/command/unit/inspection/inspectionPlanCommandHandler.ts index cf48ef7..179f8a7 100644 --- a/src/command/unit/inspection/inspectionPlanCommandHandler.ts +++ b/src/command/unit/inspection/inspectionPlanCommandHandler.ts @@ -22,8 +22,8 @@ export default abstract class InspectionPlanCommandHandler { title: createInspectionPlan.title, inspectionInterval: createInspectionPlan.inspectionInterval, remindTime: createInspectionPlan.remindTime, - equipmentId: createInspectionPlan.assigned == "equipment" ? createInspectionPlan.relatedId : null, - vehicleId: createInspectionPlan.assigned == "vehicle" ? createInspectionPlan.relatedId : null, + equipmentTypeId: createInspectionPlan.assigned == "equipment" ? createInspectionPlan.relatedId : null, + vehicleTypeId: createInspectionPlan.assigned == "vehicle" ? createInspectionPlan.relatedId : null, }) .execute() .then((result) => { diff --git a/src/controller/admin/unit/damageReportController.ts b/src/controller/admin/unit/damageReportController.ts new file mode 100644 index 0000000..9576ec9 --- /dev/null +++ b/src/controller/admin/unit/damageReportController.ts @@ -0,0 +1,113 @@ +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"; + 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 = relation === "equipment" ? { equipmentId: relationId } : { vehicleId: 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 index 8d7e200..caee218 100644 --- a/src/controller/admin/unit/equipmentController.ts +++ b/src/controller/admin/unit/equipmentController.ts @@ -24,7 +24,7 @@ export async function getAllEquipments(req: Request, res: Response): Promise} */ export async function createEquipment(req: Request, res: Response): Promise { - const salutationId = parseInt(req.body.salutationId); + 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 = { - name: "", - location: "", - commissioned: undefined, - equipmentTypeId: "", + code, + name, + location, + commissioned, + equipmentTypeId, }; let equipmentId = await EquipmentCommandHandler.create(createEquipment); @@ -91,13 +96,19 @@ export async function createEquipment(req: Request, res: Response): Promise */ export async function updateEquipmentById(req: Request, res: Response): Promise { const equipmentId = req.params.id; - const salutationId = parseInt(req.body.salutationId); + 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: "", - location: "", - commissioned: undefined, + name, + code, + location, + commissioned, + decommissioned, }; await EquipmentCommandHandler.update(updateEquipment); diff --git a/src/controller/admin/unit/equipmentTypeController.ts b/src/controller/admin/unit/equipmentTypeController.ts index dce7b77..cfae069 100644 --- a/src/controller/admin/unit/equipmentTypeController.ts +++ b/src/controller/admin/unit/equipmentTypeController.ts @@ -19,12 +19,11 @@ export async function getAllEquipmentTypes(req: Request, res: Response): Promise 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 [equipmentTypes, total] = await EquipmentTypeService.getAll({ offset, count, search, noLimit, ids }); + let [equipmentTypes, total] = await EquipmentTypeService.getAll({ offset, count, search, noLimit }); res.json({ - equipmentTypes: equipmentTypes, + equipmentTypes: EquipmentTypeFactory.mapToBase(equipmentTypes), total: total, offset: offset, count: count, @@ -51,11 +50,12 @@ export async function getEquipmentTypeById(req: Request, res: Response): Promise * @returns {Promise<*>} */ export async function createEquipmentType(req: Request, res: Response): Promise { - const salutationId = parseInt(req.body.salutationId); + const type = req.body.type; + const description = req.body.description; let createEquipmentType: CreateEquipmentTypeCommand = { - type: "", - description: "", + type, + description, }; let equipmentTypeId = await EquipmentTypeCommandHandler.create(createEquipmentType); @@ -70,12 +70,13 @@ export async function createEquipmentType(req: Request, res: Response): Promise< */ export async function updateEquipmentTypeById(req: Request, res: Response): Promise { const equipmentTypeId = req.params.id; - const salutationId = parseInt(req.body.salutationId); + const type = req.body.type; + const description = req.body.description; let updateEquipmentType: UpdateEquipmentTypeCommand = { id: equipmentTypeId, - type: "", - description: "", + type, + description, }; await EquipmentTypeCommandHandler.update(updateEquipmentType); diff --git a/src/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts index 8c4b782..2380bdf 100644 --- a/src/controller/admin/unit/inspectionController.ts +++ b/src/controller/admin/unit/inspectionController.ts @@ -7,6 +7,7 @@ import { UpdateInspectionCommand, } from "../../../command/unit/inspection/inspectionCommand"; import InspectionCommandHandler from "../../../command/unit/inspection/inspectionCommandHandler"; +import BadRequestException from "../../../exceptions/badRequestException"; /** * @description get all inspections for related id @@ -25,7 +26,7 @@ export async function getAllInspectionsForRelated(req: Request, res: Response): let [inspections, total] = await InspectionService.getAllForRelated(where, { offset, count, noLimit }); res.json({ - inspections: inspections, + inspections: InspectionFactory.mapToBase(inspections), total: total, offset: offset, count: count, @@ -52,13 +53,21 @@ export async function getInspectionById(req: Request, res: Response): Promise} */ export async function createInspection(req: Request, res: Response): Promise { - const salutationId = parseInt(req.body.salutationId); + 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") + throw new BadRequestException("set assigned to equipment or vehicle"); let createInspection: CreateInspectionCommand = { - context: "", - inspectionPlanId: "", - relatedId: "", - assigned: "equipment", + context, + nextInspection, + inspectionPlanId, + relatedId, + assigned, }; let inspectionId = await InspectionCommandHandler.create(createInspection); @@ -73,11 +82,13 @@ export async function createInspection(req: Request, res: Response): Promise { const inspectionId = req.params.id; - const salutationId = parseInt(req.body.salutationId); + const context = req.body.context; + const nextInspection = req.body.nextInspection || null; let updateInspection: UpdateInspectionCommand = { id: inspectionId, - context: "", + context, + nextInspection, }; await InspectionCommandHandler.update(updateInspection); diff --git a/src/controller/admin/unit/inspectionPlanController.ts b/src/controller/admin/unit/inspectionPlanController.ts index 358ac66..5089c82 100644 --- a/src/controller/admin/unit/inspectionPlanController.ts +++ b/src/controller/admin/unit/inspectionPlanController.ts @@ -7,6 +7,37 @@ import { UpdateInspectionPlanCommand, } from "../../../command/unit/inspection/inspectionPlanCommand"; import InspectionPlanCommandHandler from "../../../command/unit/inspection/inspectionPlanCommandHandler"; +import BadRequestException from "../../../exceptions/badRequestException"; +import TypeTester from "../../../helpers/typeTester"; + +/** + * @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 @@ -23,7 +54,7 @@ export async function getAllInspectionPlansForRelated(req: Request, res: Respons let noLimit = req.query.noLimit === "true"; let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); - let where = relation === "equipment" ? { equipmentId: relationId } : { vehicleId: relationId }; + let where = relation === "equipment" ? { equipmentTypeId: relationId } : { vehicleTypeId: relationId }; let [inspectionPlans, total] = await InspectionPlanService.getAllForRelated(where, { offset, count, @@ -60,14 +91,24 @@ export async function getInspectionPlanById(req: Request, res: Response): Promis * @returns {Promise<*>} */ export async function createInspectionPlan(req: Request, res: Response): Promise { - const salutationId = parseInt(req.body.salutationId); + 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") + throw new BadRequestException("set assigned to equipment or vehicle"); let createInspectionPlan: CreateInspectionPlanCommand = { - title: "", - inspectionInterval: "1-m", - remindTime: "1-m", - relatedId: "", - assigned: "equipment", + title, + inspectionInterval, + remindTime, + relatedId, + assigned, }; let inspectionPlanId = await InspectionPlanCommandHandler.create(createInspectionPlan); @@ -82,12 +123,18 @@ export async function createInspectionPlan(req: Request, res: Response): Promise */ export async function updateInspectionPlanById(req: Request, res: Response): Promise { const inspectionPlanId = req.params.id; - const salutationId = parseInt(req.body.salutationId); + 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: "1-m", + title, + inspectionInterval, + remindTime, }; await InspectionPlanCommandHandler.update(updateInspectionPlan); diff --git a/src/controller/admin/unit/vehicleController.ts b/src/controller/admin/unit/vehicleController.ts index 02b3606..a54d2c1 100644 --- a/src/controller/admin/unit/vehicleController.ts +++ b/src/controller/admin/unit/vehicleController.ts @@ -24,7 +24,7 @@ export async function getAllVehicles(req: Request, res: Response): Promise let [vehicles, total] = await VehicleService.getAll({ offset, count, search, noLimit, ids }); res.json({ - vehicles: vehicles, + vehicles: VehicleFactory.mapToBase(vehicles), total: total, offset: offset, count: count, @@ -44,6 +44,25 @@ export async function getVehicleById(req: Request, res: Response): Promise 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 @@ -51,13 +70,18 @@ export async function getVehicleById(req: Request, res: Response): Promise * @returns {Promise<*>} */ export async function createVehicle(req: Request, res: Response): Promise { - const salutationId = parseInt(req.body.salutationId); + 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 = { - name: "", - location: "", - commissioned: undefined, - vehicleTypeId: "", + code, + name, + location, + commissioned, + vehicleTypeId, }; let vehicleId = await VehicleCommandHandler.create(createVehicle); @@ -72,13 +96,19 @@ export async function createVehicle(req: Request, res: Response): Promise { */ export async function updateVehicleById(req: Request, res: Response): Promise { const vehicleId = req.params.id; - const salutationId = parseInt(req.body.salutationId); + 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, - name: "", - location: "", - commissioned: undefined, + code, + name, + location, + commissioned, + decommissioned, }; await VehicleCommandHandler.update(updateVehicle); diff --git a/src/controller/admin/unit/vehicleTypeController.ts b/src/controller/admin/unit/vehicleTypeController.ts index 7cf5552..b105ea1 100644 --- a/src/controller/admin/unit/vehicleTypeController.ts +++ b/src/controller/admin/unit/vehicleTypeController.ts @@ -19,12 +19,11 @@ export async function getAllVehicleTypes(req: Request, res: Response): Promise i); - let [vehicleTypes, total] = await VehicleTypeService.getAll({ offset, count, search, noLimit, ids }); + let [vehicleTypes, total] = await VehicleTypeService.getAll({ offset, count, search, noLimit }); res.json({ - vehicleTypes: vehicleTypes, + vehicleTypes: VehicleTypeFactory.mapToBase(vehicleTypes), total: total, offset: offset, count: count, @@ -51,11 +50,12 @@ export async function getVehicleTypeById(req: Request, res: Response): Promise} */ export async function createVehicleType(req: Request, res: Response): Promise { - const salutationId = parseInt(req.body.salutationId); + const type = req.body.type; + const description = req.body.description; let createVehicleType: CreateVehicleTypeCommand = { - type: "", - description: "", + type, + description, }; let vehicleTypeId = await VehicleTypeCommandHandler.create(createVehicleType); @@ -70,12 +70,13 @@ export async function createVehicleType(req: Request, res: Response): Promise { const vehicleTypeId = req.params.id; - const salutationId = parseInt(req.body.salutationId); + const type = req.body.type; + const description = req.body.description; let updateVehicleType: UpdateVehicleTypeCommand = { id: vehicleTypeId, - type: "", - description: "", + type, + description, }; await VehicleTypeCommandHandler.update(updateVehicleType); diff --git a/src/controller/admin/unit/wearableController.ts b/src/controller/admin/unit/wearableController.ts index 7775c36..b28fb46 100644 --- a/src/controller/admin/unit/wearableController.ts +++ b/src/controller/admin/unit/wearableController.ts @@ -24,7 +24,7 @@ export async function getAllWearables(req: Request, res: Response): Promise let [wearables, total] = await WearableService.getAll({ offset, count, search, noLimit, ids }); res.json({ - wearables: wearables, + wearables: WearableFactory.mapToBase(wearables), total: total, offset: offset, count: count, @@ -44,6 +44,25 @@ export async function getWearableById(req: Request, res: Response): Promise 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 @@ -51,13 +70,20 @@ export async function getWearableById(req: Request, res: Response): Promise * @returns {Promise<*>} */ export async function createWearable(req: Request, res: Response): Promise { - const salutationId = parseInt(req.body.salutationId); + 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 = { - name: "", - location: "", - commissioned: undefined, - wearableTypeId: "", + code, + name, + location, + commissioned, + wearableTypeId, + wearerId, }; let wearableId = await WearableCommandHandler.create(createWearable); @@ -72,13 +98,21 @@ export async function createWearable(req: Request, res: Response): Promise */ export async function updateWearableById(req: Request, res: Response): Promise { const wearableId = req.params.id; - const salutationId = parseInt(req.body.salutationId); + 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, - name: "", - location: "", - commissioned: undefined, + code, + name, + location, + commissioned, + decommissioned, + wearerId, }; await WearableCommandHandler.update(updateWearable); diff --git a/src/controller/admin/unit/wearableTypeController.ts b/src/controller/admin/unit/wearableTypeController.ts index 40def8b..8597916 100644 --- a/src/controller/admin/unit/wearableTypeController.ts +++ b/src/controller/admin/unit/wearableTypeController.ts @@ -19,12 +19,11 @@ export async function getAllWearableTypes(req: Request, res: Response): Promise< 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 [wearableTypes, total] = await WearableTypeService.getAll({ offset, count, search, noLimit, ids }); + let [wearableTypes, total] = await WearableTypeService.getAll({ offset, count, search, noLimit }); res.json({ - wearableTypes: wearableTypes, + wearableTypes: WearableTypeFactory.mapToBase(wearableTypes), total: total, offset: offset, count: count, @@ -51,11 +50,12 @@ export async function getWearableTypeById(req: Request, res: Response): Promise< * @returns {Promise<*>} */ export async function createWearableType(req: Request, res: Response): Promise { - const salutationId = parseInt(req.body.salutationId); + const type = req.body.type; + const description = req.body.description; let createWearableType: CreateWearableTypeCommand = { - type: "", - description: "", + type, + description, }; let wearableTypeId = await WearableTypeCommandHandler.create(createWearableType); @@ -70,12 +70,13 @@ export async function createWearableType(req: Request, res: Response): Promise { const wearableTypeId = req.params.id; - const salutationId = parseInt(req.body.salutationId); + const type = req.body.type; + const description = req.body.description; let updateWearableType: UpdateWearableTypeCommand = { id: wearableTypeId, - type: "", - description: "", + type, + description, }; await WearableTypeCommandHandler.update(updateWearableType); diff --git a/src/entity/unit/equipment/equipment.ts b/src/entity/unit/equipment/equipment.ts index d55ea0d..bbd12a0 100644 --- a/src/entity/unit/equipment/equipment.ts +++ b/src/entity/unit/equipment/equipment.ts @@ -18,10 +18,10 @@ export class equipment { @Column({ type: "varchar", length: 255 }) location: string; - @Column({ type: getTypeByORM("datetime").type as ColumnType }) + @Column({ type: getTypeByORM("date").type as ColumnType }) commissioned: Date; - @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true, default: null }) + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) decommissioned?: Date; @Column() diff --git a/src/entity/unit/equipment/equipmentType.ts b/src/entity/unit/equipment/equipmentType.ts index 3888ebe..3d0b134 100644 --- a/src/entity/unit/equipment/equipmentType.ts +++ b/src/entity/unit/equipment/equipmentType.ts @@ -16,6 +16,6 @@ export class equipmentType { @OneToMany(() => equipment, (e) => e.equipmentType, { cascade: ["insert"] }) equipment: equipment[]; - @OneToMany(() => inspectionPlan, (ip) => ip.equipment) + @OneToMany(() => inspectionPlan, (ip) => ip.equipmentType) inspectionPlans: inspectionPlan[]; } diff --git a/src/entity/unit/inspection/inspectionPlan.ts b/src/entity/unit/inspection/inspectionPlan.ts index f94bf5a..b2a43eb 100644 --- a/src/entity/unit/inspection/inspectionPlan.ts +++ b/src/entity/unit/inspection/inspectionPlan.ts @@ -1,8 +1,8 @@ import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; -import { equipment } from "../equipment/equipment"; -import { vehicle } from "../vehicle/vehicle"; import { PlanTimeDefinition } from "../../../viewmodel/admin/unit/inspection/inspectionPlan.models"; import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; +import { equipmentType } from "../equipment/equipmentType"; +import { vehicleType } from "../vehicle/vehicleType"; @Entity() export class inspectionPlan { @@ -22,24 +22,24 @@ export class inspectionPlan { createdAt: Date; @Column() - equipmentId?: string; + equipmentTypeId?: string; @Column() - vehicleId?: string; + vehicleTypeId?: string; - @ManyToOne(() => equipment, { + @ManyToOne(() => equipmentType, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) - equipment?: equipment; + equipmentType?: equipmentType; - @ManyToOne(() => vehicle, { + @ManyToOne(() => vehicleType, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) - vehicle?: vehicle; + vehicleType?: vehicleType; @OneToMany(() => inspectionVersionedPlan, (ivp) => ivp.inspectionPlan, { cascade: ["insert"], diff --git a/src/entity/unit/vehicle/vehicle.ts b/src/entity/unit/vehicle/vehicle.ts index dc4a8d6..c09a28d 100644 --- a/src/entity/unit/vehicle/vehicle.ts +++ b/src/entity/unit/vehicle/vehicle.ts @@ -18,10 +18,10 @@ export class vehicle { @Column({ type: "varchar", length: 255 }) location: string; - @Column({ type: getTypeByORM("datetime").type as ColumnType }) + @Column({ type: getTypeByORM("date").type as ColumnType }) commissioned: Date; - @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true, default: null }) + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) decommissioned?: Date; @Column() diff --git a/src/entity/unit/vehicle/vehicleType.ts b/src/entity/unit/vehicle/vehicleType.ts index d58b478..a3e3403 100644 --- a/src/entity/unit/vehicle/vehicleType.ts +++ b/src/entity/unit/vehicle/vehicleType.ts @@ -16,6 +16,6 @@ export class vehicleType { @OneToMany(() => vehicle, (e) => e.vehicleType, { cascade: ["insert"] }) vehicle: vehicle[]; - @OneToMany(() => inspectionPlan, (ip) => ip.vehicle) + @OneToMany(() => inspectionPlan, (ip) => ip.vehicleType) inspectionPlans: inspectionPlan[]; } diff --git a/src/entity/unit/wearable/wearable.ts b/src/entity/unit/wearable/wearable.ts index 3b1ea39..0313997 100644 --- a/src/entity/unit/wearable/wearable.ts +++ b/src/entity/unit/wearable/wearable.ts @@ -19,10 +19,10 @@ export class wearable { @Column({ type: "varchar", length: 255 }) location: string; - @Column({ type: getTypeByORM("datetime").type as ColumnType }) + @Column({ type: getTypeByORM("date").type as ColumnType }) commissioned: Date; - @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true, default: null }) + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) decommissioned?: Date; @Column() diff --git a/src/factory/admin/club/member/member.ts b/src/factory/admin/club/member/member.ts index 246c0ae..4e953cb 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/inspection/inspectionPlan.ts b/src/factory/admin/unit/inspection/inspectionPlan.ts index 2e8fe5b..1a0439d 100644 --- a/src/factory/admin/unit/inspection/inspectionPlan.ts +++ b/src/factory/admin/unit/inspection/inspectionPlan.ts @@ -1,7 +1,9 @@ import { inspectionPlan } from "../../../../entity/unit/inspection/inspectionPlan"; import { 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 InspectionPointFactory from "./inspectionPoint"; export default abstract class InspectionPlanFactory { @@ -16,19 +18,21 @@ export default abstract class InspectionPlanFactory { title: record.title, inspectionInterval: record.inspectionInterval, remindTime: record.remindTime, - version: record.latestVersionedPlan.version, + version: record?.latestVersionedPlan?.version ?? 0, created: record.createdAt, - inspectionPoints: InspectionPointFactory.mapToBase(record.latestVersionedPlan.inspectionPoints), - ...(record.equipmentId + inspectionPoints: record.latestVersionedPlan + ? InspectionPointFactory.mapToBase(record.latestVersionedPlan.inspectionPoints) + : [], + ...(record.equipmentTypeId ? { - relatedId: record.equipmentId, + relatedId: record.equipmentTypeId, assigned: "equipment", - related: EquipmentFactory.mapToSingle(record.equipment), + related: EquipmentTypeFactory.mapToSingle(record.equipmentType), } : { - relatedId: record.vehicleId, + relatedId: record.vehicleTypeId, assigned: "vehicle", - related: VehicleFactory.mapToSingle(record.vehicle), + related: VehicleTypeFactory.mapToSingle(record.vehicleType), }), }; } 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/migrations/baseSchemaTables/inspection.ts b/src/migrations/baseSchemaTables/inspection.ts index b064cb6..bfa480a 100644 --- a/src/migrations/baseSchemaTables/inspection.ts +++ b/src/migrations/baseSchemaTables/inspection.ts @@ -9,21 +9,21 @@ export const inspection_plan_table = new Table({ { name: "inspectionInterval", ...getTypeByORM("varchar") }, { name: "remindTime", ...getTypeByORM("varchar") }, { name: "createdAt", ...getTypeByORM("date"), default: getDefaultByORM("currentTimestamp") }, - { name: "equipmentId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, - { name: "vehicleId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "equipmentTypeId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "vehicleTypeId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, ], foreignKeys: [ new TableForeignKey({ - columnNames: ["equipmentId"], + columnNames: ["equipmentTypeId"], referencedColumnNames: ["id"], - referencedTableName: "equipment", + referencedTableName: "equipment_type", onDelete: "CASCADE", onUpdate: "RESTRICT", }), new TableForeignKey({ - columnNames: ["vehicleId"], + columnNames: ["vehicleTypeId"], referencedColumnNames: ["id"], - referencedTableName: "vehicle", + referencedTableName: "vehicle_type", onDelete: "CASCADE", onUpdate: "RESTRICT", }), diff --git a/src/routes/admin/index.ts b/src/routes/admin/index.ts index 13e48f5..782f89b 100644 --- a/src/routes/admin/index.ts +++ b/src/routes/admin/index.ts @@ -40,6 +40,7 @@ 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"; var router = express.Router({ mergeParams: true }); @@ -215,5 +216,15 @@ router.use( ]), inspectionPlan ); +router.use( + "/damagereport", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "damage_report" }, + { requiredPermission: "read", section: "unit", module: "equipment" }, + { requiredPermission: "read", section: "unit", module: "vehicle" }, + { requiredPermission: "read", section: "unit", module: "wearable" }, + ]), + damageReport +); 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/inspectionPlan.ts b/src/routes/admin/unit/inspectionPlan.ts index 0e33d5c..632c130 100644 --- a/src/routes/admin/unit/inspectionPlan.ts +++ b/src/routes/admin/unit/inspectionPlan.ts @@ -3,6 +3,7 @@ import PermissionHelper from "../../../helpers/permissionHelper"; import { createInspectionPlan, deleteInspectionPlanById, + getAllInspectionPlans, getAllInspectionPlansForRelated, getInspectionPlanById, updateInspectionPlanById, @@ -10,6 +11,10 @@ import { var router = express.Router({ mergeParams: true }); +router.get("/", async (req: Request, res: Response) => { + await getAllInspectionPlans(req, res); +}); + router.get(["/vehicle/:relatedId", "/equipment/:relatedId"], async (req: Request, res: Response) => { if (req.path.startsWith("/vehicle")) { req.params.related = "vehicle"; diff --git a/src/routes/admin/unit/vehicle.ts b/src/routes/admin/unit/vehicle.ts index 832e248..7ca588c 100644 --- a/src/routes/admin/unit/vehicle.ts +++ b/src/routes/admin/unit/vehicle.ts @@ -5,6 +5,7 @@ import { deleteVehicleById, getAllVehicles, getVehicleById, + getVehiclesByIds, updateVehicleById, } from "../../../controller/admin/unit/vehicleController"; @@ -18,6 +19,10 @@ 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"), diff --git a/src/routes/admin/unit/wearable.ts b/src/routes/admin/unit/wearable.ts index c25226a..e1c1730 100644 --- a/src/routes/admin/unit/wearable.ts +++ b/src/routes/admin/unit/wearable.ts @@ -5,6 +5,7 @@ import { deleteWearableById, getAllWearables, getWearableById, + getWearablesByIds, updateWearableById, } from "../../../controller/admin/unit/wearableController"; @@ -18,6 +19,10 @@ 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"), diff --git a/src/routes/admin/unit/wearableType.ts b/src/routes/admin/unit/wearableType.ts index b61584f..983e401 100644 --- a/src/routes/admin/unit/wearableType.ts +++ b/src/routes/admin/unit/wearableType.ts @@ -2,11 +2,11 @@ import express, { Request, Response } from "express"; import PermissionHelper from "../../../helpers/permissionHelper"; import { createWearableType, + deleteWearableTypeById, getAllWearableTypes, getWearableTypeById, updateWearableTypeById, } from "../../../controller/admin/unit/wearableTypeController"; -import { deleteWearableById } from "../../../controller/admin/unit/wearableController"; var router = express.Router({ mergeParams: true }); @@ -38,7 +38,7 @@ router.delete( "/:id", PermissionHelper.passCheckMiddleware("delete", "unit", "wearable_type"), async (req: Request, res: Response) => { - await deleteWearableById(req, res); + await deleteWearableTypeById(req, res); } ); diff --git a/src/service/unit/damageReportService.ts b/src/service/unit/damageReportService.ts index 7821ea1..622b377 100644 --- a/src/service/unit/damageReportService.ts +++ b/src/service/unit/damageReportService.ts @@ -3,20 +3,73 @@ import { damageReport } from "../../entity/unit/damageReport"; import DatabaseActionException from "../../exceptions/databaseActionException"; export default abstract class DamageReportService { - /** - * @description get all damageReports - * @returns {Promise>} - */ - static async getAll(): Promise> { - return await dataSource + private static query = () => + dataSource .getRepository(damageReport) .createQueryBuilder("damageReport") .leftJoinAndSelect("damageReport.equipment", "equipment") .leftJoinAndSelect("damageReport.vehicle", "vehicle") .leftJoinAndSelect("damageReport.wearable", "wearable") - .leftJoinAndSelect("damageReport.maintenance", "maintenance") - .orderBy("type", "ASC") - .getMany() + .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("reportedAt", "ASC") + .getManyAndCount() .then((res) => { return res; }) @@ -30,13 +83,7 @@ export default abstract class DamageReportService { * @returns {Promise} */ static async getById(id: string): Promise { - return await dataSource - .getRepository(damageReport) - .createQueryBuilder("damageReport") - .leftJoinAndSelect("damageReport.equipment", "equipment") - .leftJoinAndSelect("damageReport.vehicle", "vehicle") - .leftJoinAndSelect("damageReport.wearable", "wearable") - .leftJoinAndSelect("damageReport.maintenance", "maintenance") + return await this.query() .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/equipment/equipmentService.ts b/src/service/unit/equipment/equipmentService.ts index 7e2267b..921f574 100644 --- a/src/service/unit/equipment/equipmentService.ts +++ b/src/service/unit/equipment/equipmentService.ts @@ -27,11 +27,16 @@ export default abstract class EquipmentService { .leftJoinAndSelect("equipment.equipmentType", "equipmenttype"); if (search != "") { - query = query.where({ - code: Like(search), - name: Like(search), - location: Like(search), - }); + query = query + .where({ + code: Like(`%${search}%`), + }) + .orWhere({ + name: Like(`%${search}%`), + }) + .orWhere({ + location: Like(`%${search}%`), + }); } if (ids.length != 0) { diff --git a/src/service/unit/equipment/equipmentTypeService.ts b/src/service/unit/equipment/equipmentTypeService.ts index 8789fe7..b85b9f1 100644 --- a/src/service/unit/equipment/equipmentTypeService.ts +++ b/src/service/unit/equipment/equipmentTypeService.ts @@ -13,26 +13,20 @@ export default abstract class EquipmentTypeService { count = 25, search = "", noLimit = false, - ids = [], }: { offset?: number; count?: number; search?: string; noLimit?: boolean; - ids?: Array; }): Promise<[Array, number]> { let query = dataSource.getRepository(equipmentType).createQueryBuilder("equipmentType"); if (search != "") { query = query.where({ - type: Like(search), + type: Like(`%${search}%`), }); } - if (ids.length != 0) { - query = query.where({ id: In(ids) }); - } - if (!noLimit) { query = query.offset(offset).limit(count); } diff --git a/src/service/unit/inspection/inspectionPlanService.ts b/src/service/unit/inspection/inspectionPlanService.ts index 2514ad1..197912d 100644 --- a/src/service/unit/inspection/inspectionPlanService.ts +++ b/src/service/unit/inspection/inspectionPlanService.ts @@ -14,19 +14,63 @@ export default abstract class InspectionPlanService { "inspectionPlan.versionedPlans", "latestVersionedPlan", DB_TYPE == "postgres" - ? 'latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX("ivp"."start") FROM "inspection_versioned_plan" "ivp" WHERE "ivp"."inspectionPlanId" = "inspectionPlan"."id")' - : "latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX(ivp.start) FROM inspection_versioned_plan ivp WHERE ivp.inspectionPlanId = inspectionPlan.id)" + ? 'latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX("ivp"."createdAt") FROM "inspection_versioned_plan" "ivp" WHERE "ivp"."inspectionPlanId" = "inspectionPlan"."id")' + : "latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX(ivp.createdAt) FROM inspection_versioned_plan ivp WHERE ivp.inspectionPlanId = inspectionPlan.id)" ) .leftJoinAndSelect("latestVersionedPlan.inspectionPoints", "inspectionPoints") - .leftJoinAndSelect("inspectionPlan.equipment", "equipment") - .leftJoinAndSelect("inspectionPlan.vehicle", "vehicle"); + .leftJoinAndSelect("inspectionPlan.equipmentType", "equipmentType") + .leftJoinAndSelect("inspectionPlan.vehicleType", "vehicleType"); + + /** + * @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: { equipmentId: string } | { vehicleId: string }, + where: { equipmentTypeId: string } | { vehicleTypeId: string }, { offset = 0, count = 25, @@ -44,13 +88,13 @@ export default abstract class InspectionPlanService { let query = this.query().where(where); if (search != "") { - query = query.where({ - title: Like(search), + query = query.andWhere({ + title: Like(`%${search}%`), }); } if (ids.length != 0) { - query = query.where({ id: In(ids) }); + query = query.andWhere({ id: In(ids) }); } if (!noLimit) { @@ -58,7 +102,7 @@ export default abstract class InspectionPlanService { } return await query - .orderBy("title", "ASC") + .orderBy("inspectionPlan.title", "ASC") .getManyAndCount() .then((res) => { return res; diff --git a/src/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts index 709b335..08ec621 100644 --- a/src/service/unit/inspection/inspectionService.ts +++ b/src/service/unit/inspection/inspectionService.ts @@ -38,7 +38,7 @@ export default abstract class InspectionService { } return await query - .orderBy("createdAt", "DESC") + .orderBy("inspection.createdAt", "DESC") .getManyAndCount() .then((res) => { return res; diff --git a/src/service/unit/vehicle/vehicleService.ts b/src/service/unit/vehicle/vehicleService.ts index 921a981..61c2b73 100644 --- a/src/service/unit/vehicle/vehicleService.ts +++ b/src/service/unit/vehicle/vehicleService.ts @@ -27,11 +27,16 @@ export default abstract class VehicleService { .leftJoinAndSelect("vehicle.vehicleType", "vehicletype"); if (search != "") { - query = query.where({ - code: Like(search), - name: Like(search), - location: Like(search), - }); + query = query + .where({ + code: Like(`%${search}%`), + }) + .orWhere({ + name: Like(`%${search}%`), + }) + .orWhere({ + location: Like(`%${search}%`), + }); } if (ids.length != 0) { diff --git a/src/service/unit/vehicle/vehicleTypeService.ts b/src/service/unit/vehicle/vehicleTypeService.ts index 6ecb703..c1dbdf2 100644 --- a/src/service/unit/vehicle/vehicleTypeService.ts +++ b/src/service/unit/vehicle/vehicleTypeService.ts @@ -13,26 +13,20 @@ export default abstract class VehicleTypeService { count = 25, search = "", noLimit = false, - ids = [], }: { offset?: number; count?: number; search?: string; noLimit?: boolean; - ids?: Array; }): Promise<[Array, number]> { let query = dataSource.getRepository(vehicleType).createQueryBuilder("vehicleType"); if (search != "") { query = query.where({ - type: Like(search), + type: Like(`%${search}%`), }); } - if (ids.length != 0) { - query = query.where({ id: In(ids) }); - } - if (!noLimit) { query = query.offset(offset).limit(count); } diff --git a/src/service/unit/wearable/wearableService.ts b/src/service/unit/wearable/wearableService.ts index b369f47..d016b0f 100644 --- a/src/service/unit/wearable/wearableService.ts +++ b/src/service/unit/wearable/wearableService.ts @@ -28,11 +28,16 @@ export default abstract class WearableService { .leftJoinAndSelect("wearable.wearer", "wearer"); if (search != "") { - query = query.where({ - code: Like(search), - name: Like(search), - location: Like(search), - }); + query = query + .where({ + code: Like(`%${search}%`), + }) + .orWhere({ + name: Like(`%${search}%`), + }) + .orWhere({ + location: Like(`%${search}%`), + }); } if (ids.length != 0) { @@ -63,6 +68,7 @@ export default abstract class WearableService { .getRepository(wearable) .createQueryBuilder("wearable") .leftJoinAndSelect("wearable.wearableType", "wearabletype") + .leftJoinAndSelect("wearable.wearer", "wearer") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/wearable/wearableTypeService.ts b/src/service/unit/wearable/wearableTypeService.ts index d6f167f..cd91a26 100644 --- a/src/service/unit/wearable/wearableTypeService.ts +++ b/src/service/unit/wearable/wearableTypeService.ts @@ -13,26 +13,20 @@ export default abstract class WearableTypeService { count = 25, search = "", noLimit = false, - ids = [], }: { offset?: number; count?: number; search?: string; noLimit?: boolean; - ids?: Array; }): Promise<[Array, number]> { let query = dataSource.getRepository(wearableType).createQueryBuilder("wearableType"); if (search != "") { query = query.where({ - type: Like(search), + type: Like(`%${search}%`), }); } - if (ids.length != 0) { - query = query.where({ id: In(ids) }); - } - if (!noLimit) { query = query.offset(offset).limit(count); } diff --git a/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts index cbbea41..fe215d5 100644 --- a/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts @@ -1,6 +1,8 @@ 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"; export type PlanTimeDefinition = `${number}-${"d" | "m" | "y"}` | `${number}/${number | "*"}`; @@ -16,11 +18,11 @@ export type InspectionPlanViewModel = { } & ( | { assigned: "equipment"; - related: EquipmentViewModel; + related: EquipmentTypeViewModel; } | { assigned: "vehicle"; - related: VehicleViewModel; + related: VehicleTypeViewModel; } ); From f11ed83afc10beb95c8d8223a29bce420bdc9b7d Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sun, 8 Jun 2025 07:55:40 +0200 Subject: [PATCH 20/55] change schema according to migration --- src/entity/unit/inspection/inspection.ts | 4 ++-- src/entity/unit/inspection/inspectionPlan.ts | 4 ++-- src/entity/unit/wearable/wearable.ts | 2 +- src/migrations/baseSchemaTables/inspection.ts | 20 +++++++++---------- src/migrations/baseSchemaTables/unit.ts | 20 +++++++++---------- .../baseSchemaTables/unit_extend.ts | 18 ++++++++--------- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/entity/unit/inspection/inspection.ts b/src/entity/unit/inspection/inspection.ts index 00faf98..1bcbde4 100644 --- a/src/entity/unit/inspection/inspection.ts +++ b/src/entity/unit/inspection/inspection.ts @@ -29,10 +29,10 @@ export class inspection { @Column() inspectionVersionedPlanId: string; - @Column() + @Column({ nullable: true, default: null }) equipmentId?: string; - @Column() + @Column({ nullable: true, default: null }) vehicleId?: string; @ManyToOne(() => inspectionPlan, { diff --git a/src/entity/unit/inspection/inspectionPlan.ts b/src/entity/unit/inspection/inspectionPlan.ts index b2a43eb..1e9b2e3 100644 --- a/src/entity/unit/inspection/inspectionPlan.ts +++ b/src/entity/unit/inspection/inspectionPlan.ts @@ -21,10 +21,10 @@ export class inspectionPlan { @CreateDateColumn() createdAt: Date; - @Column() + @Column({ nullable: true, default: null }) equipmentTypeId?: string; - @Column() + @Column({ nullable: true, default: null }) vehicleTypeId?: string; @ManyToOne(() => equipmentType, { diff --git a/src/entity/unit/wearable/wearable.ts b/src/entity/unit/wearable/wearable.ts index 0313997..d1168d6 100644 --- a/src/entity/unit/wearable/wearable.ts +++ b/src/entity/unit/wearable/wearable.ts @@ -28,7 +28,7 @@ export class wearable { @Column() wearableTypeId: string; - @Column() + @Column({ nullable: true, default: null }) wearerId?: string; @ManyToOne(() => wearableType, { diff --git a/src/migrations/baseSchemaTables/inspection.ts b/src/migrations/baseSchemaTables/inspection.ts index bfa480a..fbca0ab 100644 --- a/src/migrations/baseSchemaTables/inspection.ts +++ b/src/migrations/baseSchemaTables/inspection.ts @@ -1,4 +1,4 @@ -import { Table, TableForeignKey, TableUnique, Unique } from "typeorm"; +import { Table, TableForeignKey, TableUnique } from "typeorm"; import { getTypeByORM, isUUIDPrimary, getDefaultByORM } from "../ormHelper"; export const inspection_plan_table = new Table({ @@ -8,9 +8,9 @@ export const inspection_plan_table = new Table({ { name: "title", ...getTypeByORM("varchar") }, { name: "inspectionInterval", ...getTypeByORM("varchar") }, { name: "remindTime", ...getTypeByORM("varchar") }, - { name: "createdAt", ...getTypeByORM("date"), default: getDefaultByORM("currentTimestamp") }, - { name: "equipmentTypeId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, - { name: "vehicleTypeId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, + { name: "equipmentTypeId", ...getTypeByORM("uuid", true) }, + { name: "vehicleTypeId", ...getTypeByORM("uuid", true) }, ], foreignKeys: [ new TableForeignKey({ @@ -35,7 +35,7 @@ export const inspection_versioned_plan_table = new Table({ columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, { name: "version", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, - { name: "createdAt", ...getTypeByORM("date"), default: getDefaultByORM("currentTimestamp") }, + { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, { name: "inspectionPlanId", ...getTypeByORM("uuid") }, ], foreignKeys: [ @@ -62,8 +62,8 @@ export const inspection_point_table = new Table({ { name: "title", ...getTypeByORM("varchar") }, { name: "description", ...getTypeByORM("text") }, { name: "type", ...getTypeByORM("varchar") }, - { name: "min", ...getTypeByORM("int", true), default: getDefaultByORM("null") }, - { name: "max", ...getTypeByORM("int", true), default: getDefaultByORM("null") }, + { name: "min", ...getTypeByORM("int", true) }, + { name: "max", ...getTypeByORM("int", true) }, { name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, { name: "versionedPlanId", ...getTypeByORM("uuid") }, ], @@ -83,13 +83,13 @@ export const inspection_table = new Table({ columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, { name: "context", ...getTypeByORM("text") }, - { name: "createdAt", ...getTypeByORM("date"), default: getDefaultByORM("currentTimestamp") }, + { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, { name: "finishedAt", ...getTypeByORM("date", true) }, { name: "nextInspection", ...getTypeByORM("date", true) }, { name: "inspectionPlanId", ...getTypeByORM("uuid") }, { name: "inspectionVersionedPlanId", ...getTypeByORM("uuid") }, - { name: "equipmentId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, - { name: "vehicleId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "equipmentId", ...getTypeByORM("uuid", true) }, + { name: "vehicleId", ...getTypeByORM("uuid", true) }, ], foreignKeys: [ new TableForeignKey({ diff --git a/src/migrations/baseSchemaTables/unit.ts b/src/migrations/baseSchemaTables/unit.ts index aec48b3..4d18e11 100644 --- a/src/migrations/baseSchemaTables/unit.ts +++ b/src/migrations/baseSchemaTables/unit.ts @@ -5,11 +5,11 @@ export const equipment_table = new Table({ name: "equipment", columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, - { name: "code", ...getTypeByORM("varchar", true), default: getDefaultByORM("null"), isUnique: true }, + { name: "code", ...getTypeByORM("varchar", true), isUnique: true }, { name: "name", ...getTypeByORM("varchar") }, { name: "location", ...getTypeByORM("varchar") }, { name: "commissioned", ...getTypeByORM("date") }, - { name: "decommissioned", ...getTypeByORM("date", true), default: getDefaultByORM("null") }, + { name: "decommissioned", ...getTypeByORM("date", true) }, { name: "equipmentTypeId", ...getTypeByORM("uuid") }, ], foreignKeys: [ @@ -28,7 +28,7 @@ export const equipment_type_table = new Table({ columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, { name: "type", ...getTypeByORM("varchar"), isUnique: true }, - { name: "description", ...getTypeByORM("text") }, + { name: "description", ...getTypeByORM("text", true) }, ], }); @@ -36,11 +36,11 @@ export const vehicle_table = new Table({ name: "vehicle", columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, - { name: "code", ...getTypeByORM("varchar", true), default: getDefaultByORM("null"), isUnique: true }, + { name: "code", ...getTypeByORM("varchar", true), isUnique: true }, { name: "name", ...getTypeByORM("varchar") }, { name: "location", ...getTypeByORM("varchar") }, { name: "commissioned", ...getTypeByORM("date") }, - { name: "decommissioned", ...getTypeByORM("date", true), default: getDefaultByORM("null") }, + { name: "decommissioned", ...getTypeByORM("date", true) }, { name: "vehicleTypeId", ...getTypeByORM("uuid") }, ], foreignKeys: [ @@ -59,7 +59,7 @@ export const vehicle_type_table = new Table({ columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, { name: "type", ...getTypeByORM("varchar"), isUnique: true }, - { name: "description", ...getTypeByORM("text") }, + { name: "description", ...getTypeByORM("text", true) }, ], }); @@ -67,13 +67,13 @@ export const wearable_table = new Table({ name: "wearable", columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, - { name: "code", ...getTypeByORM("varchar", true), default: getDefaultByORM("null"), isUnique: true }, + { name: "code", ...getTypeByORM("varchar", true), isUnique: true }, { name: "name", ...getTypeByORM("varchar") }, { name: "location", ...getTypeByORM("varchar") }, { name: "commissioned", ...getTypeByORM("date") }, - { name: "decommissioned", ...getTypeByORM("date", true), default: getDefaultByORM("null") }, + { name: "decommissioned", ...getTypeByORM("date", true) }, { name: "wearableTypeId", ...getTypeByORM("uuid") }, - { name: "wearerId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "wearerId", ...getTypeByORM("uuid", true) }, ], foreignKeys: [ new TableForeignKey({ @@ -98,6 +98,6 @@ export const wearable_type_table = new Table({ columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, { name: "type", ...getTypeByORM("varchar"), isUnique: true }, - { name: "description", ...getTypeByORM("text") }, + { name: "description", ...getTypeByORM("text", true) }, ], }); diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index 3f78910..43bece7 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -5,16 +5,16 @@ export const damage_report_table = new Table({ name: "damage_report", columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, - { name: "reportedAt", ...getTypeByORM("date"), default: getDefaultByORM("currentTimestamp") }, + { 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), default: getDefaultByORM("null") }, - { name: "vehicleId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, - { name: "wearableId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, - { name: "maintenanceId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "equipmentId", ...getTypeByORM("uuid", true) }, + { name: "vehicleId", ...getTypeByORM("uuid", true) }, + { name: "wearableId", ...getTypeByORM("uuid", true) }, + { name: "maintenanceId", ...getTypeByORM("uuid", true) }, ], foreignKeys: [ new TableForeignKey({ @@ -52,13 +52,13 @@ export const maintenance_table = new Table({ name: "maintenance", columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, - { name: "createdAt", ...getTypeByORM("date"), default: getDefaultByORM("currentTimestamp") }, + { 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), default: getDefaultByORM("null") }, - { name: "vehicleId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, - { name: "wearableId", ...getTypeByORM("uuid", true), default: getDefaultByORM("null") }, + { name: "equipmentId", ...getTypeByORM("uuid", true) }, + { name: "vehicleId", ...getTypeByORM("uuid", true) }, + { name: "wearableId", ...getTypeByORM("uuid", true) }, ], foreignKeys: [ new TableForeignKey({ From aeb1ccbc42087ad19fab3da5da3d6eed0255f8f6 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sun, 8 Jun 2025 08:02:07 +0200 Subject: [PATCH 21/55] fix according to move to postgres --- src/service/unit/inspection/inspectionPlanService.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/service/unit/inspection/inspectionPlanService.ts b/src/service/unit/inspection/inspectionPlanService.ts index 197912d..62f851a 100644 --- a/src/service/unit/inspection/inspectionPlanService.ts +++ b/src/service/unit/inspection/inspectionPlanService.ts @@ -1,7 +1,6 @@ import { Like, In } from "typeorm"; import { dataSource } from "../../../data-source"; import { inspectionPlan } from "../../../entity/unit/inspection/inspectionPlan"; -import { DB_TYPE } from "../../../env.defaults"; import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class InspectionPlanService { @@ -13,9 +12,7 @@ export default abstract class InspectionPlanService { "inspectionPlan.latestVersionedPlan", "inspectionPlan.versionedPlans", "latestVersionedPlan", - DB_TYPE == "postgres" - ? 'latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX("ivp"."createdAt") FROM "inspection_versioned_plan" "ivp" WHERE "ivp"."inspectionPlanId" = "inspectionPlan"."id")' - : "latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX(ivp.createdAt) FROM inspection_versioned_plan ivp WHERE ivp.inspectionPlanId = inspectionPlan.id)" + 'latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX("ivp"."createdAt") FROM "inspection_versioned_plan" "ivp" WHERE "ivp"."inspectionPlanId" = "inspectionPlan"."id")' ) .leftJoinAndSelect("latestVersionedPlan.inspectionPoints", "inspectionPoints") .leftJoinAndSelect("inspectionPlan.equipmentType", "equipmentType") From b8b2186c5861fdf986bdd9b8e465be4d79a9e86d Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 13 Jun 2025 11:31:34 +0200 Subject: [PATCH 22/55] extend wearable and enable maintenance --- .../admin/unit/damageReportController.ts | 11 +- .../admin/unit/inspectionController.ts | 15 ++- .../admin/unit/inspectionPlanController.ts | 15 ++- .../admin/unit/maintenanceController.ts | 119 ++++++++++++++++++ src/entity/unit/inspection/inspection.ts | 11 ++ src/entity/unit/inspection/inspectionPlan.ts | 11 ++ src/entity/unit/wearable/wearable.ts | 3 + src/entity/unit/wearable/wearableType.ts | 4 + .../admin/unit/inspection/inspection.ts | 36 ++++-- .../admin/unit/inspection/inspectionPlan.ts | 39 ++++-- src/routes/admin/index.ts | 56 ++++++++- src/routes/admin/unit/inspection.ts | 21 ++-- src/routes/admin/unit/inspectionPlan.ts | 21 ++-- src/routes/admin/unit/maintenance.ts | 43 +++++++ .../unit/inspection/inspectionPlanService.ts | 5 +- .../unit/inspection/inspectionService.ts | 5 +- src/service/unit/maintenanceService.ts | 78 +++++++++--- src/type/permissionTypes.ts | 3 + .../unit/inspection/inspection.models.ts | 30 +++-- .../unit/inspection/inspectionPlan.models.ts | 24 ++-- 20 files changed, 457 insertions(+), 93 deletions(-) create mode 100644 src/controller/admin/unit/maintenanceController.ts create mode 100644 src/routes/admin/unit/maintenance.ts diff --git a/src/controller/admin/unit/damageReportController.ts b/src/controller/admin/unit/damageReportController.ts index 9576ec9..443a6cc 100644 --- a/src/controller/admin/unit/damageReportController.ts +++ b/src/controller/admin/unit/damageReportController.ts @@ -34,13 +34,20 @@ export async function getAllDamageReportsByStatus(req: Request, res: Response): * @returns {Promise<*>} */ export async function getAllDamageReportsForRelated(req: Request, res: Response): Promise { - let relation = req.params.related as "vehicle" | "equipment"; + 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 = relation === "equipment" ? { equipmentId: relationId } : { vehicleId: relationId }; + 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({ diff --git a/src/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts index 2380bdf..e7d3dce 100644 --- a/src/controller/admin/unit/inspectionController.ts +++ b/src/controller/admin/unit/inspectionController.ts @@ -16,13 +16,20 @@ import BadRequestException from "../../../exceptions/badRequestException"; * @returns {Promise<*>} */ export async function getAllInspectionsForRelated(req: Request, res: Response): Promise { - let relation = req.params.related as "vehicle" | "equipment"; + 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 = relation === "equipment" ? { equipmentId: relationId } : { vehicleId: relationId }; + 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({ @@ -59,8 +66,8 @@ export async function createInspection(req: Request, res: Response): Promise} */ export async function getAllInspectionPlansForRelated(req: Request, res: Response): Promise { - let relation = req.params.related as "vehicle" | "equipment"; + 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"); @@ -54,7 +54,14 @@ export async function getAllInspectionPlansForRelated(req: Request, res: Respons let noLimit = req.query.noLimit === "true"; let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); - let where = relation === "equipment" ? { equipmentTypeId: relationId } : { vehicleTypeId: relationId }; + let where; + if (relation == "equipment") { + where = { equipmentTypeId: relationId }; + } else if (relation == "vehicle") { + where = { vehicleTypeId: relationId }; + } else { + where = { wearableTypeId: relationId }; + } let [inspectionPlans, total] = await InspectionPlanService.getAllForRelated(where, { offset, count, @@ -100,8 +107,8 @@ export async function createInspectionPlan(req: Request, res: Response): Promise TypeTester.testPlanTimeDefinition(inspectionInterval, "inspectionInterval", true); TypeTester.testPlanTimeDefinition(remindTime, "remindTime", true); - if (assigned != "equipment" && assigned != "vehicle") - throw new BadRequestException("set assigned to equipment or vehicle"); + if (assigned != "equipment" && assigned != "vehicle" && assigned != "wearable") + throw new BadRequestException("set assigned to equipment or vehicle or wearable"); let createInspectionPlan: CreateInspectionPlanCommand = { title, 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/entity/unit/inspection/inspection.ts b/src/entity/unit/inspection/inspection.ts index 1bcbde4..16aae27 100644 --- a/src/entity/unit/inspection/inspection.ts +++ b/src/entity/unit/inspection/inspection.ts @@ -5,6 +5,7 @@ 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 { @@ -35,6 +36,9 @@ export class inspection { @Column({ nullable: true, default: null }) vehicleId?: string; + @Column({ nullable: true, default: null }) + wearableId?: string; + @ManyToOne(() => inspectionPlan, { nullable: false, onDelete: "RESTRICT", @@ -63,6 +67,13 @@ export class inspection { }) vehicle: vehicle; + @ManyToOne(() => wearable, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + wearable: wearable; + @OneToMany(() => inspectionPointResult, (ipr) => ipr.inspection) pointResults: inspectionPointResult[]; } diff --git a/src/entity/unit/inspection/inspectionPlan.ts b/src/entity/unit/inspection/inspectionPlan.ts index 1e9b2e3..9fa6fad 100644 --- a/src/entity/unit/inspection/inspectionPlan.ts +++ b/src/entity/unit/inspection/inspectionPlan.ts @@ -3,6 +3,7 @@ import { PlanTimeDefinition } from "../../../viewmodel/admin/unit/inspection/ins import { inspectionVersionedPlan } from "./inspectionVersionedPlan"; import { equipmentType } from "../equipment/equipmentType"; import { vehicleType } from "../vehicle/vehicleType"; +import { wearableType } from "../wearable/wearableType"; @Entity() export class inspectionPlan { @@ -27,6 +28,9 @@ export class inspectionPlan { @Column({ nullable: true, default: null }) vehicleTypeId?: string; + @Column({ nullable: true, default: null }) + wearableTypeId?: string; + @ManyToOne(() => equipmentType, { nullable: true, onDelete: "CASCADE", @@ -41,6 +45,13 @@ export class inspectionPlan { }) vehicleType?: vehicleType; + @ManyToOne(() => wearableType, { + nullable: true, + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }) + wearableType?: wearableType; + @OneToMany(() => inspectionVersionedPlan, (ivp) => ivp.inspectionPlan, { cascade: ["insert"], }) diff --git a/src/entity/unit/wearable/wearable.ts b/src/entity/unit/wearable/wearable.ts index d1168d6..902d23a 100644 --- a/src/entity/unit/wearable/wearable.ts +++ b/src/entity/unit/wearable/wearable.ts @@ -47,4 +47,7 @@ export class wearable { @OneToMany(() => damageReport, (d) => d.wearable, { cascade: ["insert"] }) reports: damageReport[]; + + @OneToMany(() => inspection, (i) => i.wearable) + inspections: inspection[]; } diff --git a/src/entity/unit/wearable/wearableType.ts b/src/entity/unit/wearable/wearableType.ts index 0905a33..15ae351 100644 --- a/src/entity/unit/wearable/wearableType.ts +++ b/src/entity/unit/wearable/wearableType.ts @@ -1,5 +1,6 @@ import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { wearable as wearable } from "./wearable"; +import { inspectionPlan } from "../inspection/inspectionPlan"; @Entity() export class wearableType { @@ -14,4 +15,7 @@ export class wearableType { @OneToMany(() => wearable, (e) => e.wearableType, { cascade: ["insert"] }) wearable: wearable[]; + + @OneToMany(() => inspectionPlan, (ip) => ip.wearableType) + inspectionPlans: inspectionPlan[]; } diff --git a/src/factory/admin/unit/inspection/inspection.ts b/src/factory/admin/unit/inspection/inspection.ts index 3505e11..52a10f8 100644 --- a/src/factory/admin/unit/inspection/inspection.ts +++ b/src/factory/admin/unit/inspection/inspection.ts @@ -1,7 +1,8 @@ import { inspection } from "../../../../entity/unit/inspection/inspection"; -import { InspectionViewModel } from "../../../../viewmodel/admin/unit/inspection/inspection.models"; +import { InspectionRelated, InspectionViewModel } 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"; @@ -13,6 +14,27 @@ export default abstract class InspectionFactory { * @returns {InspectionViewModel} */ public static mapToSingle(record: inspection): InspectionViewModel { + 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 { id: record.id, inspectionPlanId: record.inspectionPlanId, @@ -25,17 +47,7 @@ export default abstract class InspectionFactory { isOpen: record?.finishedAt == undefined, nextInspection: record?.nextInspection, checks: InspectionPointResultFactory.mapToBase(record.pointResults), - ...(record.equipmentId - ? { - relatedId: record.equipmentId, - assigned: "equipment", - related: EquipmentFactory.mapToSingle(record.equipment), - } - : { - relatedId: record.vehicleId, - assigned: "vehicle", - related: VehicleFactory.mapToSingle(record.vehicle), - }), + ...related, }; } diff --git a/src/factory/admin/unit/inspection/inspectionPlan.ts b/src/factory/admin/unit/inspection/inspectionPlan.ts index 1a0439d..6e48ed5 100644 --- a/src/factory/admin/unit/inspection/inspectionPlan.ts +++ b/src/factory/admin/unit/inspection/inspectionPlan.ts @@ -1,9 +1,13 @@ import { inspectionPlan } from "../../../../entity/unit/inspection/inspectionPlan"; -import { InspectionPlanViewModel } from "../../../../viewmodel/admin/unit/inspection/inspectionPlan.models"; +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 { @@ -13,6 +17,27 @@ export default abstract class InspectionPlanFactory { * @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, @@ -23,17 +48,7 @@ export default abstract class InspectionPlanFactory { inspectionPoints: record.latestVersionedPlan ? InspectionPointFactory.mapToBase(record.latestVersionedPlan.inspectionPoints) : [], - ...(record.equipmentTypeId - ? { - relatedId: record.equipmentTypeId, - assigned: "equipment", - related: EquipmentTypeFactory.mapToSingle(record.equipmentType), - } - : { - relatedId: record.vehicleTypeId, - assigned: "vehicle", - related: VehicleTypeFactory.mapToSingle(record.vehicleType), - }), + ...related, }; } diff --git a/src/routes/admin/index.ts b/src/routes/admin/index.ts index 6f7e187..f6f1f3d 100644 --- a/src/routes/admin/index.ts +++ b/src/routes/admin/index.ts @@ -42,6 +42,7 @@ 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 }); @@ -189,39 +190,75 @@ router.use( router.use("/setting", PermissionHelper.passCheckMiddleware("read", "management", "setting"), setting); /** unit */ -router.use("/equipment", PermissionHelper.passCheckMiddleware("read", "unit", "equipment"), equipment); +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.passCheckMiddleware("read", "unit", "vehicle"), vehicle); +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.passCheckMiddleware("read", "unit", "wearable"), wearable); +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.passCheckMiddleware("read", "unit", "inspection"), inspection); +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 ); @@ -229,11 +266,22 @@ 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( + "/mainenance", + 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/inspection.ts b/src/routes/admin/unit/inspection.ts index 8c7a63d..41694ea 100644 --- a/src/routes/admin/unit/inspection.ts +++ b/src/routes/admin/unit/inspection.ts @@ -10,15 +10,20 @@ import { var router = express.Router({ mergeParams: true }); -router.get(["/vehicle/:relatedId", "/equipment/:relatedId"], async (req: Request, res: Response) => { - if (req.path.startsWith("/vehicle")) { - req.params.related = "vehicle"; - } else { - req.params.related = "equipment"; - } +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); -}); + await getAllInspectionsForRelated(req, res); + } +); router.get("/:id", async (req: Request, res: Response) => { await getInspectionById(req, res); diff --git a/src/routes/admin/unit/inspectionPlan.ts b/src/routes/admin/unit/inspectionPlan.ts index 632c130..0aaf46a 100644 --- a/src/routes/admin/unit/inspectionPlan.ts +++ b/src/routes/admin/unit/inspectionPlan.ts @@ -15,15 +15,20 @@ router.get("/", async (req: Request, res: Response) => { await getAllInspectionPlans(req, res); }); -router.get(["/vehicle/:relatedId", "/equipment/:relatedId"], async (req: Request, res: Response) => { - if (req.path.startsWith("/vehicle")) { - req.params.related = "vehicle"; - } else { - req.params.related = "equipment"; - } +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 getAllInspectionPlansForRelated(req, res); -}); + await getAllInspectionPlansForRelated(req, res); + } +); router.get("/:id", async (req: Request, res: Response) => { await getInspectionPlanById(req, res); 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/service/unit/inspection/inspectionPlanService.ts b/src/service/unit/inspection/inspectionPlanService.ts index 62f851a..f422798 100644 --- a/src/service/unit/inspection/inspectionPlanService.ts +++ b/src/service/unit/inspection/inspectionPlanService.ts @@ -16,7 +16,8 @@ export default abstract class InspectionPlanService { ) .leftJoinAndSelect("latestVersionedPlan.inspectionPoints", "inspectionPoints") .leftJoinAndSelect("inspectionPlan.equipmentType", "equipmentType") - .leftJoinAndSelect("inspectionPlan.vehicleType", "vehicleType"); + .leftJoinAndSelect("inspectionPlan.vehicleType", "vehicleType") + .leftJoinAndSelect("inspectionPlan.wearableType", "wearableType"); /** * @description get all inspectionPlans for related @@ -67,7 +68,7 @@ export default abstract class InspectionPlanService { * @returns {Promise<[Array, number]>} */ static async getAllForRelated( - where: { equipmentTypeId: string } | { vehicleTypeId: string }, + where: { equipmentTypeId: string } | { vehicleTypeId: string } | { wearableTypeId: string }, { offset = 0, count = 25, diff --git a/src/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts index 08ec621..c4c8b1d 100644 --- a/src/service/unit/inspection/inspectionService.ts +++ b/src/service/unit/inspection/inspectionService.ts @@ -13,14 +13,15 @@ export default abstract class InspectionService { .leftJoinAndSelect("inspection.pointResults", "pointResults") .leftJoinAndSelect("pointResults.inspectionPoint", "inspectionPoint") .leftJoinAndSelect("inspection.equipment", "equipment") - .leftJoinAndSelect("inspection.vehicle", "vehicle"); + .leftJoinAndSelect("inspection.vehicle", "vehicle") + .leftJoinAndSelect("inspection.wearable", "wearable"); /** * @description get all inspections for related * @returns {Promise>} */ static async getAllForRelated( - where: { equipmentId: string } | { vehicleId: string }, + where: { equipmentId: string } | { vehicleId: string } | { wearableId: string }, { offset = 0, count = 25, diff --git a/src/service/unit/maintenanceService.ts b/src/service/unit/maintenanceService.ts index d9e8519..73a8a39 100644 --- a/src/service/unit/maintenanceService.ts +++ b/src/service/unit/maintenanceService.ts @@ -3,20 +3,72 @@ import { maintenance } from "../../entity/unit/maintenance"; import DatabaseActionException from "../../exceptions/databaseActionException"; export default abstract class MaintenanceService { - /** - * @description get all maintenances - * @returns {Promise>} - */ - static async getAll(): Promise> { - return await dataSource + private static query = () => + dataSource .getRepository(maintenance) .createQueryBuilder("maintenance") .leftJoinAndSelect("maintenance.equipment", "equipment") .leftJoinAndSelect("maintenance.vehicle", "vehicle") .leftJoinAndSelect("maintenance.wearable", "wearable") - .leftJoinAndSelect("maintenance.reports", "reports") - .orderBy("type", "ASC") - .getMany() + .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.type", "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; }) @@ -30,13 +82,7 @@ export default abstract class MaintenanceService { * @returns {Promise} */ static async getById(id: string): Promise { - return await dataSource - .getRepository(maintenance) - .createQueryBuilder("maintenance") - .leftJoinAndSelect("maintenance.equipment", "equipment") - .leftJoinAndSelect("maintenance.vehicle", "vehicle") - .leftJoinAndSelect("maintenance.wearable", "wearable") - .leftJoinAndSelect("maintenance.reports", "reports") + return await this.query() .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/type/permissionTypes.ts b/src/type/permissionTypes.ts index f85a54f..2442d9f 100644 --- a/src/type/permissionTypes.ts +++ b/src/type/permissionTypes.ts @@ -21,6 +21,7 @@ export type PermissionModule = | "respiratory_wearer" | "respiratory_mission" | "damage_report" + | "maintenance" // configuration | "qualification" | "award" @@ -95,6 +96,7 @@ export const permissionModules: Array = [ "respiratory_wearer", "respiratory_mission", "damage_report", + "maintenance", // configuration "qualification", "award", @@ -131,6 +133,7 @@ export const sectionsAndModules: SectionsAndModulesObject = { "respiratory_wearer", "respiratory_mission", "damage_report", + "maintenance", ], configuration: [ "qualification", diff --git a/src/viewmodel/admin/unit/inspection/inspection.models.ts b/src/viewmodel/admin/unit/inspection/inspection.models.ts index 6565377..4d18e7c 100644 --- a/src/viewmodel/admin/unit/inspection/inspection.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspection.models.ts @@ -5,6 +5,24 @@ import type { 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; @@ -18,17 +36,7 @@ export type InspectionViewModel = { isOpen: boolean; nextInspection?: Date; checks: Array; - relatedId: string; -} & ( - | { - assigned: "equipment"; - related: EquipmentViewModel; - } - | { - assigned: "vehicle"; - related: VehicleViewModel; - } -); +} & InspectionRelated; export interface InspectionPointResultViewModel { inspectionId: string; diff --git a/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts index fe215d5..30706a7 100644 --- a/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts @@ -3,17 +3,11 @@ 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 InspectionPlanViewModel = { - id: string; - title: string; - inspectionInterval: PlanTimeDefinition; - remindTime: PlanTimeDefinition; - version: number; - created: Date; - inspectionPoints: InspectionPointViewModel[]; +export type InspectionPlanRelated = { relatedId: string; } & ( | { @@ -24,8 +18,22 @@ export type InspectionPlanViewModel = { 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; From d5646753f1d47675b29d1b4ba02ad0515eac67f7 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 13 Jun 2025 18:05:55 +0200 Subject: [PATCH 23/55] inspection extend fixes --- src/migrations/baseSchemaTables/inspection.ts | 16 ++++++++++++++++ src/routes/admin/index.ts | 2 +- src/service/unit/damageReportService.ts | 2 +- src/service/unit/maintenanceService.ts | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/migrations/baseSchemaTables/inspection.ts b/src/migrations/baseSchemaTables/inspection.ts index fbca0ab..2b827fc 100644 --- a/src/migrations/baseSchemaTables/inspection.ts +++ b/src/migrations/baseSchemaTables/inspection.ts @@ -11,6 +11,7 @@ export const inspection_plan_table = new Table({ { 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({ @@ -27,6 +28,13 @@ export const inspection_plan_table = new Table({ onDelete: "CASCADE", onUpdate: "RESTRICT", }), + new TableForeignKey({ + columnNames: ["wearableTypeId"], + referencedColumnNames: ["id"], + referencedTableName: "wearable_type", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), ], }); @@ -90,6 +98,7 @@ export const inspection_table = new Table({ { name: "inspectionVersionedPlanId", ...getTypeByORM("uuid") }, { name: "equipmentId", ...getTypeByORM("uuid", true) }, { name: "vehicleId", ...getTypeByORM("uuid", true) }, + { name: "wearableId", ...getTypeByORM("uuid", true) }, ], foreignKeys: [ new TableForeignKey({ @@ -120,6 +129,13 @@ export const inspection_table = new Table({ 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 f6f1f3d..3a6edf2 100644 --- a/src/routes/admin/index.ts +++ b/src/routes/admin/index.ts @@ -274,7 +274,7 @@ router.use( damageReport ); router.use( - "/mainenance", + "/maintenance", PermissionHelper.passCheckSomeMiddleware([ { requiredPermission: "read", section: "unit", module: "maintenance" }, { requiredPermission: "read", section: "unit", module: "equipment" }, diff --git a/src/service/unit/damageReportService.ts b/src/service/unit/damageReportService.ts index 622b377..21072c7 100644 --- a/src/service/unit/damageReportService.ts +++ b/src/service/unit/damageReportService.ts @@ -68,7 +68,7 @@ export default abstract class DamageReportService { } return await query - .orderBy("reportedAt", "ASC") + .orderBy("damageReport.reportedAt", "ASC") .getManyAndCount() .then((res) => { return res; diff --git a/src/service/unit/maintenanceService.ts b/src/service/unit/maintenanceService.ts index 73a8a39..d9d997d 100644 --- a/src/service/unit/maintenanceService.ts +++ b/src/service/unit/maintenanceService.ts @@ -34,7 +34,7 @@ export default abstract class MaintenanceService { } return await query - .orderBy("maintenance.type", "ASC") + .orderBy("maintenance.createdAt", "ASC") .getManyAndCount() .then((res) => { return res; From a0a8edf7afd4af861c673e9a496996dd24744007 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 4 Jul 2025 11:57:13 +0200 Subject: [PATCH 24/55] fix get latest inspection plan --- src/service/unit/inspection/inspectionPlanService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/unit/inspection/inspectionPlanService.ts b/src/service/unit/inspection/inspectionPlanService.ts index f422798..0ee2e89 100644 --- a/src/service/unit/inspection/inspectionPlanService.ts +++ b/src/service/unit/inspection/inspectionPlanService.ts @@ -12,7 +12,7 @@ export default abstract class InspectionPlanService { "inspectionPlan.latestVersionedPlan", "inspectionPlan.versionedPlans", "latestVersionedPlan", - 'latestVersionedPlan.inspectionPlanId = inspectionPlan.id AND latestVersionedPlan.version = (SELECT MAX("ivp"."createdAt") FROM "inspection_versioned_plan" "ivp" WHERE "ivp"."inspectionPlanId" = "inspectionPlan"."id")' + '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") From 95b6cec66d48999ac46195a76baadd481bc72d26 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 9 Jul 2025 12:57:37 +0200 Subject: [PATCH 25/55] inspection fetch --- .../inspection/inspectionCommandHandler.ts | 87 ++++++++++++++----- .../admin/unit/inspectionController.ts | 48 +++++++++- src/entity/unit/inspection/inspection.ts | 3 + src/migrations/baseSchemaTables/inspection.ts | 1 + src/routes/admin/unit/inspection.ts | 10 +++ .../unit/inspection/inspectionService.ts | 61 +++++++++++++ 6 files changed, 189 insertions(+), 21 deletions(-) diff --git a/src/command/unit/inspection/inspectionCommandHandler.ts b/src/command/unit/inspection/inspectionCommandHandler.ts index 5f4a61d..34b7bf5 100644 --- a/src/command/unit/inspection/inspectionCommandHandler.ts +++ b/src/command/unit/inspection/inspectionCommandHandler.ts @@ -1,6 +1,8 @@ +import { 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 } from "./inspectionCommand"; @@ -8,27 +10,42 @@ export default abstract class InspectionCommandHandler { /** * @description create inspection * @param {CreateInspectionCommand} createInspection - * @returns {Promise} + * @returns {Promise} */ - static async create(createInspection: CreateInspectionCommand): Promise { + static async create(createInspection: CreateInspectionCommand): Promise { let latestVersionedPlan = await InspectionVersionedPlanService.getLatestForInspectionPlan( createInspection.inspectionPlanId ); + let insertId = ""; return await dataSource - .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, + .transaction(async (manager) => { + await manager + .createQueryBuilder() + .update(inspection) + .set({ + hasNewer: true, + }) + .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, + }) + .execute() + .then((result) => { + insertId = result.identifiers[0].id; + }); }) - .execute() - .then((result) => { - return result.identifiers[0].id; + .then(() => { + return insertId; }) .catch((err) => { throw new DatabaseActionException("CREATE", "inspection", err); @@ -62,12 +79,42 @@ export default abstract class InspectionCommandHandler { * @returns {Promise} */ static async delete(deleteInspection: DeleteInspectionCommand): Promise { + let deleteInspectionData = await InspectionService.getById(deleteInspection.id); return await dataSource - .createQueryBuilder() - .delete() - .from(inspection) - .where("id = :id", { id: deleteInspection.id }) - .execute() + .transaction(async (manager) => { + await manager + .createQueryBuilder() + .update(inspection) + .set({ + hasNewer: false, + }) + .where((qb) => { + const subQuery = qb + .createQueryBuilder() + .select("id") + .from(inspection, "sub") + .where({ + inspectionPlanId: deleteInspectionData.inspectionPlanId, + inspectionVersionedPlanId: deleteInspectionData.inspectionVersionedPlanId, + equipmentId: deleteInspectionData.equipmentId, + vehicleId: deleteInspectionData.vehicleId, + wearableId: deleteInspectionData.wearableId, + }) + .andWhere({ id: Not(deleteInspection.id) }) + .orderBy("sub.createdAt", "DESC") + .limit(1) + .getQuery(); + return "id = " + subQuery; + }) + .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/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts index e7d3dce..f98f4c5 100644 --- a/src/controller/admin/unit/inspectionController.ts +++ b/src/controller/admin/unit/inspectionController.ts @@ -8,6 +8,49 @@ import { } from "../../../command/unit/inspection/inspectionCommand"; import InspectionCommandHandler from "../../../command/unit/inspection/inspectionCommandHandler"; import BadRequestException from "../../../exceptions/badRequestException"; +import ForbiddenRequestException from "../../../exceptions/forbiddenRequestException"; + +/** + * @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.mapToBase(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.mapToBase(inspections), + total: total, + offset: offset, + count: count, + }); +} /** * @description get all inspections for related id @@ -111,7 +154,10 @@ export async function updateInspectionById(req: Request, res: Response): Promise export async function deleteInspectionById(req: Request, res: Response): Promise { const inspectionId = req.params.id; - // TODO finished inspection cannot be deleted + 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, diff --git a/src/entity/unit/inspection/inspection.ts b/src/entity/unit/inspection/inspection.ts index 16aae27..f49b92d 100644 --- a/src/entity/unit/inspection/inspection.ts +++ b/src/entity/unit/inspection/inspection.ts @@ -24,6 +24,9 @@ export class inspection { @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) nextInspection?: Date; + @Column({ type: "boolean", default: false }) + hasNewer: boolean; + @Column() inspectionPlanId: string; diff --git a/src/migrations/baseSchemaTables/inspection.ts b/src/migrations/baseSchemaTables/inspection.ts index 2b827fc..87af919 100644 --- a/src/migrations/baseSchemaTables/inspection.ts +++ b/src/migrations/baseSchemaTables/inspection.ts @@ -94,6 +94,7 @@ export const inspection_table = new Table({ { 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) }, diff --git a/src/routes/admin/unit/inspection.ts b/src/routes/admin/unit/inspection.ts index 41694ea..57f6883 100644 --- a/src/routes/admin/unit/inspection.ts +++ b/src/routes/admin/unit/inspection.ts @@ -3,13 +3,23 @@ import PermissionHelper from "../../../helpers/permissionHelper"; import { createInspection, deleteInspectionById, + getAllInspectionsSortedNotHavingNewer, getAllInspectionsForRelated, getInspectionById, updateInspectionById, + getAllInspectionsRunning, } from "../../../controller/admin/unit/inspectionController"; 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) => { diff --git a/src/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts index c4c8b1d..7de9af8 100644 --- a/src/service/unit/inspection/inspectionService.ts +++ b/src/service/unit/inspection/inspectionService.ts @@ -1,3 +1,4 @@ +import { IsNull } from "typeorm"; import { dataSource } from "../../../data-source"; import { inspection } from "../../../entity/unit/inspection/inspection"; import DatabaseActionException from "../../../exceptions/databaseActionException"; @@ -16,6 +17,66 @@ export default abstract class InspectionService { .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.query().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>} From db3004fa04449a8fc77c857b5b3e0292041a6fcf Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 9 Jul 2025 16:01:44 +0200 Subject: [PATCH 26/55] update or create inspection versioned plan --- .../unit/inspection/inspectionPointCommand.ts | 4 +- .../inspectionPointCommandHandler.ts | 39 +++++++++++++++ .../inspectionVersionedPlanCommandHandler.ts | 48 +++++++++++++++---- .../admin/unit/inspectionPlanController.ts | 48 +++++++++++++++++++ src/entity/unit/inspection/inspectionPoint.ts | 3 ++ src/enums/inspectionEnum.ts | 1 + .../admin/unit/inspection/inspectionPoint.ts | 1 + src/migrations/1752063536385-test.ts | 44 +++++++++++++++++ src/migrations/baseSchemaTables/inspection.ts | 1 + src/routes/admin/unit/inspectionPlan.ts | 14 ++++++ .../unit/inspection/inspectionService.ts | 18 +++++++ .../inspectionVersionedPlanService.ts | 18 +++++++ .../unit/inspection/inspectionPlan.models.ts | 1 + 13 files changed, 229 insertions(+), 11 deletions(-) create mode 100644 src/migrations/1752063536385-test.ts diff --git a/src/command/unit/inspection/inspectionPointCommand.ts b/src/command/unit/inspection/inspectionPointCommand.ts index 5aa45a6..8163bae 100644 --- a/src/command/unit/inspection/inspectionPointCommand.ts +++ b/src/command/unit/inspection/inspectionPointCommand.ts @@ -1,11 +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; + versionedPointId?: string; } diff --git a/src/command/unit/inspection/inspectionPointCommandHandler.ts b/src/command/unit/inspection/inspectionPointCommandHandler.ts index 749f325..2a46ae1 100644 --- a/src/command/unit/inspection/inspectionPointCommandHandler.ts +++ b/src/command/unit/inspection/inspectionPointCommandHandler.ts @@ -1,6 +1,7 @@ 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 { @@ -31,4 +32,42 @@ export default abstract class InspectionPointCommandHandler { 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, + 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/inspectionVersionedPlanCommandHandler.ts b/src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts index 9c9710b..decbba2 100644 --- a/src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts +++ b/src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts @@ -1,25 +1,53 @@ 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} + * @returns {Promise} */ - static async create(createInspectionVersionedPlan: CreateInspectionVersionedPlanCommand): Promise { + static async create( + createInspectionVersionedPlan: CreateInspectionVersionedPlanCommand, + inspectionPoints: Array + ): Promise { + let count = await InspectionVersionedPlanService.countForPlanId(createInspectionVersionedPlan.inspectionPlanId); + let returnId = ""; + return await dataSource - .createQueryBuilder() - .insert() - .into(inspectionVersionedPlan) - .values({ - inspectionPlanId: createInspectionVersionedPlan.inspectionPlanId, + .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) => ({ + ...ip, + versionedPlanId: returnId, + })) + ) + .execute(); }) - .execute() - .then((result) => { - return result.identifiers[0].id; + .then(() => { + return returnId; }) .catch((err) => { throw new DatabaseActionException("CREATE", "inspectionVersionedPlan", err); diff --git a/src/controller/admin/unit/inspectionPlanController.ts b/src/controller/admin/unit/inspectionPlanController.ts index a913e2b..69e3e1e 100644 --- a/src/controller/admin/unit/inspectionPlanController.ts +++ b/src/controller/admin/unit/inspectionPlanController.ts @@ -9,6 +9,11 @@ import { 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 @@ -78,6 +83,25 @@ export async function getAllInspectionPlansForRelated(req: Request, res: Respons }); } +/** + * @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 @@ -148,6 +172,30 @@ export async function updateInspectionPlanById(req: Request, res: Response): Pro 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 diff --git a/src/entity/unit/inspection/inspectionPoint.ts b/src/entity/unit/inspection/inspectionPoint.ts index 4995bc8..84c0a80 100644 --- a/src/entity/unit/inspection/inspectionPoint.ts +++ b/src/entity/unit/inspection/inspectionPoint.ts @@ -33,6 +33,9 @@ export class inspectionPoint { @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; diff --git a/src/enums/inspectionEnum.ts b/src/enums/inspectionEnum.ts index 5c87e55..58143e6 100644 --- a/src/enums/inspectionEnum.ts +++ b/src/enums/inspectionEnum.ts @@ -2,4 +2,5 @@ export enum InspectionPointEnum { oknok = "oknok", text = "text", number = "number", + file = "file", } diff --git a/src/factory/admin/unit/inspection/inspectionPoint.ts b/src/factory/admin/unit/inspection/inspectionPoint.ts index 1f6150c..b494542 100644 --- a/src/factory/admin/unit/inspection/inspectionPoint.ts +++ b/src/factory/admin/unit/inspection/inspectionPoint.ts @@ -16,6 +16,7 @@ export default abstract class InspectionPointFactory { sort: record.sort, min: record?.min, max: record?.max, + others: record?.others, }; } diff --git a/src/migrations/1752063536385-test.ts b/src/migrations/1752063536385-test.ts new file mode 100644 index 0000000..2760925 --- /dev/null +++ b/src/migrations/1752063536385-test.ts @@ -0,0 +1,44 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class Test1752063536385 implements MigrationInterface { + name = 'Test1752063536385' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","membership_view","public"]); + await queryRunner.query(`DROP VIEW "membership_view"`); + await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","membership_total_view","public"]); + await queryRunner.query(`DROP VIEW "membership_total_view"`); + await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","member_qualifications_view","public"]); + await queryRunner.query(`DROP VIEW "member_qualifications_view"`); + await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","member_executive_positions_view","public"]); + await queryRunner.query(`DROP VIEW "member_executive_positions_view"`); + await queryRunner.query(`CREATE VIEW "member_executive_positions_view" AS SELECT "executivePosition"."id" AS "positionId", "executivePosition"."position" AS "position", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start"))) AS "durationInYears", SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")) AS "exactDuration" FROM "member_executive_positions" "memberExecutivePositions" LEFT JOIN "executive_position" "executivePosition" ON "executivePosition"."id"="memberExecutivePositions"."executivePositionId" LEFT JOIN "member" "member" ON "member"."id"="memberExecutivePositions"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "executivePosition"."id", "member"."id", "salutation"."id"`); + await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","member_executive_positions_view","SELECT \"executivePosition\".\"id\" AS \"positionId\", \"executivePosition\".\"position\" AS \"position\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE) - \"memberExecutivePositions\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE), \"memberExecutivePositions\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE), \"memberExecutivePositions\".\"start\")) AS \"exactDuration\" FROM \"member_executive_positions\" \"memberExecutivePositions\" LEFT JOIN \"executive_position\" \"executivePosition\" ON \"executivePosition\".\"id\"=\"memberExecutivePositions\".\"executivePositionId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"memberExecutivePositions\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"executivePosition\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); + await queryRunner.query(`CREATE VIEW "member_qualifications_view" AS SELECT "qualification"."id" AS "qualificationId", "qualification"."qualification" AS "qualification", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start"))) AS "durationInYears", SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start")) AS "exactDuration" FROM "member_qualifications" "memberQualifications" LEFT JOIN "qualification" "qualification" ON "qualification"."id"="memberQualifications"."qualificationId" LEFT JOIN "member" "member" ON "member"."id"="memberQualifications"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "qualification"."id", "member"."id", "salutation"."id"`); + await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","member_qualifications_view","SELECT \"qualification\".\"id\" AS \"qualificationId\", \"qualification\".\"qualification\" AS \"qualification\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE) - \"memberQualifications\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE), \"memberQualifications\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE), \"memberQualifications\".\"start\")) AS \"exactDuration\" FROM \"member_qualifications\" \"memberQualifications\" LEFT JOIN \"qualification\" \"qualification\" ON \"qualification\".\"id\"=\"memberQualifications\".\"qualificationId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"memberQualifications\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"qualification\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); + await queryRunner.query(`CREATE VIEW "membership_view" AS SELECT "status"."id" AS "statusId", "status"."status" AS "status", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))) AS "durationInYears", SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")) AS "exactDuration" FROM "membership" "membership" LEFT JOIN "membership_status" "status" ON "status"."id"="membership"."statusId" LEFT JOIN "member" "member" ON "member"."id"="membership"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "status"."id", "member"."id", "salutation"."id"`); + await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","membership_view","SELECT \"status\".\"id\" AS \"statusId\", \"status\".\"status\" AS \"status\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"membership\".\"end\", CURRENT_DATE) - \"membership\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\")) AS \"exactDuration\" FROM \"membership\" \"membership\" LEFT JOIN \"membership_status\" \"status\" ON \"status\".\"id\"=\"membership\".\"statusId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"membership\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"status\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); + await queryRunner.query(`CREATE VIEW "membership_total_view" AS SELECT "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))) AS "durationInYears", SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")) AS "exactDuration" FROM "membership" "membership" LEFT JOIN "membership_status" "status" ON "status"."id"="membership"."statusId" LEFT JOIN "member" "member" ON "member"."id"="membership"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "member"."id", "salutation"."id"`); + await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","membership_total_view","SELECT \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"membership\".\"end\", CURRENT_DATE) - \"membership\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\")) AS \"exactDuration\" FROM \"membership\" \"membership\" LEFT JOIN \"membership_status\" \"status\" ON \"status\".\"id\"=\"membership\".\"statusId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"membership\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"member\".\"id\", \"salutation\".\"id\""]); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","membership_total_view","public"]); + await queryRunner.query(`DROP VIEW "membership_total_view"`); + await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","membership_view","public"]); + await queryRunner.query(`DROP VIEW "membership_view"`); + await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","member_qualifications_view","public"]); + await queryRunner.query(`DROP VIEW "member_qualifications_view"`); + await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","member_executive_positions_view","public"]); + await queryRunner.query(`DROP VIEW "member_executive_positions_view"`); + await queryRunner.query(`CREATE VIEW "member_executive_positions_view" AS SELECT "executivePosition"."id" AS "positionId", "executivePosition"."position" AS "position", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start"))) AS "durationInYears", SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")) AS "exactDuration" FROM "member_executive_positions" "memberExecutivePositions" LEFT JOIN "executive_position" "executivePosition" ON "executivePosition"."id"="memberExecutivePositions"."executivePositionId" LEFT JOIN "member" "member" ON "member"."id"="memberExecutivePositions"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "executivePosition"."id", "member"."id", "salutation"."id"`); + await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","member_executive_positions_view","SELECT \"executivePosition\".\"id\" AS \"positionId\", \"executivePosition\".\"position\" AS \"position\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE) - \"memberExecutivePositions\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE), \"memberExecutivePositions\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE), \"memberExecutivePositions\".\"start\")) AS \"exactDuration\" FROM \"member_executive_positions\" \"memberExecutivePositions\" LEFT JOIN \"executive_position\" \"executivePosition\" ON \"executivePosition\".\"id\"=\"memberExecutivePositions\".\"executivePositionId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"memberExecutivePositions\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"executivePosition\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); + await queryRunner.query(`CREATE VIEW "member_qualifications_view" AS SELECT "qualification"."id" AS "qualificationId", "qualification"."qualification" AS "qualification", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start"))) AS "durationInYears", SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start")) AS "exactDuration" FROM "member_qualifications" "memberQualifications" LEFT JOIN "qualification" "qualification" ON "qualification"."id"="memberQualifications"."qualificationId" LEFT JOIN "member" "member" ON "member"."id"="memberQualifications"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "qualification"."id", "member"."id", "salutation"."id"`); + await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","member_qualifications_view","SELECT \"qualification\".\"id\" AS \"qualificationId\", \"qualification\".\"qualification\" AS \"qualification\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE) - \"memberQualifications\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE), \"memberQualifications\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE), \"memberQualifications\".\"start\")) AS \"exactDuration\" FROM \"member_qualifications\" \"memberQualifications\" LEFT JOIN \"qualification\" \"qualification\" ON \"qualification\".\"id\"=\"memberQualifications\".\"qualificationId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"memberQualifications\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"qualification\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); + await queryRunner.query(`CREATE VIEW "membership_total_view" AS SELECT "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))) AS "durationInYears", SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")) AS "exactDuration" FROM "membership" "membership" LEFT JOIN "membership_status" "status" ON "status"."id"="membership"."statusId" LEFT JOIN "member" "member" ON "member"."id"="membership"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "member"."id", "salutation"."id"`); + await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","membership_total_view","SELECT \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"membership\".\"end\", CURRENT_DATE) - \"membership\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\")) AS \"exactDuration\" FROM \"membership\" \"membership\" LEFT JOIN \"membership_status\" \"status\" ON \"status\".\"id\"=\"membership\".\"statusId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"membership\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"member\".\"id\", \"salutation\".\"id\""]); + await queryRunner.query(`CREATE VIEW "membership_view" AS SELECT "status"."id" AS "statusId", "status"."status" AS "status", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))) AS "durationInYears", SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")) AS "exactDuration" FROM "membership" "membership" LEFT JOIN "membership_status" "status" ON "status"."id"="membership"."statusId" LEFT JOIN "member" "member" ON "member"."id"="membership"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "status"."id", "member"."id", "salutation"."id"`); + await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","membership_view","SELECT \"status\".\"id\" AS \"statusId\", \"status\".\"status\" AS \"status\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"membership\".\"end\", CURRENT_DATE) - \"membership\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\")) AS \"exactDuration\" FROM \"membership\" \"membership\" LEFT JOIN \"membership_status\" \"status\" ON \"status\".\"id\"=\"membership\".\"statusId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"membership\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"status\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); + } + +} diff --git a/src/migrations/baseSchemaTables/inspection.ts b/src/migrations/baseSchemaTables/inspection.ts index 87af919..fad995f 100644 --- a/src/migrations/baseSchemaTables/inspection.ts +++ b/src/migrations/baseSchemaTables/inspection.ts @@ -72,6 +72,7 @@ export const inspection_point_table = new Table({ { 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") }, ], diff --git a/src/routes/admin/unit/inspectionPlan.ts b/src/routes/admin/unit/inspectionPlan.ts index 0aaf46a..4fbab19 100644 --- a/src/routes/admin/unit/inspectionPlan.ts +++ b/src/routes/admin/unit/inspectionPlan.ts @@ -6,7 +6,9 @@ import { getAllInspectionPlans, getAllInspectionPlansForRelated, getInspectionPlanById, + getInspectionPointsByPlanId, updateInspectionPlanById, + updateInspectionPointsByPlanId, } from "../../../controller/admin/unit/inspectionPlanController"; var router = express.Router({ mergeParams: true }); @@ -15,6 +17,10 @@ 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( ["/vehicle/:relatedId", "/equipment/:relatedId", "/wearable/:relatedId"], async (req: Request, res: Response) => { @@ -50,6 +56,14 @@ router.patch( } ); +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"), diff --git a/src/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts index 7de9af8..e31ad0e 100644 --- a/src/service/unit/inspection/inspectionService.ts +++ b/src/service/unit/inspection/inspectionService.ts @@ -125,4 +125,22 @@ export default abstract class InspectionService { throw new DatabaseActionException("SELECT", "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 index 55eab31..00f7500 100644 --- a/src/service/unit/inspection/inspectionVersionedPlanService.ts +++ b/src/service/unit/inspection/inspectionVersionedPlanService.ts @@ -62,4 +62,22 @@ export default abstract class InspectionVersionedPlanService { 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/viewmodel/admin/unit/inspection/inspectionPlan.models.ts b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts index 30706a7..67d7957 100644 --- a/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspectionPlan.models.ts @@ -48,5 +48,6 @@ export interface InspectionPointViewModel { type: InspectionPointEnum; min?: number; max?: number; + others?: string; sort: number; } From 8747baaf2e98240c46d638bf5d95b4535fe81dd9 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Thu, 10 Jul 2025 10:49:30 +0200 Subject: [PATCH 27/55] corrections in plan update and fetch --- .../unit/inspection/inspectionPointCommandHandler.ts | 1 + .../inspection/inspectionVersionedPlanCommandHandler.ts | 8 +++++++- .../admin/unit/inspection/inspectionPointResult.ts | 1 - src/service/unit/inspection/inspectionService.ts | 3 +++ src/viewmodel/admin/unit/inspection/inspection.models.ts | 1 - 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/command/unit/inspection/inspectionPointCommandHandler.ts b/src/command/unit/inspection/inspectionPointCommandHandler.ts index 2a46ae1..a704f04 100644 --- a/src/command/unit/inspection/inspectionPointCommandHandler.ts +++ b/src/command/unit/inspection/inspectionPointCommandHandler.ts @@ -51,6 +51,7 @@ export default abstract class InspectionPointCommandHandler { .values( sync.map((s) => ({ ...s, + id: points.some((p) => p.id == s.id) ? s.id : undefined, versionedPlanId, })) ) diff --git a/src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts b/src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts index decbba2..151aa16 100644 --- a/src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts +++ b/src/command/unit/inspection/inspectionVersionedPlanCommandHandler.ts @@ -40,7 +40,13 @@ export default abstract class InspectionVersionedPlanCommandHandler { .into(inspectionPoint) .values( inspectionPoints.map((ip) => ({ - ...ip, + title: ip.title, + description: ip.description, + type: ip.type, + min: ip.min, + max: ip.max, + others: ip.others, + sort: ip.sort, versionedPlanId: returnId, })) ) diff --git a/src/factory/admin/unit/inspection/inspectionPointResult.ts b/src/factory/admin/unit/inspection/inspectionPointResult.ts index b258a6a..e210e57 100644 --- a/src/factory/admin/unit/inspection/inspectionPointResult.ts +++ b/src/factory/admin/unit/inspection/inspectionPointResult.ts @@ -11,7 +11,6 @@ export default abstract class InspectionPointResultFactory { public static mapToSingle(record: inspectionPointResult): InspectionPointResultViewModel { return { inspectionId: record.inspectionId, - inspectionVersionedPlanId: record.inspection.inspectionVersionedPlanId, inspectionPointId: record.inspectionPointId, inspectionPoint: InspectionPointFactory.mapToSingle(record.inspectionPoint), value: record.value, diff --git a/src/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts index e31ad0e..fbbe081 100644 --- a/src/service/unit/inspection/inspectionService.ts +++ b/src/service/unit/inspection/inspectionService.ts @@ -9,6 +9,9 @@ export default abstract class InspectionService { .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") diff --git a/src/viewmodel/admin/unit/inspection/inspection.models.ts b/src/viewmodel/admin/unit/inspection/inspection.models.ts index 4d18e7c..7efdede 100644 --- a/src/viewmodel/admin/unit/inspection/inspection.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspection.models.ts @@ -40,7 +40,6 @@ export type InspectionViewModel = { export interface InspectionPointResultViewModel { inspectionId: string; - inspectionVersionedPlanId: string; inspectionPointId: string; inspectionPoint: InspectionPointViewModel; value: string; From 705297ba5013b17a204cf5078fdd027ba49fbf18 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Thu, 10 Jul 2025 13:22:29 +0200 Subject: [PATCH 28/55] next and running inspections --- .../unit/inspection/inspectionCommand.ts | 2 +- .../inspection/inspectionCommandHandler.ts | 9 ++- .../admin/unit/inspectionController.ts | 11 ++- .../admin/unit/inspection/inspection.ts | 75 ++++++++++++++++++- .../unit/inspection/inspectionService.ts | 32 +++++++- .../unit/inspection/inspection.models.ts | 20 +++++ 6 files changed, 142 insertions(+), 7 deletions(-) diff --git a/src/command/unit/inspection/inspectionCommand.ts b/src/command/unit/inspection/inspectionCommand.ts index 1c020fb..e3a7b9d 100644 --- a/src/command/unit/inspection/inspectionCommand.ts +++ b/src/command/unit/inspection/inspectionCommand.ts @@ -3,7 +3,7 @@ export interface CreateInspectionCommand { nextInspection?: Date; inspectionPlanId: string; relatedId: string; - assigned: "vehicle" | "equipment"; + assigned: "vehicle" | "equipment" | "wearable"; } export interface UpdateInspectionCommand { diff --git a/src/command/unit/inspection/inspectionCommandHandler.ts b/src/command/unit/inspection/inspectionCommandHandler.ts index 34b7bf5..f287625 100644 --- a/src/command/unit/inspection/inspectionCommandHandler.ts +++ b/src/command/unit/inspection/inspectionCommandHandler.ts @@ -1,4 +1,4 @@ -import { Not } from "typeorm"; +import { IsNull, Not } from "typeorm"; import { dataSource } from "../../../data-source"; import { inspection } from "../../../entity/unit/inspection/inspection"; import DatabaseActionException from "../../../exceptions/databaseActionException"; @@ -25,6 +25,12 @@ export default abstract class InspectionCommandHandler { .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 @@ -38,6 +44,7 @@ export default abstract class InspectionCommandHandler { 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) => { diff --git a/src/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts index f98f4c5..fc126cd 100644 --- a/src/controller/admin/unit/inspectionController.ts +++ b/src/controller/admin/unit/inspectionController.ts @@ -24,7 +24,7 @@ export async function getAllInspectionsSortedNotHavingNewer(req: Request, res: R let [inspections, total] = await InspectionService.getAllSortedNotHavingNewer({ offset, count, noLimit }); res.json({ - inspections: InspectionFactory.mapToBase(inspections), + inspections: InspectionFactory.mapToBaseNext(inspections), total: total, offset: offset, count: count, @@ -45,7 +45,7 @@ export async function getAllInspectionsRunning(req: Request, res: Response): Pro let [inspections, total] = await InspectionService.getAllRunning({ offset, count, noLimit }); res.json({ - inspections: InspectionFactory.mapToBase(inspections), + inspections: InspectionFactory.mapToBaseMinified(inspections), total: total, offset: offset, count: count, @@ -112,6 +112,13 @@ export async function createInspection(req: Request, res: Response): Promise): 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/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts index fbbe081..6e4e79b 100644 --- a/src/service/unit/inspection/inspectionService.ts +++ b/src/service/unit/inspection/inspectionService.ts @@ -1,4 +1,4 @@ -import { IsNull } from "typeorm"; +import { IsNull, Not } from "typeorm"; import { dataSource } from "../../../data-source"; import { inspection } from "../../../entity/unit/inspection/inspection"; import DatabaseActionException from "../../../exceptions/databaseActionException"; @@ -129,6 +129,36 @@ export default abstract class InspectionService { }); } + /** + * @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} diff --git a/src/viewmodel/admin/unit/inspection/inspection.models.ts b/src/viewmodel/admin/unit/inspection/inspection.models.ts index 7efdede..87af590 100644 --- a/src/viewmodel/admin/unit/inspection/inspection.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspection.models.ts @@ -38,6 +38,26 @@ export type InspectionViewModel = { 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; From c42a41d89565872c735e94ba5a214481ea5d12a7 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 11 Jul 2025 09:33:37 +0200 Subject: [PATCH 29/55] count instances to type --- src/entity/unit/equipment/equipmentType.ts | 2 ++ src/entity/unit/vehicle/vehicleType.ts | 2 ++ src/entity/unit/wearable/wearableType.ts | 2 ++ src/factory/admin/unit/equipment/equipmentType.ts | 1 + src/factory/admin/unit/vehicle/vehicleType.ts | 1 + src/factory/admin/unit/wearable/wearableType.ts | 1 + src/service/unit/equipment/equipmentTypeService.ts | 5 ++++- src/service/unit/vehicle/vehicleTypeService.ts | 5 ++++- src/service/unit/wearable/wearableTypeService.ts | 5 ++++- src/viewmodel/admin/unit/equipment/equipmentType.models.ts | 1 + src/viewmodel/admin/unit/vehicle/vehicleType.models.ts | 1 + src/viewmodel/admin/unit/wearable/wearableType.models.ts | 1 + 12 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/entity/unit/equipment/equipmentType.ts b/src/entity/unit/equipment/equipmentType.ts index 3d0b134..1739cfa 100644 --- a/src/entity/unit/equipment/equipmentType.ts +++ b/src/entity/unit/equipment/equipmentType.ts @@ -18,4 +18,6 @@ export class equipmentType { @OneToMany(() => inspectionPlan, (ip) => ip.equipmentType) inspectionPlans: inspectionPlan[]; + + equipmentCount: number; } diff --git a/src/entity/unit/vehicle/vehicleType.ts b/src/entity/unit/vehicle/vehicleType.ts index a3e3403..2e83b23 100644 --- a/src/entity/unit/vehicle/vehicleType.ts +++ b/src/entity/unit/vehicle/vehicleType.ts @@ -18,4 +18,6 @@ export class vehicleType { @OneToMany(() => inspectionPlan, (ip) => ip.vehicleType) inspectionPlans: inspectionPlan[]; + + vehicleCount: number; } diff --git a/src/entity/unit/wearable/wearableType.ts b/src/entity/unit/wearable/wearableType.ts index 15ae351..099ed26 100644 --- a/src/entity/unit/wearable/wearableType.ts +++ b/src/entity/unit/wearable/wearableType.ts @@ -18,4 +18,6 @@ export class wearableType { @OneToMany(() => inspectionPlan, (ip) => ip.wearableType) inspectionPlans: inspectionPlan[]; + + wearableCount: number; } diff --git a/src/factory/admin/unit/equipment/equipmentType.ts b/src/factory/admin/unit/equipment/equipmentType.ts index 6e84107..6906215 100644 --- a/src/factory/admin/unit/equipment/equipmentType.ts +++ b/src/factory/admin/unit/equipment/equipmentType.ts @@ -12,6 +12,7 @@ export default abstract class EquipmentTypeFactory { id: record.id, type: record.type, description: record.description, + equipmentCount: record.equipmentCount, }; } diff --git a/src/factory/admin/unit/vehicle/vehicleType.ts b/src/factory/admin/unit/vehicle/vehicleType.ts index 6155ca1..b282b79 100644 --- a/src/factory/admin/unit/vehicle/vehicleType.ts +++ b/src/factory/admin/unit/vehicle/vehicleType.ts @@ -12,6 +12,7 @@ export default abstract class VehicleTypeFactory { id: record.id, type: record.type, description: record.description, + vehicleCount: record.vehicleCount, }; } diff --git a/src/factory/admin/unit/wearable/wearableType.ts b/src/factory/admin/unit/wearable/wearableType.ts index b574bae..89c1066 100644 --- a/src/factory/admin/unit/wearable/wearableType.ts +++ b/src/factory/admin/unit/wearable/wearableType.ts @@ -12,6 +12,7 @@ export default abstract class WearableTypeFactory { id: record.id, type: record.type, description: record.description, + wearableCount: record.wearableCount, }; } diff --git a/src/service/unit/equipment/equipmentTypeService.ts b/src/service/unit/equipment/equipmentTypeService.ts index b85b9f1..16a9527 100644 --- a/src/service/unit/equipment/equipmentTypeService.ts +++ b/src/service/unit/equipment/equipmentTypeService.ts @@ -19,7 +19,10 @@ export default abstract class EquipmentTypeService { search?: string; noLimit?: boolean; }): Promise<[Array, number]> { - let query = dataSource.getRepository(equipmentType).createQueryBuilder("equipmentType"); + let query = dataSource + .getRepository(equipmentType) + .createQueryBuilder("equipmentType") + .loadRelationCountAndMap("equipmentType.equipmentCount", "equipmentType.equipment"); if (search != "") { query = query.where({ diff --git a/src/service/unit/vehicle/vehicleTypeService.ts b/src/service/unit/vehicle/vehicleTypeService.ts index c1dbdf2..438d46b 100644 --- a/src/service/unit/vehicle/vehicleTypeService.ts +++ b/src/service/unit/vehicle/vehicleTypeService.ts @@ -19,7 +19,10 @@ export default abstract class VehicleTypeService { search?: string; noLimit?: boolean; }): Promise<[Array, number]> { - let query = dataSource.getRepository(vehicleType).createQueryBuilder("vehicleType"); + let query = dataSource + .getRepository(vehicleType) + .createQueryBuilder("vehicleType") + .loadRelationCountAndMap("vehicleType.vehicleCount", "vehicleType.vehicle"); if (search != "") { query = query.where({ diff --git a/src/service/unit/wearable/wearableTypeService.ts b/src/service/unit/wearable/wearableTypeService.ts index cd91a26..a9f06f3 100644 --- a/src/service/unit/wearable/wearableTypeService.ts +++ b/src/service/unit/wearable/wearableTypeService.ts @@ -19,7 +19,10 @@ export default abstract class WearableTypeService { search?: string; noLimit?: boolean; }): Promise<[Array, number]> { - let query = dataSource.getRepository(wearableType).createQueryBuilder("wearableType"); + let query = dataSource + .getRepository(wearableType) + .createQueryBuilder("wearableType") + .loadRelationCountAndMap("wearableType.wearableCount", "wearableType.wearable"); if (search != "") { query = query.where({ diff --git a/src/viewmodel/admin/unit/equipment/equipmentType.models.ts b/src/viewmodel/admin/unit/equipment/equipmentType.models.ts index 05ad905..b7a1648 100644 --- a/src/viewmodel/admin/unit/equipment/equipmentType.models.ts +++ b/src/viewmodel/admin/unit/equipment/equipmentType.models.ts @@ -2,4 +2,5 @@ export interface EquipmentTypeViewModel { id: string; type: string; description: string; + equipmentCount: number; } diff --git a/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts b/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts index 726311e..a005853 100644 --- a/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts +++ b/src/viewmodel/admin/unit/vehicle/vehicleType.models.ts @@ -2,4 +2,5 @@ export interface VehicleTypeViewModel { id: string; type: string; description: string; + vehicleCount: number; } diff --git a/src/viewmodel/admin/unit/wearable/wearableType.models.ts b/src/viewmodel/admin/unit/wearable/wearableType.models.ts index 73fd7c5..0c15a26 100644 --- a/src/viewmodel/admin/unit/wearable/wearableType.models.ts +++ b/src/viewmodel/admin/unit/wearable/wearableType.models.ts @@ -2,4 +2,5 @@ export interface WearableTypeViewModel { id: string; type: string; description: string; + wearableCount: number; } From 0f3e4488f4f0cb5c6ac8503e58c91f7194c5d0e4 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 11 Jul 2025 09:42:04 +0200 Subject: [PATCH 30/55] correct type inspectionPlans request --- .../admin/unit/inspectionPlanController.ts | 14 +++++++------- src/routes/admin/unit/inspectionPlan.ts | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/controller/admin/unit/inspectionPlanController.ts b/src/controller/admin/unit/inspectionPlanController.ts index 69e3e1e..1165485 100644 --- a/src/controller/admin/unit/inspectionPlanController.ts +++ b/src/controller/admin/unit/inspectionPlanController.ts @@ -51,8 +51,8 @@ export async function getAllInspectionPlans(req: Request, res: Response): Promis * @returns {Promise<*>} */ export async function getAllInspectionPlansForRelated(req: Request, res: Response): Promise { - let relation = req.params.related as "vehicle" | "equipment" | "wearable"; - let relationId = req.params.relatedId as string; + 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) ?? ""; @@ -60,12 +60,12 @@ export async function getAllInspectionPlansForRelated(req: Request, res: Respons let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); let where; - if (relation == "equipment") { - where = { equipmentTypeId: relationId }; - } else if (relation == "vehicle") { - where = { vehicleTypeId: relationId }; + if (relation == "equipmentType") { + where = { equipmentTypeId: relationTypeId }; + } else if (relation == "vehicleType") { + where = { vehicleTypeId: relationTypeId }; } else { - where = { wearableTypeId: relationId }; + where = { wearableTypeId: relationTypeId }; } let [inspectionPlans, total] = await InspectionPlanService.getAllForRelated(where, { offset, diff --git a/src/routes/admin/unit/inspectionPlan.ts b/src/routes/admin/unit/inspectionPlan.ts index 4fbab19..914d9d3 100644 --- a/src/routes/admin/unit/inspectionPlan.ts +++ b/src/routes/admin/unit/inspectionPlan.ts @@ -22,14 +22,14 @@ router.get("/:id/points", async (req: Request, res: Response) => { }); router.get( - ["/vehicle/:relatedId", "/equipment/:relatedId", "/wearable/:relatedId"], + ["/vehicleType/:relatedTypeId", "/equipmentType/:relatedTypeId", "/wearableType/:relatedTypeId"], async (req: Request, res: Response) => { - if (req.path.startsWith("/vehicle")) { - req.params.related = "vehicle"; - } else if (req.path.startsWith("/equipment")) { - req.params.related = "equipment"; + if (req.path.startsWith("/vehicleType")) { + req.params.related = "vehicleType"; + } else if (req.path.startsWith("/equipmentType")) { + req.params.related = "equipmentType"; } else { - req.params.related = "wearable"; + req.params.related = "wearableType"; } await getAllInspectionPlansForRelated(req, res); From 9ef82adef74f15d3f773a2e16b599d519de6b00e Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 11 Jul 2025 14:02:36 +0200 Subject: [PATCH 31/55] inspection finish and print --- .../unit/inspection/inspectionCommand.ts | 4 + .../inspection/inspectionCommandHandler.ts | 72 ++++++---- .../inspectionPointResultCommand.ts | 2 +- .../inspectionPointResultCommandHandler.ts | 31 ++++- .../admin/unit/inspectionController.ts | 131 +++++++++++++++++- src/middleware/multer.ts | 25 ++++ src/migrations/1749361405703-UnitBase.ts | 21 +++ src/migrations/1752063536385-test.ts | 44 ------ src/routes/admin/unit/inspection.ts | 25 ++++ .../unit/inspection/inspectionService.ts | 16 ++- src/templates/inspection.body.template.html | 62 +++++++++ src/type/templateTypes.ts | 1 + 12 files changed, 358 insertions(+), 76 deletions(-) delete mode 100644 src/migrations/1752063536385-test.ts create mode 100644 src/templates/inspection.body.template.html diff --git a/src/command/unit/inspection/inspectionCommand.ts b/src/command/unit/inspection/inspectionCommand.ts index e3a7b9d..254d690 100644 --- a/src/command/unit/inspection/inspectionCommand.ts +++ b/src/command/unit/inspection/inspectionCommand.ts @@ -12,6 +12,10 @@ export interface UpdateInspectionCommand { 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 index f287625..f6156b3 100644 --- a/src/command/unit/inspection/inspectionCommandHandler.ts +++ b/src/command/unit/inspection/inspectionCommandHandler.ts @@ -4,7 +4,12 @@ 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 } from "./inspectionCommand"; +import { + CreateInspectionCommand, + UpdateInspectionCommand, + DeleteInspectionCommand, + FinishInspectionCommand, +} from "./inspectionCommand"; export default abstract class InspectionCommandHandler { /** @@ -80,6 +85,26 @@ export default abstract class InspectionCommandHandler { }); } + /** + * @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 @@ -89,31 +114,30 @@ export default abstract class InspectionCommandHandler { let deleteInspectionData = await InspectionService.getById(deleteInspection.id); return await dataSource .transaction(async (manager) => { - await manager + let latestInspection = await manager .createQueryBuilder() - .update(inspection) - .set({ - hasNewer: false, + .from(inspection, "sub") + .where({ + inspectionPlanId: deleteInspectionData.inspectionPlanId, + inspectionVersionedPlanId: deleteInspectionData.inspectionVersionedPlanId, + equipmentId: deleteInspectionData.equipmentId ?? IsNull(), + vehicleId: deleteInspectionData.vehicleId ?? IsNull(), + wearableId: deleteInspectionData.wearableId ?? IsNull(), }) - .where((qb) => { - const subQuery = qb - .createQueryBuilder() - .select("id") - .from(inspection, "sub") - .where({ - inspectionPlanId: deleteInspectionData.inspectionPlanId, - inspectionVersionedPlanId: deleteInspectionData.inspectionVersionedPlanId, - equipmentId: deleteInspectionData.equipmentId, - vehicleId: deleteInspectionData.vehicleId, - wearableId: deleteInspectionData.wearableId, - }) - .andWhere({ id: Not(deleteInspection.id) }) - .orderBy("sub.createdAt", "DESC") - .limit(1) - .getQuery(); - return "id = " + subQuery; - }) - .execute(); + .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() diff --git a/src/command/unit/inspection/inspectionPointResultCommand.ts b/src/command/unit/inspection/inspectionPointResultCommand.ts index 9fb36d6..c1893d3 100644 --- a/src/command/unit/inspection/inspectionPointResultCommand.ts +++ b/src/command/unit/inspection/inspectionPointResultCommand.ts @@ -1,4 +1,4 @@ -export interface CreateInspectionPointResultCommand { +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 index 23916dc..8ffeb6a 100644 --- a/src/command/unit/inspection/inspectionPointResultCommandHandler.ts +++ b/src/command/unit/inspection/inspectionPointResultCommandHandler.ts @@ -1,15 +1,17 @@ import { dataSource } from "../../../data-source"; import { inspectionPointResult } from "../../../entity/unit/inspection/inspectionPointResult"; import DatabaseActionException from "../../../exceptions/databaseActionException"; -import { CreateInspectionPointResultCommand } from "./inspectionPointResultCommand"; +import { CreateOrUpdateInspectionPointResultCommand } from "./inspectionPointResultCommand"; export default abstract class InspectionPointResultCommandHandler { /** * @description create inspectionPointResult - * @param {CreateInspectionPointResultCommand} createInspectionPointResult + * @param {CreateOrUpdateInspectionPointResultCommand} createInspectionPointResult * @returns {Promise} */ - static async createOrUpdate(createInspectionPointResult: CreateInspectionPointResultCommand): Promise { + static async createOrUpdate( + createInspectionPointResult: CreateOrUpdateInspectionPointResultCommand + ): Promise { return await dataSource .createQueryBuilder() .insert() @@ -25,7 +27,28 @@ export default abstract class InspectionPointResultCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new DatabaseActionException("CREATE", "inspectionPointResult", 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/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts index fc126cd..9337899 100644 --- a/src/controller/admin/unit/inspectionController.ts +++ b/src/controller/admin/unit/inspectionController.ts @@ -4,11 +4,18 @@ 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"; /** * @description get all inspections sorted by id not having newer inspection @@ -76,13 +83,35 @@ export async function getAllInspectionsForRelated(req: Request, res: Response): let [inspections, total] = await InspectionService.getAllForRelated(where, { offset, count, noLimit }); res.json({ - inspections: InspectionFactory.mapToBase(inspections), + 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 @@ -152,6 +181,106 @@ export async function updateInspectionById(req: Request, res: Response): Promise 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")}`; + + await PdfExport.renderFile({ + template: "inspection", + title, + filename: "printout", + folder: `inspection/${inspection.id}`, + data: { + inspector: `${req.userId}`, + 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: formattedInspection.inspectionVersionedPlan.inspectionPoints + .sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)) + .map((ip) => ({ + title: ip.title, + description: ip.description, + type: ip.type, + min: ip.min, + max: ip.max, + value: + ip.type == InspectionPointEnum.file + ? "siehe Anhang" + : formattedInspection.checks.find((c) => c.inspectionPointId == ip.id).value, + })), + }, + }); + + // TODO: Anhang hinzufügen + + let finish: FinishInspectionCommand = { + id: inspectionId, + }; + await InspectionCommandHandler.finish(finish); + + res.sendStatus(204); +} + /** * @description delete inspection by id * @param req {Request} Express req object 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 index 20e6908..97c657f 100644 --- a/src/migrations/1749361405703-UnitBase.ts +++ b/src/migrations/1749361405703-UnitBase.ts @@ -15,6 +15,8 @@ import { 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"; @@ -36,6 +38,18 @@ export class UnitBase1749361405703 implements MigrationInterface { 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 { @@ -55,5 +69,12 @@ export class UnitBase1749361405703 implements MigrationInterface { 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/1752063536385-test.ts b/src/migrations/1752063536385-test.ts deleted file mode 100644 index 2760925..0000000 --- a/src/migrations/1752063536385-test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class Test1752063536385 implements MigrationInterface { - name = 'Test1752063536385' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","membership_view","public"]); - await queryRunner.query(`DROP VIEW "membership_view"`); - await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","membership_total_view","public"]); - await queryRunner.query(`DROP VIEW "membership_total_view"`); - await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","member_qualifications_view","public"]); - await queryRunner.query(`DROP VIEW "member_qualifications_view"`); - await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","member_executive_positions_view","public"]); - await queryRunner.query(`DROP VIEW "member_executive_positions_view"`); - await queryRunner.query(`CREATE VIEW "member_executive_positions_view" AS SELECT "executivePosition"."id" AS "positionId", "executivePosition"."position" AS "position", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start"))) AS "durationInYears", SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")) AS "exactDuration" FROM "member_executive_positions" "memberExecutivePositions" LEFT JOIN "executive_position" "executivePosition" ON "executivePosition"."id"="memberExecutivePositions"."executivePositionId" LEFT JOIN "member" "member" ON "member"."id"="memberExecutivePositions"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "executivePosition"."id", "member"."id", "salutation"."id"`); - await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","member_executive_positions_view","SELECT \"executivePosition\".\"id\" AS \"positionId\", \"executivePosition\".\"position\" AS \"position\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE) - \"memberExecutivePositions\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE), \"memberExecutivePositions\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE), \"memberExecutivePositions\".\"start\")) AS \"exactDuration\" FROM \"member_executive_positions\" \"memberExecutivePositions\" LEFT JOIN \"executive_position\" \"executivePosition\" ON \"executivePosition\".\"id\"=\"memberExecutivePositions\".\"executivePositionId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"memberExecutivePositions\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"executivePosition\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); - await queryRunner.query(`CREATE VIEW "member_qualifications_view" AS SELECT "qualification"."id" AS "qualificationId", "qualification"."qualification" AS "qualification", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start"))) AS "durationInYears", SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start")) AS "exactDuration" FROM "member_qualifications" "memberQualifications" LEFT JOIN "qualification" "qualification" ON "qualification"."id"="memberQualifications"."qualificationId" LEFT JOIN "member" "member" ON "member"."id"="memberQualifications"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "qualification"."id", "member"."id", "salutation"."id"`); - await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","member_qualifications_view","SELECT \"qualification\".\"id\" AS \"qualificationId\", \"qualification\".\"qualification\" AS \"qualification\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE) - \"memberQualifications\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE), \"memberQualifications\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE), \"memberQualifications\".\"start\")) AS \"exactDuration\" FROM \"member_qualifications\" \"memberQualifications\" LEFT JOIN \"qualification\" \"qualification\" ON \"qualification\".\"id\"=\"memberQualifications\".\"qualificationId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"memberQualifications\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"qualification\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); - await queryRunner.query(`CREATE VIEW "membership_view" AS SELECT "status"."id" AS "statusId", "status"."status" AS "status", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))) AS "durationInYears", SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")) AS "exactDuration" FROM "membership" "membership" LEFT JOIN "membership_status" "status" ON "status"."id"="membership"."statusId" LEFT JOIN "member" "member" ON "member"."id"="membership"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "status"."id", "member"."id", "salutation"."id"`); - await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","membership_view","SELECT \"status\".\"id\" AS \"statusId\", \"status\".\"status\" AS \"status\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"membership\".\"end\", CURRENT_DATE) - \"membership\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\")) AS \"exactDuration\" FROM \"membership\" \"membership\" LEFT JOIN \"membership_status\" \"status\" ON \"status\".\"id\"=\"membership\".\"statusId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"membership\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"status\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); - await queryRunner.query(`CREATE VIEW "membership_total_view" AS SELECT "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))) AS "durationInYears", SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")) AS "exactDuration" FROM "membership" "membership" LEFT JOIN "membership_status" "status" ON "status"."id"="membership"."statusId" LEFT JOIN "member" "member" ON "member"."id"="membership"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "member"."id", "salutation"."id"`); - await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","membership_total_view","SELECT \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"membership\".\"end\", CURRENT_DATE) - \"membership\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\")) AS \"exactDuration\" FROM \"membership\" \"membership\" LEFT JOIN \"membership_status\" \"status\" ON \"status\".\"id\"=\"membership\".\"statusId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"membership\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"member\".\"id\", \"salutation\".\"id\""]); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","membership_total_view","public"]); - await queryRunner.query(`DROP VIEW "membership_total_view"`); - await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","membership_view","public"]); - await queryRunner.query(`DROP VIEW "membership_view"`); - await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","member_qualifications_view","public"]); - await queryRunner.query(`DROP VIEW "member_qualifications_view"`); - await queryRunner.query(`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, ["VIEW","member_executive_positions_view","public"]); - await queryRunner.query(`DROP VIEW "member_executive_positions_view"`); - await queryRunner.query(`CREATE VIEW "member_executive_positions_view" AS SELECT "executivePosition"."id" AS "positionId", "executivePosition"."position" AS "position", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start"))) AS "durationInYears", SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")) AS "exactDuration" FROM "member_executive_positions" "memberExecutivePositions" LEFT JOIN "executive_position" "executivePosition" ON "executivePosition"."id"="memberExecutivePositions"."executivePositionId" LEFT JOIN "member" "member" ON "member"."id"="memberExecutivePositions"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "executivePosition"."id", "member"."id", "salutation"."id"`); - await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","member_executive_positions_view","SELECT \"executivePosition\".\"id\" AS \"positionId\", \"executivePosition\".\"position\" AS \"position\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE) - \"memberExecutivePositions\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE), \"memberExecutivePositions\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"memberExecutivePositions\".\"end\", CURRENT_DATE), \"memberExecutivePositions\".\"start\")) AS \"exactDuration\" FROM \"member_executive_positions\" \"memberExecutivePositions\" LEFT JOIN \"executive_position\" \"executivePosition\" ON \"executivePosition\".\"id\"=\"memberExecutivePositions\".\"executivePositionId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"memberExecutivePositions\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"executivePosition\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); - await queryRunner.query(`CREATE VIEW "member_qualifications_view" AS SELECT "qualification"."id" AS "qualificationId", "qualification"."qualification" AS "qualification", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start"))) AS "durationInYears", SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start")) AS "exactDuration" FROM "member_qualifications" "memberQualifications" LEFT JOIN "qualification" "qualification" ON "qualification"."id"="memberQualifications"."qualificationId" LEFT JOIN "member" "member" ON "member"."id"="memberQualifications"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "qualification"."id", "member"."id", "salutation"."id"`); - await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","member_qualifications_view","SELECT \"qualification\".\"id\" AS \"qualificationId\", \"qualification\".\"qualification\" AS \"qualification\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE) - \"memberQualifications\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE), \"memberQualifications\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"memberQualifications\".\"end\", CURRENT_DATE), \"memberQualifications\".\"start\")) AS \"exactDuration\" FROM \"member_qualifications\" \"memberQualifications\" LEFT JOIN \"qualification\" \"qualification\" ON \"qualification\".\"id\"=\"memberQualifications\".\"qualificationId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"memberQualifications\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"qualification\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); - await queryRunner.query(`CREATE VIEW "membership_total_view" AS SELECT "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))) AS "durationInYears", SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")) AS "exactDuration" FROM "membership" "membership" LEFT JOIN "membership_status" "status" ON "status"."id"="membership"."statusId" LEFT JOIN "member" "member" ON "member"."id"="membership"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "member"."id", "salutation"."id"`); - await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","membership_total_view","SELECT \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"membership\".\"end\", CURRENT_DATE) - \"membership\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\")) AS \"exactDuration\" FROM \"membership\" \"membership\" LEFT JOIN \"membership_status\" \"status\" ON \"status\".\"id\"=\"membership\".\"statusId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"membership\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"member\".\"id\", \"salutation\".\"id\""]); - await queryRunner.query(`CREATE VIEW "membership_view" AS SELECT "status"."id" AS "statusId", "status"."status" AS "status", "member"."id" AS "memberId", "member"."firstname" AS "memberFirstname", "member"."lastname" AS "memberLastname", "member"."nameaffix" AS "memberNameaffix", "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") AS "durationInDays", SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))) AS "durationInYears", SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")) AS "exactDuration" FROM "membership" "membership" LEFT JOIN "membership_status" "status" ON "status"."id"="membership"."statusId" LEFT JOIN "member" "member" ON "member"."id"="membership"."memberId" LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId" GROUP BY "status"."id", "member"."id", "salutation"."id"`); - await queryRunner.query(`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, ["public","VIEW","membership_view","SELECT \"status\".\"id\" AS \"statusId\", \"status\".\"status\" AS \"status\", \"member\".\"id\" AS \"memberId\", \"member\".\"firstname\" AS \"memberFirstname\", \"member\".\"lastname\" AS \"memberLastname\", \"member\".\"nameaffix\" AS \"memberNameaffix\", \"member\".\"birthdate\" AS \"memberBirthdate\", \"salutation\".\"salutation\" AS \"memberSalutation\", SUM(COALESCE(\"membership\".\"end\", CURRENT_DATE) - \"membership\".\"start\") AS \"durationInDays\", SUM(EXTRACT(YEAR FROM AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\"))) AS \"durationInYears\", SUM(AGE(COALESCE(\"membership\".\"end\", CURRENT_DATE), \"membership\".\"start\")) AS \"exactDuration\" FROM \"membership\" \"membership\" LEFT JOIN \"membership_status\" \"status\" ON \"status\".\"id\"=\"membership\".\"statusId\" LEFT JOIN \"member\" \"member\" ON \"member\".\"id\"=\"membership\".\"memberId\" LEFT JOIN \"salutation\" \"salutation\" ON \"salutation\".\"id\"=\"member\".\"salutationId\" GROUP BY \"status\".\"id\", \"member\".\"id\", \"salutation\".\"id\""]); - } - -} diff --git a/src/routes/admin/unit/inspection.ts b/src/routes/admin/unit/inspection.ts index 57f6883..e57e1cd 100644 --- a/src/routes/admin/unit/inspection.ts +++ b/src/routes/admin/unit/inspection.ts @@ -8,7 +8,11 @@ import { getInspectionById, updateInspectionById, getAllInspectionsRunning, + updateInspectionResults, + getInspectionPrintoutById, + finishInspection, } from "../../../controller/admin/unit/inspectionController"; +import { inspectionFileUpload } from "../../../middleware/multer"; var router = express.Router({ mergeParams: true }); @@ -39,6 +43,10 @@ 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.post( "/", PermissionHelper.passCheckMiddleware("create", "unit", "inspection"), @@ -55,6 +63,23 @@ router.patch( } ); +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"), diff --git a/src/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts index 6e4e79b..5e85f83 100644 --- a/src/service/unit/inspection/inspectionService.ts +++ b/src/service/unit/inspection/inspectionService.ts @@ -20,6 +20,18 @@ export default abstract class InspectionService { .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>} @@ -63,7 +75,7 @@ export default abstract class InspectionService { count?: number; noLimit?: boolean; }): Promise<[Array, number]> { - let query = this.query().where({ finishedAt: IsNull() }); + let query = this.minifiedQuery().where({ finishedAt: IsNull() }); if (!noLimit) { query = query.offset(offset).limit(count); @@ -96,7 +108,7 @@ export default abstract class InspectionService { noLimit?: boolean; } ): Promise<[Array, number]> { - let query = this.query().where(where); + let query = this.minifiedQuery().where(where); if (!noLimit) { query = query.offset(offset).limit(count); diff --git a/src/templates/inspection.body.template.html b/src/templates/inspection.body.template.html new file mode 100644 index 0000000..0d243ab --- /dev/null +++ b/src/templates/inspection.body.template.html @@ -0,0 +1,62 @@ + + + + + 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}} +
+ {{this.description}} +
{{this.value}}
+ + + 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", ]; From 98ce39efc5f9a6425e342d1b91c87b5477f76f18 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 12 Jul 2025 16:38:14 +0200 Subject: [PATCH 32/55] print file with appendix --- .../admin/unit/inspectionController.ts | 94 +++++++++++++++---- src/handlebars.config.ts | 4 + src/helpers/fileSystemHelper.ts | 2 +- src/helpers/pdfExport.ts | 2 +- src/index.ts | 2 + src/middleware/authenticate.ts | 2 + src/templates/inspection.body.template.html | 15 ++- 7 files changed, 97 insertions(+), 24 deletions(-) diff --git a/src/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts index 9337899..1b35af0 100644 --- a/src/controller/admin/unit/inspectionController.ts +++ b/src/controller/admin/unit/inspectionController.ts @@ -16,6 +16,8 @@ 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"; /** * @description get all inspections sorted by id not having newer inspection @@ -240,13 +242,37 @@ export async function finishInspection(req: Request, res: Response): Promise (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, - filename: "printout", - folder: `inspection/${inspection.id}`, + saveToDisk: false, data: { - inspector: `${req.userId}`, + inspector: `${req.lastname}, ${req.firstname}`, context: formattedInspection.context || "---", createdAt: formattedInspection.created, finishedAt: formattedInspection.finished ?? new Date(), @@ -255,23 +281,55 @@ export async function finishInspection(req: Request, res: Response): Promise (a.sort ?? 0) - (b.sort ?? 0)) - .map((ip) => ({ - title: ip.title, - description: ip.description, - type: ip.type, - min: ip.min, - max: ip.max, - value: - ip.type == InspectionPointEnum.file - ? "siehe Anhang" - : formattedInspection.checks.find((c) => c.inspectionPointId == ip.id).value, - })), + checks: inspectionPoints, }, }); - // TODO: Anhang hinzufügen + 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, 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/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/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/templates/inspection.body.template.html b/src/templates/inspection.body.template.html index 0d243ab..cc2a6e5 100644 --- a/src/templates/inspection.body.template.html +++ b/src/templates/inspection.body.template.html @@ -17,16 +17,23 @@

Kontext: {{context}}


-

Prüfpunkte:

+

Prüfpunkte:

{{#each checks}} - + - {{/each}}
+ {{this.title}}
- {{this.description}} + {{#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}} {{this.value}}
From 9a1bf6dfde04a6d1ab6abb0b54bfa83359346553 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 12 Jul 2025 17:04:29 +0200 Subject: [PATCH 33/55] provide files for viewing of uploaded files to inspection points --- .../admin/unit/inspectionController.ts | 30 +++++++++++++++++++ src/routes/admin/unit/inspection.ts | 5 ++++ .../inspectionPointResultService.ts | 21 +++++++++++++ 3 files changed, 56 insertions(+) diff --git a/src/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts index 1b35af0..f91b3d0 100644 --- a/src/controller/admin/unit/inspectionController.ts +++ b/src/controller/admin/unit/inspectionController.ts @@ -18,6 +18,8 @@ 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 @@ -114,6 +116,34 @@ export async function getInspectionPrintoutById(req: Request, res: Response): Pr }); } +/** + * @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 diff --git a/src/routes/admin/unit/inspection.ts b/src/routes/admin/unit/inspection.ts index e57e1cd..6a43526 100644 --- a/src/routes/admin/unit/inspection.ts +++ b/src/routes/admin/unit/inspection.ts @@ -11,6 +11,7 @@ import { updateInspectionResults, getInspectionPrintoutById, finishInspection, + getInspectionPointUpload, } from "../../../controller/admin/unit/inspectionController"; import { inspectionFileUpload } from "../../../middleware/multer"; @@ -47,6 +48,10 @@ 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"), diff --git a/src/service/unit/inspection/inspectionPointResultService.ts b/src/service/unit/inspection/inspectionPointResultService.ts index d294139..deb56a8 100644 --- a/src/service/unit/inspection/inspectionPointResultService.ts +++ b/src/service/unit/inspection/inspectionPointResultService.ts @@ -21,4 +21,25 @@ export default abstract class InspectionPointResultService { 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); + }); + } } From a69c3e048e7b42b5e3e810866b165d6ac4a1abdc Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Mon, 14 Jul 2025 15:28:57 +0200 Subject: [PATCH 34/55] enable backups for unit --- src/entity/unit/damageReport.ts | 14 ++--- src/entity/unit/equipment/equipment.ts | 6 ++- src/entity/unit/equipment/equipmentType.ts | 2 +- src/entity/unit/inspection/inspection.ts | 2 +- src/entity/unit/maintenance.ts | 6 +-- src/entity/unit/vehicle/vehicle.ts | 6 ++- src/entity/unit/vehicle/vehicleType.ts | 2 +- src/entity/unit/wearable/wearable.ts | 6 ++- src/entity/unit/wearable/wearableType.ts | 2 +- src/helpers/backupHelper.ts | 60 ++++++++++++++++++++-- 10 files changed, 85 insertions(+), 21 deletions(-) diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts index f080e38..0a4fd7e 100644 --- a/src/entity/unit/damageReport.ts +++ b/src/entity/unit/damageReport.ts @@ -30,37 +30,37 @@ export class damageReport { @Column({ nullable: true, default: null }) equipmentId?: string; - @Column({ nullable: true, default: null }) - maintenanceId?: string; - @Column({ nullable: true, default: null }) vehicleId?: string; @Column({ nullable: true, default: null }) wearableId?: string; - @ManyToOne(() => equipment, { + @Column({ nullable: true, default: null }) + maintenanceId?: string; + + @ManyToOne(() => equipment, (e) => e.reports, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) equipment?: equipment; - @ManyToOne(() => vehicle, { + @ManyToOne(() => vehicle, (v) => v.reports, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) vehicle?: vehicle; - @ManyToOne(() => wearable, { + @ManyToOne(() => wearable, (w) => w.reports, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) wearable?: wearable; - @ManyToOne(() => maintenance, { + @ManyToOne(() => maintenance, (m) => m.reports, { nullable: true, onDelete: "SET NULL", onUpdate: "RESTRICT", diff --git a/src/entity/unit/equipment/equipment.ts b/src/entity/unit/equipment/equipment.ts index bbd12a0..2d3b022 100644 --- a/src/entity/unit/equipment/equipment.ts +++ b/src/entity/unit/equipment/equipment.ts @@ -3,6 +3,7 @@ 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 { @@ -37,6 +38,9 @@ export class equipment { @OneToMany(() => damageReport, (d) => d.equipment, { cascade: ["insert"] }) reports: damageReport[]; - @OneToMany(() => inspection, (i) => i.equipment) + @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 index 1739cfa..40c363b 100644 --- a/src/entity/unit/equipment/equipmentType.ts +++ b/src/entity/unit/equipment/equipmentType.ts @@ -16,7 +16,7 @@ export class equipmentType { @OneToMany(() => equipment, (e) => e.equipmentType, { cascade: ["insert"] }) equipment: equipment[]; - @OneToMany(() => inspectionPlan, (ip) => ip.equipmentType) + @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 index f49b92d..eff01ec 100644 --- a/src/entity/unit/inspection/inspection.ts +++ b/src/entity/unit/inspection/inspection.ts @@ -77,6 +77,6 @@ export class inspection { }) wearable: wearable; - @OneToMany(() => inspectionPointResult, (ipr) => ipr.inspection) + @OneToMany(() => inspectionPointResult, (ipr) => ipr.inspection, { cascade: ["insert"] }) pointResults: inspectionPointResult[]; } diff --git a/src/entity/unit/maintenance.ts b/src/entity/unit/maintenance.ts index f3c9669..3c9b7c0 100644 --- a/src/entity/unit/maintenance.ts +++ b/src/entity/unit/maintenance.ts @@ -30,21 +30,21 @@ export class maintenance { @Column({ nullable: true, default: null }) wearableId?: string; - @ManyToOne(() => equipment, { + @ManyToOne(() => equipment, (e) => e.maintenances, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) equipment?: equipment; - @ManyToOne(() => vehicle, { + @ManyToOne(() => vehicle, (v) => v.maintenances, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", }) vehicle?: vehicle; - @ManyToOne(() => wearable, { + @ManyToOne(() => wearable, (w) => w.maintenances, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", diff --git a/src/entity/unit/vehicle/vehicle.ts b/src/entity/unit/vehicle/vehicle.ts index c09a28d..3f75651 100644 --- a/src/entity/unit/vehicle/vehicle.ts +++ b/src/entity/unit/vehicle/vehicle.ts @@ -3,6 +3,7 @@ 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 { @@ -37,6 +38,9 @@ export class vehicle { @OneToMany(() => damageReport, (d) => d.vehicle, { cascade: ["insert"] }) reports: damageReport[]; - @OneToMany(() => inspection, (i) => i.vehicle) + @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 index 2e83b23..2ed071a 100644 --- a/src/entity/unit/vehicle/vehicleType.ts +++ b/src/entity/unit/vehicle/vehicleType.ts @@ -16,7 +16,7 @@ export class vehicleType { @OneToMany(() => vehicle, (e) => e.vehicleType, { cascade: ["insert"] }) vehicle: vehicle[]; - @OneToMany(() => inspectionPlan, (ip) => ip.vehicleType) + @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 index 902d23a..2c5ce44 100644 --- a/src/entity/unit/wearable/wearable.ts +++ b/src/entity/unit/wearable/wearable.ts @@ -4,6 +4,7 @@ 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 { @@ -48,6 +49,9 @@ export class wearable { @OneToMany(() => damageReport, (d) => d.wearable, { cascade: ["insert"] }) reports: damageReport[]; - @OneToMany(() => inspection, (i) => i.wearable) + @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 index 099ed26..a7d7538 100644 --- a/src/entity/unit/wearable/wearableType.ts +++ b/src/entity/unit/wearable/wearableType.ts @@ -16,7 +16,7 @@ export class wearableType { @OneToMany(() => wearable, (e) => e.wearableType, { cascade: ["insert"] }) wearable: wearable[]; - @OneToMany(() => inspectionPlan, (ip) => ip.wearableType) + @OneToMany(() => inspectionPlan, (ip) => ip.wearableType, { cascade: ["insert"] }) inspectionPlans: inspectionPlan[]; wearableCount: number; 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"]); + } } From b29cdae08855972916dad685f9cd1f8d6877be53 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 15 Jul 2025 11:52:58 +0200 Subject: [PATCH 35/55] add basic socketio to server --- src/index.ts | 6 ++- src/middleware/authenticateSocket.ts | 54 +++++++++++++++++++++ src/middleware/checkSocketExists.ts | 11 +++++ src/storage/socketMap.ts | 35 ++++++++++++++ src/websocket/base.ts | 35 ++++++++++++++ src/websocket/handleEvent.ts | 72 ++++++++++++++++++++++++++++ src/websocket/index.ts | 33 +++++++++++++ src/websocket/scanner/index.ts | 7 +++ 8 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 src/middleware/authenticateSocket.ts create mode 100644 src/middleware/checkSocketExists.ts create mode 100644 src/storage/socketMap.ts create mode 100644 src/websocket/base.ts create mode 100644 src/websocket/handleEvent.ts create mode 100644 src/websocket/index.ts create mode 100644 src/websocket/scanner/index.ts diff --git a/src/index.ts b/src/index.ts index 1e28a8c..250ef17 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import "dotenv/config"; import "./handlebars.config"; import express from "express"; +import { createServer } from "http"; import { configCheck } from "./env.defaults"; configCheck(); @@ -37,7 +38,10 @@ dataSource.initialize().then(async () => { const app = express(); import router from "./routes/index"; router(app); -app.listen(process.env.NODE_ENV ? process.env.SERVER_PORT ?? 5000 : 5000, () => { +const httpServer = createServer(app); +import SocketServer from "./websocket"; +SocketServer.init(httpServer); +httpServer.listen(process.env.NODE_ENV ? process.env.SERVER_PORT ?? 5000 : 5000, () => { console.log( `${new Date().toISOString()}: listening on port ${process.env.NODE_ENV ? process.env.SERVER_PORT ?? 5000 : 5000}` ); diff --git a/src/middleware/authenticateSocket.ts b/src/middleware/authenticateSocket.ts new file mode 100644 index 0000000..fe8af95 --- /dev/null +++ b/src/middleware/authenticateSocket.ts @@ -0,0 +1,54 @@ +import jwt from "jsonwebtoken"; +import BadRequestException from "../exceptions/badRequestException"; +import InternalException from "../exceptions/internalException"; +import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException"; +import { JWTHelper } from "../helpers/jwtHelper"; +import { SocketMap } from "../storage/socketMap"; +import { Socket } from "socket.io"; + +export default async function authenticateSocket(socket: Socket, next: Function) { + try { + const token = socket.handshake.auth.token; + + if (!token) { + throw new BadRequestException("Provide valid Authorization Header"); + } + + let decoded: string | jwt.JwtPayload; + await JWTHelper.validate(token) + .then((result) => { + decoded = result; + }) + .catch((err) => { + if (err == "jwt expired") { + throw new UnauthorizedRequestException("Token expired", err); + } else { + throw new BadRequestException("Failed Authorization Header decoding", err); + } + }); + + if (typeof decoded == "string" || !decoded) { + throw new InternalException("process failed"); + } + + if (decoded?.sub == "api_token_retrieve") { + throw new BadRequestException("This token is only authorized to get temporary access tokens via GET /api/webapi"); + } + + SocketMap.write(socket.id, { + socketId: socket.id, + userId: decoded.userId, + username: decoded.username, + firstname: decoded.firstname, + lastname: decoded.lastname, + isOwner: decoded.isOwner, + permissions: decoded.permissions, + isWebApiRequest: decoded?.sub == "webapi_access_token", + }); + socket.join("home"); + + next(); + } catch (err) { + next(err); + } +} diff --git a/src/middleware/checkSocketExists.ts b/src/middleware/checkSocketExists.ts new file mode 100644 index 0000000..db43554 --- /dev/null +++ b/src/middleware/checkSocketExists.ts @@ -0,0 +1,11 @@ +import { Event, Socket } from "socket.io"; +import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException"; +import { SocketMap } from "../storage/socketMap"; + +export default async (socket: Socket, [event, ...args]: Event, next: any) => { + if (SocketMap.exists(socket.id)) { + next(); + } else { + next(new UnauthorizedRequestException("not authorized for connection")); + } +}; diff --git a/src/storage/socketMap.ts b/src/storage/socketMap.ts new file mode 100644 index 0000000..e5bfeec --- /dev/null +++ b/src/storage/socketMap.ts @@ -0,0 +1,35 @@ +import { PermissionObject } from "../type/permissionTypes"; + +export interface socketStoreModel { + socketId: string; + userId: string; + username: string; + firstname: string; + lastname: string; + isOwner: string; + permissions: PermissionObject; + isWebApiRequest: boolean; +} + +/** + * @description store credentials to socket to prevent auth data change while connected + */ +export abstract class SocketMap { + private static store = new Map(); + + public static write(identifier: string, data: socketStoreModel, overwrite: boolean = false): void { + if (!this.exists(identifier) || overwrite) this.store.set(identifier, data); + } + + public static read(identifier: string): socketStoreModel { + return this.store.get(identifier); + } + + public static exists(identifier: string): boolean { + return this.store.has(identifier); + } + + public static delete(identifier: string): void { + this.store.delete(identifier); + } +} diff --git a/src/websocket/base.ts b/src/websocket/base.ts new file mode 100644 index 0000000..8243717 --- /dev/null +++ b/src/websocket/base.ts @@ -0,0 +1,35 @@ +import { Server, Socket } from "socket.io"; +import CustomRequestException from "../exceptions/customRequestException"; +import { SocketMap } from "../storage/socketMap"; + +export default (io: Server, socket: Socket) => { + socket.on("ping", (callback: Function) => { + callback(); + }); + + socket.on("error", (err) => { + let status = 500; + let msg = "Internal Server Error"; + + if (err instanceof CustomRequestException) { + status = err.statusCode; + msg = err.message; + } + + if (err instanceof CustomRequestException) { + console.log("WS Custom Handler", status, msg); + } else { + console.log("WS Error Handler", err); + } + + socket.emit("error", msg); + }); + + socket.on("disconnecting", () => { + console.log("socket disconnection: ", socket.id); + + SocketMap.delete(socket.id); + }); + + socket.on("disconnect", () => {}); +}; diff --git a/src/websocket/handleEvent.ts b/src/websocket/handleEvent.ts new file mode 100644 index 0000000..0a484f2 --- /dev/null +++ b/src/websocket/handleEvent.ts @@ -0,0 +1,72 @@ +import { Server, Socket } from "socket.io"; +import { PermissionObject, PermissionType, PermissionSection, PermissionModule } from "../type/permissionTypes"; +import PermissionHelper from "../helpers/permissionHelper"; +import ForbiddenRequestException from "../exceptions/forbiddenRequestException"; +import { SocketMap } from "../storage/socketMap"; + +export type EventResponseType = { + answer: any; + type: + | `package-${string}` + | `status-${string}:${string}` + | "display" + | "warning" + | "reload" + | "deleted" + | "action required"; + room?: string; +}; + +type PermissionPass = + | { + type: PermissionType; + section: PermissionSection; + module?: PermissionModule; + } + | "admin"; + +export let handleEvent = ( + permissions: PermissionPass, + handler: (...args: any[]) => Promise, + socket: Socket +) => { + return async (...args: any[]) => { + try { + const socketData = SocketMap.read(socket.id); + if (permissions == "admin") { + if (!socketData.isOwner && !socketData.permissions.admin) { + throw new ForbiddenRequestException(`missing admin permission`); + } + } else { + if ( + !socketData.isOwner && + !PermissionHelper.can(socketData.permissions, permissions.type, permissions.section, permissions.module) + ) { + throw new ForbiddenRequestException(`missing required permission`); + } + } + + const { answer, type, room } = await handler(...args); + if (room === undefined || room == "") { + socket.emit(type, answer); + } else { + socket.to(room).emit(type, answer); + } + } catch (e) { + socket.emit("error", e.message); + } + }; +}; + +/** + socket.on( + "event", + { permissions } + handleResponse( + async (data:any) => { + throw new Error("failed") + }, + socket, + ) + ); + */ diff --git a/src/websocket/index.ts b/src/websocket/index.ts new file mode 100644 index 0000000..aac4c17 --- /dev/null +++ b/src/websocket/index.ts @@ -0,0 +1,33 @@ +import helmet from "helmet"; +import { Server as httpServerType } from "http"; +import { Server } from "socket.io"; +import authenticateSocket from "../middleware/authenticateSocket"; +import checkSocketExists from "../middleware/checkSocketExists"; +import base from "./base"; + +export default abstract class SocketServer { + private static io: Server; + + public static init(httpServer: httpServerType) { + this.io = new Server(httpServer, { + cors: { + origin: "*", + methods: ["GET", "POST"], + credentials: true, + }, + }); + + this.io.engine.use(helmet()); + + this.io + .of("/scanner") + .use(authenticateSocket) + .on("connection", (socket) => { + console.log("socket connection: ", socket.id); + socket.use((packet, next) => authenticateSocket(socket, next)); + socket.use((packet, next) => checkSocketExists(socket, packet, next)); + + base(this.io, socket); + }); + } +} diff --git a/src/websocket/scanner/index.ts b/src/websocket/scanner/index.ts new file mode 100644 index 0000000..c588e2c --- /dev/null +++ b/src/websocket/scanner/index.ts @@ -0,0 +1,7 @@ +import { Server, Socket } from "socket.io"; + +export default (io: Server, socket: Socket) => { + socket.on("disconnecting", () => { + // tell public client, that host left - connection will be closed + }); +}; From 83ab0c4ea768ef4ff0e7796b5660c897f73a08d4 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 15 Jul 2025 15:16:11 +0200 Subject: [PATCH 36/55] admin side scan connection --- package-lock.json | 48 +++++++++++++++++++++++ package.json | 1 + src/enums/socketEnum.ts | 4 ++ src/middleware/authenticateSocket.ts | 1 - src/websocket/handleEvent.ts | 26 ++++++++++++- src/websocket/index.ts | 24 +++++++++++- src/websocket/scanner/index.ts | 58 +++++++++++++++++++++++++++- 7 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 src/enums/socketEnum.ts diff --git a/package-lock.json b/package-lock.json index cfbc2a5..e0c92d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "validator": "^13.15.15" }, "devDependencies": { + "@socket.io/admin-ui": "^0.5.1", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/ip": "^1.1.3", @@ -764,6 +765,39 @@ "node": ">=18" } }, + "node_modules/@socket.io/admin-ui": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@socket.io/admin-ui/-/admin-ui-0.5.1.tgz", + "integrity": "sha512-1dlGL2FGm6T+uL1e6iDvbo2eCINwvW7iVbjIblwh5kPPRM1SP8lmZrbFZf4QNJ/cqQ+JLcx49eXGM9WAB4TK7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bcryptjs": "^2.4.2", + "bcryptjs": "^2.4.3", + "debug": "~4.3.1" + }, + "peerDependencies": { + "socket.io": ">=3.1.0" + } + }, + "node_modules/@socket.io/admin-ui/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", @@ -821,6 +855,13 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -1421,6 +1462,13 @@ "node": ">=10.0.0" } }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "dev": true, + "license": "MIT" + }, "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", diff --git a/package.json b/package.json index cf7f32c..695a152 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "validator": "^13.15.15" }, "devDependencies": { + "@socket.io/admin-ui": "^0.5.1", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/ip": "^1.1.3", diff --git a/src/enums/socketEnum.ts b/src/enums/socketEnum.ts new file mode 100644 index 0000000..4aed946 --- /dev/null +++ b/src/enums/socketEnum.ts @@ -0,0 +1,4 @@ +export enum SocketConnectionTypes { + scanner = "/scanner", + pscanner = "/public_scanner", +} diff --git a/src/middleware/authenticateSocket.ts b/src/middleware/authenticateSocket.ts index fe8af95..d8718c4 100644 --- a/src/middleware/authenticateSocket.ts +++ b/src/middleware/authenticateSocket.ts @@ -45,7 +45,6 @@ export default async function authenticateSocket(socket: Socket, next: Function) permissions: decoded.permissions, isWebApiRequest: decoded?.sub == "webapi_access_token", }); - socket.join("home"); next(); } catch (err) { diff --git a/src/websocket/handleEvent.ts b/src/websocket/handleEvent.ts index 0a484f2..c36c601 100644 --- a/src/websocket/handleEvent.ts +++ b/src/websocket/handleEvent.ts @@ -3,6 +3,8 @@ import { PermissionObject, PermissionType, PermissionSection, PermissionModule } import PermissionHelper from "../helpers/permissionHelper"; import ForbiddenRequestException from "../exceptions/forbiddenRequestException"; import { SocketMap } from "../storage/socketMap"; +import { SocketConnectionTypes } from "../enums/socketEnum"; +import SocketServer from "."; export type EventResponseType = { answer: any; @@ -23,7 +25,8 @@ type PermissionPass = section: PermissionSection; module?: PermissionModule; } - | "admin"; + | "admin" + | "noPermissionsRequired"; export let handleEvent = ( permissions: PermissionPass, @@ -37,7 +40,7 @@ export let handleEvent = ( if (!socketData.isOwner && !socketData.permissions.admin) { throw new ForbiddenRequestException(`missing admin permission`); } - } else { + } else if (permissions != "noPermissionsRequired") { if ( !socketData.isOwner && !PermissionHelper.can(socketData.permissions, permissions.type, permissions.section, permissions.module) @@ -58,6 +61,25 @@ export let handleEvent = ( }; }; +export let emitEvent = ( + event: EventResponseType & { namespace?: SocketConnectionTypes }, + socket: Socket, + io: Server +) => { + try { + const { answer, type, room, namespace } = event; + if (room === undefined || room == "") { + socket.emit(type, answer); + } else if (namespace === undefined) { + socket.to(room).emit(type, answer); + } else { + io.of(namespace).emit(type, answer); + } + } catch (e) { + socket.emit("error", e.message); + } +}; + /** socket.on( "event", diff --git a/src/websocket/index.ts b/src/websocket/index.ts index aac4c17..4f68826 100644 --- a/src/websocket/index.ts +++ b/src/websocket/index.ts @@ -1,9 +1,15 @@ import helmet from "helmet"; import { Server as httpServerType } from "http"; import { Server } from "socket.io"; +import { instrument } from "@socket.io/admin-ui"; + import authenticateSocket from "../middleware/authenticateSocket"; import checkSocketExists from "../middleware/checkSocketExists"; +import { SocketConnectionTypes } from "../enums/socketEnum"; + import base from "./base"; +import scanner from "./scanner"; +import pScanner from "./pScanner"; export default abstract class SocketServer { private static io: Server; @@ -17,17 +23,31 @@ export default abstract class SocketServer { }, }); + if (process.env.NODE_ENV) { + instrument(this.io, { + auth: false, + mode: "development", + }); + } + this.io.engine.use(helmet()); this.io - .of("/scanner") + .of(SocketConnectionTypes.scanner) .use(authenticateSocket) .on("connection", (socket) => { console.log("socket connection: ", socket.id); - socket.use((packet, next) => authenticateSocket(socket, next)); socket.use((packet, next) => checkSocketExists(socket, packet, next)); base(this.io, socket); + scanner(this.io, socket); }); + + this.io.of(SocketConnectionTypes.pscanner).on("connection", (socket) => { + console.log("socket connection: ", socket.id); + + base(this.io, socket); + pScanner(this.io, socket); + }); } } diff --git a/src/websocket/scanner/index.ts b/src/websocket/scanner/index.ts index c588e2c..0a8575e 100644 --- a/src/websocket/scanner/index.ts +++ b/src/websocket/scanner/index.ts @@ -1,7 +1,63 @@ import { Server, Socket } from "socket.io"; +import { emitEvent, handleEvent } from "../handleEvent"; +import { SocketConnectionTypes } from "../../enums/socketEnum"; export default (io: Server, socket: Socket) => { + socket.on( + "session:create", + handleEvent( + "noPermissionsRequired", + async (room: string) => { + socket.join(room); + return { + type: "status-session:create", + answer: { status: "success" }, + }; + }, + socket + ) + ); + + socket.on( + "session:close", + handleEvent( + "noPermissionsRequired", + async () => { + const rooms = Array.from(socket.rooms).filter((r) => r !== socket.id); + const room = rooms[0]; + socket.leave(room); + emitEvent( + { + type: "package-host_leave", + answer: "host_leave", + room: room, + namespace: SocketConnectionTypes.pscanner, + }, + socket, + io + ); + return { + type: "status-session:close", + answer: { status: "success" }, + }; + }, + socket + ) + ); + socket.on("disconnecting", () => { - // tell public client, that host left - connection will be closed + const rooms = Array.from(socket.rooms).filter((r) => r !== socket.id); + const room = rooms[0]; + io.of(SocketConnectionTypes.pscanner).in(room).disconnectSockets(); + emitEvent( + { + type: "package-host_leave", + answer: "host_leave", + room: room, + namespace: SocketConnectionTypes.pscanner, + }, + socket, + io + ); }); }; From 70d03553d708650066eed75f2b54370cf22c39d7 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 15 Jul 2025 16:59:05 +0200 Subject: [PATCH 37/55] process external scans and check room exists --- src/controller/publicController.ts | 23 ++++++ src/routes/public.ts | 5 ++ src/websocket/index.ts | 4 + src/websocket/pScanner/index.ts | 116 +++++++++++++++++++++++++++++ src/websocket/scanner/index.ts | 2 +- 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/websocket/pScanner/index.ts diff --git a/src/controller/publicController.ts b/src/controller/publicController.ts index e48c008..943aa73 100644 --- a/src/controller/publicController.ts +++ b/src/controller/publicController.ts @@ -9,6 +9,9 @@ import SettingHelper from "../helpers/settingsHelper"; import sharp from "sharp"; import ico from "sharp-ico"; import { FileSystemHelper } from "../helpers/fileSystemHelper"; +import { SocketConnectionTypes } from "../enums/socketEnum"; +import SocketServer from "../websocket"; +import BadRequestException from "../exceptions/badRequestException"; /** * @description get all calendar items by types or nscdr @@ -54,6 +57,26 @@ export async function getCalendarItemsByTypes(req: Request, res: Response): Prom } } +/** + * @description get all calendar items by types or nscdr + * @summary passphrase is passed as value pair like `type:passphrase` + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function checkScannerRoomExists(req: Request, res: Response): Promise { + let roomId = req.body.roomId; + + const socketsInOtherRoom = await SocketServer.server.of(SocketConnectionTypes.scanner).in(roomId).fetchSockets(); + const count = socketsInOtherRoom.length; + + if (count != 0) { + res.sendStatus(204); + } else { + throw new BadRequestException("room does not exists"); + } +} + /** * @description get configuration of UI * @param req {Request} Express req object diff --git a/src/routes/public.ts b/src/routes/public.ts index 360f962..30750a0 100644 --- a/src/routes/public.ts +++ b/src/routes/public.ts @@ -1,5 +1,6 @@ import express from "express"; import { + checkScannerRoomExists, getApplicationConfig, getApplicationFavicon, getApplicationIcon, @@ -18,6 +19,10 @@ router.post("/reportdamage", async (req, res) => { res.send("TODO"); }); +router.post("/checkscannerroom", async (req, res) => { + await checkScannerRoomExists(req, res); +}); + router.get("/configuration", async (req, res) => { await getApplicationConfig(req, res); }); diff --git a/src/websocket/index.ts b/src/websocket/index.ts index 4f68826..82c7a8f 100644 --- a/src/websocket/index.ts +++ b/src/websocket/index.ts @@ -14,6 +14,10 @@ import pScanner from "./pScanner"; export default abstract class SocketServer { private static io: Server; + static get server() { + return this.io; + } + public static init(httpServer: httpServerType) { this.io = new Server(httpServer, { cors: { diff --git a/src/websocket/pScanner/index.ts b/src/websocket/pScanner/index.ts new file mode 100644 index 0000000..19526c4 --- /dev/null +++ b/src/websocket/pScanner/index.ts @@ -0,0 +1,116 @@ +import { Server, Socket } from "socket.io"; +import { emitEvent, handleEvent } from "../handleEvent"; +import { SocketConnectionTypes } from "../../enums/socketEnum"; + +export default (io: Server, socket: Socket) => { + socket.on( + "session:join", + handleEvent( + "noPermissionsRequired", + async (room: string) => { + const socketsInOtherRoom = await io.of(SocketConnectionTypes.scanner).in(room).fetchSockets(); + const count = socketsInOtherRoom.length; + + if (count == 0) { + return { + type: "status-session:join", + answer: { status: "failed" }, + }; + } else { + socket.join(room); + emitEvent( + { + type: "package-scanner_join", + answer: socket.id, + room: room, + namespace: SocketConnectionTypes.scanner, + }, + socket, + io + ); + + return { + type: "status-session:join", + answer: { status: "success" }, + }; + } + }, + socket + ) + ); + + socket.on( + "session:leave", + handleEvent( + "noPermissionsRequired", + async () => { + console.log("called leave"); + const rooms = Array.from(socket.rooms).filter((r) => r !== socket.id); + const room = rooms[0]; + + socket.leave(room); + emitEvent( + { + type: "package-scanner_leave", + answer: socket.id, + room: room, + namespace: SocketConnectionTypes.scanner, + }, + socket, + io + ); + + return { + type: "status-session:leave", + answer: { status: "success" }, + }; + }, + socket + ) + ); + + socket.on( + "scan:send", + handleEvent( + "noPermissionsRequired", + async (result: string) => { + const rooms = Array.from(socket.rooms).filter((r) => r !== socket.id); + const room = rooms[0]; + + emitEvent( + { + type: "package-scan_receive", + answer: result, + room: room, + namespace: SocketConnectionTypes.scanner, + }, + socket, + io + ); + + return { + type: "status-scan:send", + answer: { status: "success" }, + }; + }, + socket + ) + ); + + socket.on("disconnecting", () => { + const rooms = Array.from(socket.rooms).filter((r) => r !== socket.id); + const room = rooms[0]; + + socket.leave(room); + emitEvent( + { + type: "package-scanner_leave", + answer: socket.id, + room: room, + namespace: SocketConnectionTypes.scanner, + }, + socket, + io + ); + }); +}; diff --git a/src/websocket/scanner/index.ts b/src/websocket/scanner/index.ts index 0a8575e..2c8aada 100644 --- a/src/websocket/scanner/index.ts +++ b/src/websocket/scanner/index.ts @@ -48,7 +48,7 @@ export default (io: Server, socket: Socket) => { socket.on("disconnecting", () => { const rooms = Array.from(socket.rooms).filter((r) => r !== socket.id); const room = rooms[0]; - io.of(SocketConnectionTypes.pscanner).in(room).disconnectSockets(); + // io.of(SocketConnectionTypes.pscanner).in(room).disconnectSockets(); emitEvent( { type: "package-host_leave", From 41c3093754b469385dd7a99c6969693a8f160e33 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 16 Jul 2025 12:24:50 +0200 Subject: [PATCH 38/55] enable public report --- src/command/unit/damageReportCommand.ts | 8 ++- .../unit/damageReportCommandHandler.ts | 5 +- .../admin/unit/damageReportController.ts | 7 +- src/controller/publicController.ts | 68 ++++++++++++++++++- src/entity/unit/damageReport.ts | 22 +++++- src/factory/admin/unit/damageReport.ts | 2 +- src/factory/admin/unit/equipment/equipment.ts | 29 +++++++- src/factory/admin/unit/vehicle/vehicle.ts | 26 ++++++- src/factory/admin/unit/wearable/wearable.ts | 29 +++++++- src/helpers/settingsHelper.ts | 2 - src/middleware/multer.ts | 26 +++++++ .../baseSchemaTables/unit_extend.ts | 2 + src/routes/public.ts | 11 ++- .../unit/equipment/equipmentService.ts | 20 ++++++ src/service/unit/vehicle/vehicleService.ts | 20 ++++++ src/service/unit/wearable/wearableService.ts | 20 ++++++ src/type/settingTypes.ts | 3 + .../admin/unit/damageReport.models.ts | 2 +- .../admin/unit/equipment/equipment.models.ts | 8 +++ .../admin/unit/vehicle/vehicle.models.ts | 8 +++ .../admin/unit/wearable/wearable.models.ts | 8 +++ 21 files changed, 307 insertions(+), 19 deletions(-) diff --git a/src/command/unit/damageReportCommand.ts b/src/command/unit/damageReportCommand.ts index 62a4773..c1a5538 100644 --- a/src/command/unit/damageReportCommand.ts +++ b/src/command/unit/damageReportCommand.ts @@ -1,9 +1,11 @@ export interface CreateDamageReportCommand { description: string; + location: string; + note: string; reportedBy: string; - imageCount: number; - affectedId: string; - affected: "equipment" | "vehicle" | "wearable"; + images: string[]; + affectedId?: string; + affected?: "equipment" | "vehicle" | "wearable"; } export interface UpdateDamageReportCommand { diff --git a/src/command/unit/damageReportCommandHandler.ts b/src/command/unit/damageReportCommandHandler.ts index 71a72fa..248d7c9 100644 --- a/src/command/unit/damageReportCommandHandler.ts +++ b/src/command/unit/damageReportCommandHandler.ts @@ -22,8 +22,10 @@ export default abstract class DamageReportCommandHandler { .values({ status: "eingereicht", description: createDamageReport.description, + location: createDamageReport.location, + note: createDamageReport.note, reportedBy: createDamageReport.reportedBy, - imageCount: createDamageReport.imageCount, + images: createDamageReport.images, equipmentId: createDamageReport.affected == "equipment" ? createDamageReport.affectedId : null, vehicleId: createDamageReport.affected == "vehicle" ? createDamageReport.affectedId : null, wearableId: createDamageReport.affected == "wearable" ? createDamageReport.affectedId : null, @@ -86,6 +88,7 @@ export default abstract class DamageReportCommandHandler { * @returns {Promise} */ static async delete(deleteDamageReport: DeleteDamageReportCommand): Promise { + // TODO: remove related images return await dataSource .createQueryBuilder() .delete() diff --git a/src/controller/admin/unit/damageReportController.ts b/src/controller/admin/unit/damageReportController.ts index 443a6cc..88c78d4 100644 --- a/src/controller/admin/unit/damageReportController.ts +++ b/src/controller/admin/unit/damageReportController.ts @@ -79,7 +79,10 @@ export async function getDamageReportById(req: Request, res: Response): Promise< */ export async function createDamageReport(req: Request, res: Response): Promise { const description = req.body.description; + const location = req.body.location; + const note = req.body.note; const reportedBy = req.body.reportedBy; + const images = req.files as Express.Multer.File[]; const affectedId = req.body.affectedId; const affected = req.body.affected; @@ -88,8 +91,10 @@ export async function createDamageReport(req: Request, res: Response): Promise
i.filename), affectedId, affected, }; diff --git a/src/controller/publicController.ts b/src/controller/publicController.ts index 943aa73..e385307 100644 --- a/src/controller/publicController.ts +++ b/src/controller/publicController.ts @@ -12,6 +12,17 @@ import { FileSystemHelper } from "../helpers/fileSystemHelper"; import { SocketConnectionTypes } from "../enums/socketEnum"; import SocketServer from "../websocket"; import BadRequestException from "../exceptions/badRequestException"; +import EquipmentService from "../service/unit/equipment/equipmentService"; +import VehicleService from "../service/unit/vehicle/vehicleService"; +import WearableService from "../service/unit/wearable/wearableService"; +import EquipmentFactory from "../factory/admin/unit/equipment/equipment"; +import VehicleFactory from "../factory/admin/unit/vehicle/vehicle"; +import WearableFactory from "../factory/admin/unit/wearable/wearable"; +import { MinifiedEquipmentViewModel } from "../viewmodel/admin/unit/equipment/equipment.models"; +import { MinifiedVehicleViewModel } from "../viewmodel/admin/unit/vehicle/vehicle.models"; +import { MinifiedWearableViewModel } from "../viewmodel/admin/unit/wearable/wearable.models"; +import DamageReportCommandHandler from "../command/unit/damageReportCommandHandler"; +import { CreateDamageReportCommand } from "../command/unit/damageReportCommand"; /** * @description get all calendar items by types or nscdr @@ -58,8 +69,8 @@ export async function getCalendarItemsByTypes(req: Request, res: Response): Prom } /** - * @description get all calendar items by types or nscdr - * @summary passphrase is passed as value pair like `type:passphrase` + * @description check if scanner session exists + * @summary existance is checked by room exists * @param req {Request} Express req object * @param res {Response} Express res object * @returns {Promise<*>} @@ -77,6 +88,58 @@ export async function checkScannerRoomExists(req: Request, res: Response): Promi } } +/** + * @description get equipment, vehicle, wearable by code + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function searchStuffByCode(req: Request, res: Response): Promise { + let code = req.query.code.toString(); + + let e = await EquipmentService.getAllByCode(code); + let v = await VehicleService.getAllByCode(code); + let w = await WearableService.getAllByCode(code); + + res.json([ + ...EquipmentFactory.mapToBaseMinifed(e), + ...VehicleFactory.mapToBaseMinifed(v), + ...WearableFactory.mapToBaseMinifed(w), + ]); +} + +/** + * @description create damagereport to equipment, vehicle, wearable + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createDamageReport(req: Request, res: Response): Promise { + const related = (req.body.related ? JSON.parse(req.body.related) : undefined) as + | undefined + | MinifiedEquipmentViewModel + | MinifiedVehicleViewModel + | MinifiedWearableViewModel; + const description = req.body.description; + const location = req.body.location; + const note = req.body.note; + const reportedBy = req.body.reportedBy; + const images = req.files as Express.Multer.File[]; + + let createDamageReport: CreateDamageReportCommand = { + description: description, + location: location, + note: note, + reportedBy: reportedBy, + images: images.map((i) => i.filename), + affectedId: related ? related.id : undefined, + affected: related ? related.assigned : undefined, + }; + await DamageReportCommandHandler.create(createDamageReport); + + res.sendStatus(204); +} + /** * @description get configuration of UI * @param req {Request} Express req object @@ -91,6 +154,7 @@ export async function getApplicationConfig(req: Request, res: Response): Promise "club.website": SettingHelper.getSetting("club.website"), "app.custom_login_message": SettingHelper.getSetting("app.custom_login_message"), "app.show_link_to_calendar": SettingHelper.getSetting("app.show_link_to_calendar"), + "app.show_link_to_damagereport": SettingHelper.getSetting("app.show_link_to_damagereport"), }; res.json(config); diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts index 0a4fd7e..c401bf2 100644 --- a/src/entity/unit/damageReport.ts +++ b/src/entity/unit/damageReport.ts @@ -21,12 +21,28 @@ export class damageReport { @Column({ type: "text" }) description: string; + @Column({ type: "text" }) + location: string; + + @Column({ type: "text" }) + note: string; + + @Column({ + type: "text", + transformer: { + from(value: string): Array { + return value.split(","); + }, + to(value: Array): string { + return value.join(","); + }, + }, + }) + images: string[]; + @Column({ type: "varchar", length: 255 }) reportedBy: string; - @Column({ type: "int", default: 0 }) - imageCount: number; - @Column({ nullable: true, default: null }) equipmentId?: string; diff --git a/src/factory/admin/unit/damageReport.ts b/src/factory/admin/unit/damageReport.ts index fb9115e..9c7cdb4 100644 --- a/src/factory/admin/unit/damageReport.ts +++ b/src/factory/admin/unit/damageReport.ts @@ -39,7 +39,7 @@ export default abstract class DamageReportFactory { status: record.status, done: record.done, description: record.description, - imageCount: record.imageCount, + image: record.images, reportedBy: record?.reportedBy, ...assigned, maintenance: record.maintenance ? MaintenanceFactory.mapToSingle(record.maintenance) : null, diff --git a/src/factory/admin/unit/equipment/equipment.ts b/src/factory/admin/unit/equipment/equipment.ts index 8f0b520..a35ebc1 100644 --- a/src/factory/admin/unit/equipment/equipment.ts +++ b/src/factory/admin/unit/equipment/equipment.ts @@ -1,5 +1,8 @@ import { equipment } from "../../../../entity/unit/equipment/equipment"; -import { EquipmentViewModel } from "../../../../viewmodel/admin/unit/equipment/equipment.models"; +import { + EquipmentViewModel, + MinifiedEquipmentViewModel, +} from "../../../../viewmodel/admin/unit/equipment/equipment.models"; import EquipmentTypeFactory from "./equipmentType"; export default abstract class EquipmentFactory { @@ -29,4 +32,28 @@ export default abstract class EquipmentFactory { public static mapToBase(records: Array): Array { return records.map((r) => this.mapToSingle(r)); } + + /** + * @description map record to minifed equipment + * @param {equipment} record + * @returns {MinifiedEquipmentViewModel} + */ + public static mapToSingleMinified(record: equipment): MinifiedEquipmentViewModel { + return { + id: record.id, + code: record?.code, + name: record.name, + type: record?.equipmentType.type, + assigned: "equipment", + }; + } + + /** + * @description map records to minified equipment + * @param {Array} records + * @returns {Array} + */ + public static mapToBaseMinifed(records: Array): Array { + return records.map((r) => this.mapToSingleMinified(r)); + } } diff --git a/src/factory/admin/unit/vehicle/vehicle.ts b/src/factory/admin/unit/vehicle/vehicle.ts index fd7a86d..70e0575 100644 --- a/src/factory/admin/unit/vehicle/vehicle.ts +++ b/src/factory/admin/unit/vehicle/vehicle.ts @@ -1,5 +1,5 @@ import { vehicle } from "../../../../entity/unit/vehicle/vehicle"; -import { VehicleViewModel } from "../../../../viewmodel/admin/unit/vehicle/vehicle.models"; +import { MinifiedVehicleViewModel, VehicleViewModel } from "../../../../viewmodel/admin/unit/vehicle/vehicle.models"; import VehicleTypeFactory from "./vehicleType"; export default abstract class VehicleFactory { @@ -29,4 +29,28 @@ export default abstract class VehicleFactory { public static mapToBase(records: Array): Array { return records.map((r) => this.mapToSingle(r)); } + + /** + * @description map record to minifed vehicle + * @param {vehicle} record + * @returns {MinifiedVehicleViewModel} + */ + public static mapToSingleMinified(record: vehicle): MinifiedVehicleViewModel { + return { + id: record.id, + code: record?.code, + name: record.name, + type: record?.vehicleType.type, + assigned: "vehicle", + }; + } + + /** + * @description map records to minified vehicle + * @param {Array} records + * @returns {Array} + */ + public static mapToBaseMinifed(records: Array): Array { + return records.map((r) => this.mapToSingleMinified(r)); + } } diff --git a/src/factory/admin/unit/wearable/wearable.ts b/src/factory/admin/unit/wearable/wearable.ts index d3b39af..4f58e2d 100644 --- a/src/factory/admin/unit/wearable/wearable.ts +++ b/src/factory/admin/unit/wearable/wearable.ts @@ -1,5 +1,8 @@ import { wearable } from "../../../../entity/unit/wearable/wearable"; -import { WearableViewModel } from "../../../../viewmodel/admin/unit/wearable/wearable.models"; +import { + MinifiedWearableViewModel, + WearableViewModel, +} from "../../../../viewmodel/admin/unit/wearable/wearable.models"; import MemberFactory from "../../club/member/member"; import WearableTypeFactory from "./wearableType"; @@ -32,4 +35,28 @@ export default abstract class WearableFactory { public static mapToBase(records: Array): Array { return records.map((r) => this.mapToSingle(r)); } + + /** + * @description map record to minifed wearable + * @param {wearable} record + * @returns {MinifiedWearableViewModel} + */ + public static mapToSingleMinified(record: wearable): MinifiedWearableViewModel { + return { + id: record.id, + code: record?.code, + name: record.name, + type: record?.wearableType.type, + assigned: "wearable", + }; + } + + /** + * @description map records to minified wearable + * @param {Array} records + * @returns {Array} + */ + public static mapToBaseMinifed(records: Array): Array { + return records.map((r) => this.mapToSingleMinified(r)); + } } diff --git a/src/helpers/settingsHelper.ts b/src/helpers/settingsHelper.ts index d4c21d8..b3eb5f8 100644 --- a/src/helpers/settingsHelper.ts +++ b/src/helpers/settingsHelper.ts @@ -14,8 +14,6 @@ import { UrlConverter, } from "./convertHelper"; import cloneDeep from "lodash.clonedeep"; -import { rejects } from "assert"; -import InternalException from "../exceptions/internalException"; import MailHelper from "./mailHelper"; export default abstract class SettingHelper { diff --git a/src/middleware/multer.ts b/src/middleware/multer.ts index 84a677c..321439e 100644 --- a/src/middleware/multer.ts +++ b/src/middleware/multer.ts @@ -2,6 +2,7 @@ import multer from "multer"; import { FileSystemHelper } from "../helpers/fileSystemHelper"; import path from "path"; import BadRequestException from "../exceptions/badRequestException"; +import { v4 as uuid } from "uuid"; /**Settings image upload */ export const clubImageStorage = multer.diskStorage({ @@ -58,3 +59,28 @@ export const inspectionFileMulter = multer({ }); export const inspectionFileUpload = inspectionFileMulter.array("files"); + +/**public damage report upload */ +export const pDamageReportFileStorage = multer.diskStorage({ + destination: function (req, file, cb) { + FileSystemHelper.createFolder("damageReport"); + cb(null, FileSystemHelper.formatPath("damageReport")); + }, + filename: function (req, file, cb) { + const fileExtension = path.extname(file.originalname).toLowerCase(); + cb(null, uuid() + fileExtension); + }, +}); + +export const pDamageReportFileMulter = multer({ + storage: pDamageReportFileStorage, + fileFilter(req, file, cb) { + if (file.mimetype.startsWith("image/")) { + cb(null, true); + } else { + cb(new BadRequestException("Wrong file format")); + } + }, +}); + +export const pDamageReportFileUpload = pDamageReportFileMulter.array("images"); diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index 43bece7..0beef1c 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -9,6 +9,8 @@ export const damage_report_table = new Table({ { name: "status", ...getTypeByORM("varchar") }, { name: "done", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, { name: "description", ...getTypeByORM("text") }, + { name: "location", ...getTypeByORM("text") }, + { name: "note", ...getTypeByORM("text") }, { name: "reportedBy", ...getTypeByORM("varchar") }, { name: "imageCount", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, { name: "equipmentId", ...getTypeByORM("uuid", true) }, diff --git a/src/routes/public.ts b/src/routes/public.ts index 30750a0..ac39669 100644 --- a/src/routes/public.ts +++ b/src/routes/public.ts @@ -1,13 +1,16 @@ import express from "express"; import { checkScannerRoomExists, + createDamageReport, getApplicationConfig, getApplicationFavicon, getApplicationIcon, getApplicationLogo, getApplicationManifest, getCalendarItemsByTypes, + searchStuffByCode, } from "../controller/publicController"; +import { pDamageReportFileUpload } from "../middleware/multer"; var router = express.Router({ mergeParams: true }); @@ -15,8 +18,12 @@ router.get("/calendar", async (req, res) => { await getCalendarItemsByTypes(req, res); }); -router.post("/reportdamage", async (req, res) => { - res.send("TODO"); +router.get("/reportdamage", async (req, res) => { + await searchStuffByCode(req, res); +}); + +router.post("/reportdamage", pDamageReportFileUpload, async (req, res) => { + await createDamageReport(req, res); }); router.post("/checkscannerroom", async (req, res) => { diff --git a/src/service/unit/equipment/equipmentService.ts b/src/service/unit/equipment/equipmentService.ts index 921f574..cb0cb2c 100644 --- a/src/service/unit/equipment/equipmentService.ts +++ b/src/service/unit/equipment/equipmentService.ts @@ -58,6 +58,26 @@ export default abstract class EquipmentService { }); } + /** + * @description get equipment by code + * @returns {Promise>} + */ + static async getAllByCode(code: string): Promise> { + return await dataSource + .getRepository(equipment) + .createQueryBuilder("equipment") + .leftJoinAndSelect("equipment.equipmentType", "equipmenttype") + .where({ code: Like(`%${code}%`) }) + .orderBy("name", "ASC") + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "equipment", err); + }); + } + /** * @description get equipment by id * @returns {Promise} diff --git a/src/service/unit/vehicle/vehicleService.ts b/src/service/unit/vehicle/vehicleService.ts index 61c2b73..5f9c3ca 100644 --- a/src/service/unit/vehicle/vehicleService.ts +++ b/src/service/unit/vehicle/vehicleService.ts @@ -58,6 +58,26 @@ export default abstract class VehicleService { }); } + /** + * @description get vehicle by code + * @returns {Promise>} + */ + static async getAllByCode(code: string): Promise> { + return await dataSource + .getRepository(vehicle) + .createQueryBuilder("vehicle") + .leftJoinAndSelect("vehicle.vehicleType", "vehicletype") + .where({ code: Like(`%${code}%`) }) + .orderBy("name", "ASC") + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "vehicle", err); + }); + } + /** * @description get vehicle by id * @returns {Promise} diff --git a/src/service/unit/wearable/wearableService.ts b/src/service/unit/wearable/wearableService.ts index d016b0f..557a9f9 100644 --- a/src/service/unit/wearable/wearableService.ts +++ b/src/service/unit/wearable/wearableService.ts @@ -59,6 +59,26 @@ export default abstract class WearableService { }); } + /** + * @description get wearable by code + * @returns {Promise>} + */ + static async getAllByCode(code: string): Promise> { + return await dataSource + .getRepository(wearable) + .createQueryBuilder("wearable") + .leftJoinAndSelect("wearable.wearableType", "wearabletype") + .where({ code: Like(`%${code}%`) }) + .orderBy("name", "ASC") + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "wearable", err); + }); + } + /** * @description get wearable by id * @returns {Promise} diff --git a/src/type/settingTypes.ts b/src/type/settingTypes.ts index ff6e728..08d8fbc 100644 --- a/src/type/settingTypes.ts +++ b/src/type/settingTypes.ts @@ -10,6 +10,7 @@ export type SettingString = | "club.website" | "app.custom_login_message" | "app.show_link_to_calendar" + | "app.show_link_to_damagereport" | "session.jwt_expiration" | "session.refresh_expiration" | "session.pwa_refresh_expiration" @@ -34,6 +35,7 @@ export type SettingValueMapping = { "club.website": string; "app.custom_login_message": string; "app.show_link_to_calendar": boolean; + "app.show_link_to_damagereport": boolean; "session.jwt_expiration": ms.StringValue; "session.refresh_expiration": ms.StringValue; "session.pwa_refresh_expiration": ms.StringValue; @@ -68,6 +70,7 @@ export const settingsType: SettingsSchema = { "club.website": { type: "url", optional: true }, "app.custom_login_message": { type: "string", optional: true }, "app.show_link_to_calendar": { type: "boolean", default: true }, + "app.show_link_to_damagereport": { type: "boolean", default: false }, "session.jwt_expiration": { type: "ms", default: "15m" }, "session.refresh_expiration": { type: "ms", default: "1d" }, "session.pwa_refresh_expiration": { type: "ms", default: "5d" }, diff --git a/src/viewmodel/admin/unit/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport.models.ts index 9427d8a..7871d06 100644 --- a/src/viewmodel/admin/unit/damageReport.models.ts +++ b/src/viewmodel/admin/unit/damageReport.models.ts @@ -26,7 +26,7 @@ export type DamageReportViewModel = { status: string; done: boolean; description: string; - imageCount: number; + image: string[]; 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 index 2990c1d..625d482 100644 --- a/src/viewmodel/admin/unit/equipment/equipment.models.ts +++ b/src/viewmodel/admin/unit/equipment/equipment.models.ts @@ -10,3 +10,11 @@ export interface EquipmentViewModel { equipmentTypeId: string; equipmentType: EquipmentTypeViewModel; } + +export interface MinifiedEquipmentViewModel { + id: string; + code?: string; + name: string; + type: string; + assigned: "equipment"; +} diff --git a/src/viewmodel/admin/unit/vehicle/vehicle.models.ts b/src/viewmodel/admin/unit/vehicle/vehicle.models.ts index b25ab0c..b82b190 100644 --- a/src/viewmodel/admin/unit/vehicle/vehicle.models.ts +++ b/src/viewmodel/admin/unit/vehicle/vehicle.models.ts @@ -10,3 +10,11 @@ export interface VehicleViewModel { vehicleTypeId: string; vehicleType: VehicleTypeViewModel; } + +export interface MinifiedVehicleViewModel { + id: string; + code?: string; + name: string; + type: string; + assigned: "vehicle"; +} diff --git a/src/viewmodel/admin/unit/wearable/wearable.models.ts b/src/viewmodel/admin/unit/wearable/wearable.models.ts index e3bacac..de982e7 100644 --- a/src/viewmodel/admin/unit/wearable/wearable.models.ts +++ b/src/viewmodel/admin/unit/wearable/wearable.models.ts @@ -13,3 +13,11 @@ export interface WearableViewModel { wearableTypeId: string; wearableType: WearableTypeViewModel; } + +export interface MinifiedWearableViewModel { + id: string; + code?: string; + name: string; + type: string; + assigned: "wearable"; +} From 0fdb77d7cae993195a137077cca546e56c066823 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 16 Jul 2025 12:42:54 +0200 Subject: [PATCH 39/55] pass reports to admin ui --- src/factory/admin/unit/damageReport.ts | 12 ++++++++++-- src/viewmodel/admin/unit/damageReport.models.ts | 4 +++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/factory/admin/unit/damageReport.ts b/src/factory/admin/unit/damageReport.ts index 9c7cdb4..8bf72d9 100644 --- a/src/factory/admin/unit/damageReport.ts +++ b/src/factory/admin/unit/damageReport.ts @@ -25,12 +25,18 @@ export default abstract class DamageReportFactory { assigned: "vehicle", related: VehicleFactory.mapToSingle(record.vehicle), }; - } else { + } else if (record?.wearableId) { assigned = { relatedId: record.wearableId, assigned: "wearable", related: WearableFactory.mapToSingle(record.wearable), }; + } else { + assigned = { + relatedId: undefined, + assigned: undefined, + related: undefined, + }; } return { @@ -39,7 +45,9 @@ export default abstract class DamageReportFactory { status: record.status, done: record.done, description: record.description, - image: record.images, + location: record.location, + note: record.note, + images: record.images.filter((i) => !!i), reportedBy: record?.reportedBy, ...assigned, maintenance: record.maintenance ? MaintenanceFactory.mapToSingle(record.maintenance) : null, diff --git a/src/viewmodel/admin/unit/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport.models.ts index 7871d06..25692b2 100644 --- a/src/viewmodel/admin/unit/damageReport.models.ts +++ b/src/viewmodel/admin/unit/damageReport.models.ts @@ -26,7 +26,9 @@ export type DamageReportViewModel = { status: string; done: boolean; description: string; - image: string[]; + location: string; + note: string; + images: string[]; reportedBy: string; maintenance?: MaintenanceViewModel; } & DamageReportAssigned; From ca1ca9edc88e881f3432fba828a38b9bbe90012b Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 16 Jul 2025 12:44:23 +0200 Subject: [PATCH 40/55] update image resolve by entity --- src/entity/unit/damageReport.ts | 2 +- src/factory/admin/unit/damageReport.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts index c401bf2..5eba9b2 100644 --- a/src/entity/unit/damageReport.ts +++ b/src/entity/unit/damageReport.ts @@ -31,7 +31,7 @@ export class damageReport { type: "text", transformer: { from(value: string): Array { - return value.split(","); + return value.split(",").filter((i) => !!i); }, to(value: Array): string { return value.join(","); diff --git a/src/factory/admin/unit/damageReport.ts b/src/factory/admin/unit/damageReport.ts index 8bf72d9..cb61860 100644 --- a/src/factory/admin/unit/damageReport.ts +++ b/src/factory/admin/unit/damageReport.ts @@ -47,7 +47,7 @@ export default abstract class DamageReportFactory { description: record.description, location: record.location, note: record.note, - images: record.images.filter((i) => !!i), + images: record.images, reportedBy: record?.reportedBy, ...assigned, maintenance: record.maintenance ? MaintenanceFactory.mapToSingle(record.maintenance) : null, From a208cdd158ea2482788ff8c778977984d842218a Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Thu, 17 Jul 2025 09:32:27 +0200 Subject: [PATCH 41/55] provide damage image --- .../admin/unit/damageReportController.ts | 16 ++++++++++++++++ src/routes/admin/unit/damageReport.ts | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/src/controller/admin/unit/damageReportController.ts b/src/controller/admin/unit/damageReportController.ts index 88c78d4..7e23556 100644 --- a/src/controller/admin/unit/damageReportController.ts +++ b/src/controller/admin/unit/damageReportController.ts @@ -4,6 +4,7 @@ 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"; +import { FileSystemHelper } from "../../../helpers/fileSystemHelper"; /** * @description get all damageReports by status @@ -71,6 +72,21 @@ export async function getDamageReportById(req: Request, res: Response): Promise< res.json(DamageReportFactory.mapToSingle(damageReport)); } +/** + * @description provide uploaded image for damage report + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function provideDamageReportImageUpload(req: Request, res: Response): Promise { + const damageReportId = req.params.id; + const filename = req.params.filename; + + let filepath = FileSystemHelper.formatPath("damageReport", filename); + + res.sendFile(filepath); +} + /** * @description create damageReport * @param req {Request} Express req object diff --git a/src/routes/admin/unit/damageReport.ts b/src/routes/admin/unit/damageReport.ts index aee6261..8a19e95 100644 --- a/src/routes/admin/unit/damageReport.ts +++ b/src/routes/admin/unit/damageReport.ts @@ -11,6 +11,7 @@ import { getAllDamageReportsByStatus, getAllDamageReportsForRelated, getDamageReportById, + provideDamageReportImageUpload, updateDamageReportById, } from "../../../controller/admin/unit/damageReportController"; @@ -39,6 +40,10 @@ router.get("/:id", async (req: Request, res: Response) => { await getDamageReportById(req, res); }); +router.get("/:id/:filename", async (req: Request, res: Response) => { + await provideDamageReportImageUpload(req, res); +}); + router.patch( "/:id", PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), From c02487ad3c572c2b351d4f74e1b7913bb20489b1 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Thu, 17 Jul 2025 10:37:26 +0200 Subject: [PATCH 42/55] extend damageReport with noteByWorker --- src/command/unit/damageReportCommand.ts | 3 ++- src/command/unit/damageReportCommandHandler.ts | 3 ++- src/controller/admin/unit/damageReportController.ts | 4 +++- src/controller/publicController.ts | 2 +- src/entity/unit/damageReport.ts | 5 ++++- src/factory/admin/unit/damageReport.ts | 3 ++- src/migrations/baseSchemaTables/unit_extend.ts | 3 ++- src/viewmodel/admin/unit/damageReport.models.ts | 3 ++- 8 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/command/unit/damageReportCommand.ts b/src/command/unit/damageReportCommand.ts index c1a5538..9d7a631 100644 --- a/src/command/unit/damageReportCommand.ts +++ b/src/command/unit/damageReportCommand.ts @@ -1,7 +1,7 @@ export interface CreateDamageReportCommand { description: string; location: string; - note: string; + noteByReporter: string; reportedBy: string; images: string[]; affectedId?: string; @@ -11,6 +11,7 @@ export interface CreateDamageReportCommand { export interface UpdateDamageReportCommand { id: string; status: string; + noteByWorker: string; done: boolean; } diff --git a/src/command/unit/damageReportCommandHandler.ts b/src/command/unit/damageReportCommandHandler.ts index 248d7c9..e5357d6 100644 --- a/src/command/unit/damageReportCommandHandler.ts +++ b/src/command/unit/damageReportCommandHandler.ts @@ -23,7 +23,7 @@ export default abstract class DamageReportCommandHandler { status: "eingereicht", description: createDamageReport.description, location: createDamageReport.location, - note: createDamageReport.note, + noteByReporter: createDamageReport.noteByReporter, reportedBy: createDamageReport.reportedBy, images: createDamageReport.images, equipmentId: createDamageReport.affected == "equipment" ? createDamageReport.affectedId : null, @@ -50,6 +50,7 @@ export default abstract class DamageReportCommandHandler { .update(damageReport) .set({ status: updateDamageReport.status, + noteByWorker: updateDamageReport.noteByWorker, done: updateDamageReport.done, }) .where("id = :id", { id: updateDamageReport.id }) diff --git a/src/controller/admin/unit/damageReportController.ts b/src/controller/admin/unit/damageReportController.ts index 7e23556..055b3bc 100644 --- a/src/controller/admin/unit/damageReportController.ts +++ b/src/controller/admin/unit/damageReportController.ts @@ -108,7 +108,7 @@ export async function createDamageReport(req: Request, res: Response): Promise i.filename), affectedId, @@ -128,11 +128,13 @@ export async function createDamageReport(req: Request, res: Response): Promise { const damageReportId = req.params.id; const status = req.body.status; + const noteByWorker = req.body.noteByWorker; const done = req.body.done; let updateDamageReport: UpdateDamageReportCommand = { id: damageReportId, status, + noteByWorker, done, }; await DamageReportCommandHandler.update(updateDamageReport); diff --git a/src/controller/publicController.ts b/src/controller/publicController.ts index e385307..fd2ff81 100644 --- a/src/controller/publicController.ts +++ b/src/controller/publicController.ts @@ -129,7 +129,7 @@ export async function createDamageReport(req: Request, res: Response): Promise i.filename), affectedId: related ? related.id : undefined, diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts index 5eba9b2..bfb45ae 100644 --- a/src/entity/unit/damageReport.ts +++ b/src/entity/unit/damageReport.ts @@ -25,7 +25,10 @@ export class damageReport { location: string; @Column({ type: "text" }) - note: string; + noteByReporter: string; + + @Column({ type: "text" }) + noteByWorker: string; @Column({ type: "text", diff --git a/src/factory/admin/unit/damageReport.ts b/src/factory/admin/unit/damageReport.ts index cb61860..fe6ff61 100644 --- a/src/factory/admin/unit/damageReport.ts +++ b/src/factory/admin/unit/damageReport.ts @@ -46,7 +46,8 @@ export default abstract class DamageReportFactory { done: record.done, description: record.description, location: record.location, - note: record.note, + noteByReporter: record.noteByReporter, + noteByWorker: record.noteByWorker, images: record.images, reportedBy: record?.reportedBy, ...assigned, diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index 0beef1c..86de02c 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -10,7 +10,8 @@ export const damage_report_table = new Table({ { name: "done", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, { name: "description", ...getTypeByORM("text") }, { name: "location", ...getTypeByORM("text") }, - { name: "note", ...getTypeByORM("text") }, + { name: "noteByReporter", ...getTypeByORM("text") }, + { name: "noteByWorker", ...getTypeByORM("text") }, { name: "reportedBy", ...getTypeByORM("varchar") }, { name: "imageCount", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, { name: "equipmentId", ...getTypeByORM("uuid", true) }, diff --git a/src/viewmodel/admin/unit/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport.models.ts index 25692b2..15b03f2 100644 --- a/src/viewmodel/admin/unit/damageReport.models.ts +++ b/src/viewmodel/admin/unit/damageReport.models.ts @@ -27,7 +27,8 @@ export type DamageReportViewModel = { done: boolean; description: string; location: string; - note: string; + noteByReporter: string; + noteByWorker: string; images: string[]; reportedBy: string; maintenance?: MaintenanceViewModel; From 2d0f2b50f1331d517981d64391058af8fb46e93b Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Thu, 17 Jul 2025 10:49:40 +0200 Subject: [PATCH 43/55] update schema migration --- src/migrations/baseSchemaTables/unit_extend.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index 86de02c..171c808 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -13,7 +13,7 @@ export const damage_report_table = new Table({ { name: "noteByReporter", ...getTypeByORM("text") }, { name: "noteByWorker", ...getTypeByORM("text") }, { name: "reportedBy", ...getTypeByORM("varchar") }, - { name: "imageCount", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, + { name: "images", ...getTypeByORM("text") }, { name: "equipmentId", ...getTypeByORM("uuid", true) }, { name: "vehicleId", ...getTypeByORM("uuid", true) }, { name: "wearableId", ...getTypeByORM("uuid", true) }, From 4d37571cb64d63df4cb7de69617561c92b7648bc Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 19 Jul 2025 11:02:07 +0200 Subject: [PATCH 44/55] schema change and base operations --- src/command/unit/damageReportCommand.ts | 4 +- .../unit/damageReportCommandHandler.ts | 8 +- src/command/unit/repairCommand.ts | 19 +++ src/command/unit/repairCommandHandler.ts | 75 +++++++++++ src/controller/admin/unit/repairController.ts | 126 ++++++++++++++++++ src/data-source.ts | 6 +- src/entity/unit/damageReport.ts | 10 +- src/entity/unit/equipment/equipment.ts | 4 + src/entity/unit/maintenance.ts | 12 +- src/entity/unit/repair.ts | 76 +++++++++++ src/entity/unit/vehicle/vehicle.ts | 4 + src/entity/unit/wearable/wearable.ts | 4 + src/factory/admin/unit/damageReport.ts | 3 +- src/factory/admin/unit/maintenance.ts | 2 - src/factory/admin/unit/repair.ts | 64 +++++++++ src/helpers/backupHelper.ts | 30 +++-- ...-UnitBase.ts => 1752914551204-UnitBase.ts} | 8 +- .../baseSchemaTables/unit_extend.ts | 48 ++++++- src/routes/admin/index.ts | 11 ++ src/routes/admin/unit/repair.ts | 52 ++++++++ src/service/unit/damageReportService.ts | 2 +- src/service/unit/maintenanceService.ts | 6 +- src/service/unit/repairService.ts | 96 +++++++++++++ src/type/permissionTypes.ts | 3 + .../admin/unit/damageReport.models.ts | 3 +- .../admin/unit/maintenance.models.ts | 3 - src/viewmodel/admin/unit/repair.models.ts | 33 +++++ 27 files changed, 660 insertions(+), 52 deletions(-) create mode 100644 src/command/unit/repairCommand.ts create mode 100644 src/command/unit/repairCommandHandler.ts create mode 100644 src/controller/admin/unit/repairController.ts create mode 100644 src/entity/unit/repair.ts create mode 100644 src/factory/admin/unit/repair.ts rename src/migrations/{1749361405703-UnitBase.ts => 1752914551204-UnitBase.ts} (90%) create mode 100644 src/routes/admin/unit/repair.ts create mode 100644 src/service/unit/repairService.ts create mode 100644 src/viewmodel/admin/unit/repair.models.ts diff --git a/src/command/unit/damageReportCommand.ts b/src/command/unit/damageReportCommand.ts index 9d7a631..1b397c2 100644 --- a/src/command/unit/damageReportCommand.ts +++ b/src/command/unit/damageReportCommand.ts @@ -15,9 +15,9 @@ export interface UpdateDamageReportCommand { done: boolean; } -export interface UpdateDamageReportRelatedMaintenanceCommand { +export interface UpdateDamageReportRelatedRepairCommand { id: string; - maintenanceId: string; + repairId: string; } export interface DeleteDamageReportCommand { diff --git a/src/command/unit/damageReportCommandHandler.ts b/src/command/unit/damageReportCommandHandler.ts index e5357d6..d7d8485 100644 --- a/src/command/unit/damageReportCommandHandler.ts +++ b/src/command/unit/damageReportCommandHandler.ts @@ -5,7 +5,7 @@ import { CreateDamageReportCommand, UpdateDamageReportCommand, DeleteDamageReportCommand, - UpdateDamageReportRelatedMaintenanceCommand, + UpdateDamageReportRelatedRepairCommand, } from "./damageReportCommand"; export default abstract class DamageReportCommandHandler { @@ -66,14 +66,12 @@ export default abstract class DamageReportCommandHandler { * @param {UpdateDamageReportCommand} updateDamageReport * @returns {Promise} */ - static async updateRelatedMaintenance( - updateDamageReport: UpdateDamageReportRelatedMaintenanceCommand - ): Promise { + static async updateRelatedMaintenance(updateDamageReport: UpdateDamageReportRelatedRepairCommand): Promise { return await dataSource .createQueryBuilder() .update(damageReport) .set({ - maintenanceId: updateDamageReport.maintenanceId, + repairId: updateDamageReport.repairId, }) .where("id = :id", { id: updateDamageReport.id }) .execute() diff --git a/src/command/unit/repairCommand.ts b/src/command/unit/repairCommand.ts new file mode 100644 index 0000000..bd7af10 --- /dev/null +++ b/src/command/unit/repairCommand.ts @@ -0,0 +1,19 @@ +export interface CreateRepairCommand { + description: string; + responsible: string; + affectedId: string; + affected: "equipment" | "vehicle" | "wearable"; + reports: string[]; +} + +export interface UpdateRepairCommand { + id: string; + status: string; + description: string; + responsible: string; + reports: string[]; +} + +export interface DeleteRepairCommand { + id: string; +} diff --git a/src/command/unit/repairCommandHandler.ts b/src/command/unit/repairCommandHandler.ts new file mode 100644 index 0000000..b2713f5 --- /dev/null +++ b/src/command/unit/repairCommandHandler.ts @@ -0,0 +1,75 @@ +import { dataSource } from "../../data-source"; +import { repair } from "../../entity/unit/repair"; +import DatabaseActionException from "../../exceptions/databaseActionException"; +import { CreateRepairCommand, UpdateRepairCommand, DeleteRepairCommand } from "./repairCommand"; + +export default abstract class RepairCommandHandler { + /** + * @description create repair + * @param {CreateRepairCommand} createRepair + * @returns {Promise} + */ + static async create(createRepair: CreateRepairCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(repair) + .values({ + status: "in Arbeit", + description: createRepair.description, + reports: createRepair.reports.map((r) => ({ id: r })), + responsible: createRepair.responsible, + equipmentId: createRepair.affected == "equipment" ? createRepair.affectedId : null, + vehicleId: createRepair.affected == "vehicle" ? createRepair.affectedId : null, + wearableId: createRepair.affected == "wearable" ? createRepair.affectedId : null, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new DatabaseActionException("CREATE", "repair", err); + }); + } + + /** + * @description update repair + * @param {UpdateRepairCommand} updateRepair + * @returns {Promise} + */ + static async update(updateRepair: UpdateRepairCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(repair) + .set({ + status: updateRepair.status, + description: updateRepair.description, + reports: updateRepair.reports.map((r) => ({ id: r })), + responsible: updateRepair.responsible, + }) + .where("id = :id", { id: updateRepair.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "repair", err); + }); + } + + /** + * @description delete repair + * @param {DeleteRepairCommand} deleteRepair + * @returns {Promise} + */ + static async delete(deleteRepair: DeleteRepairCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(repair) + .where("id = :id", { id: deleteRepair.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("DELETE", "repair", err); + }); + } +} diff --git a/src/controller/admin/unit/repairController.ts b/src/controller/admin/unit/repairController.ts new file mode 100644 index 0000000..9e6a25e --- /dev/null +++ b/src/controller/admin/unit/repairController.ts @@ -0,0 +1,126 @@ +import { Request, Response } from "express"; +import RepairService from "../../../service/unit/repairService"; +import RepairFactory from "../../../factory/admin/unit/repair"; +import { CreateRepairCommand, UpdateRepairCommand } from "../../../command/unit/repairCommand"; +import RepairCommandHandler from "../../../command/unit/repairCommandHandler"; +import BadRequestException from "../../../exceptions/badRequestException"; +import { FileSystemHelper } from "../../../helpers/fileSystemHelper"; + +/** + * @description get all repairs by status + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllRepairsByStatus(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 [repairs, total] = await RepairService.getAll(done, { offset, count, noLimit }); + + res.json({ + repairs: RepairFactory.mapToBase(repairs), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get all repairs for related id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllRepairsForRelated(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 [repairs, total] = await RepairService.getAllForRelated(where, { offset, count, noLimit }); + + res.json({ + repairs: RepairFactory.mapToBase(repairs), + total: total, + offset: offset, + count: count, + }); +} + +/** + * @description get repair by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getRepairById(req: Request, res: Response): Promise { + const repairId = req.params.id; + let repair = await RepairService.getById(repairId); + + res.json(RepairFactory.mapToSingle(repair)); +} + +/** + * @description create repair + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createRepair(req: Request, res: Response): Promise { + const description = req.body.description; + const responsible = req.body.responsible; + const reports = req.body.reports; + 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 createRepair: CreateRepairCommand = { + description, + affectedId, + affected, + responsible, + reports, + }; + let repairId = await RepairCommandHandler.create(createRepair); + + res.status(200).send(repairId); +} + +/** + * @description update repair by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateRepairById(req: Request, res: Response): Promise { + const repairId = req.params.id; + const status = req.body.status; + const description = req.body.description; + const responsible = req.body.responsible; + const reports = req.body.reports; + + let updateRepair: UpdateRepairCommand = { + id: repairId, + status, + description, + responsible, + reports, + }; + await RepairCommandHandler.update(updateRepair); + + res.sendStatus(204); +} diff --git a/src/data-source.ts b/src/data-source.ts index b54707a..3f337f1 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -65,7 +65,8 @@ import { maintenance } from "./entity/unit/maintenance"; import { BackupAndResetDatabase1749296262915 } from "./migrations/1749296262915-BackupAndResetDatabase"; import { CreateSchema1749296280721 } from "./migrations/1749296280721-CreateSchema"; import { UpdateNewsletterQueryRelation1752502069178 } from "./migrations/1752502069178-updateNewsletterQueryRelation"; -import { UnitBase1749361405703 } from "./migrations/1749361405703-UnitBase"; +import { UnitBase1752914551204 } from "./migrations/1752914551204-UnitBase"; +import { repair } from "./entity/unit/repair"; configCheck(); @@ -131,6 +132,7 @@ const dataSource = new DataSource({ wearable, damageReport, maintenance, + repair, inspectionPlan, inspectionVersionedPlan, inspectionPoint, @@ -140,8 +142,8 @@ const dataSource = new DataSource({ migrations: [ BackupAndResetDatabase1749296262915, CreateSchema1749296280721, + UnitBase1752914551204, UpdateNewsletterQueryRelation1752502069178, - UnitBase1749361405703, ], migrationsRun: true, migrationsTransactionMode: "each", diff --git a/src/entity/unit/damageReport.ts b/src/entity/unit/damageReport.ts index bfb45ae..59ed8c2 100644 --- a/src/entity/unit/damageReport.ts +++ b/src/entity/unit/damageReport.ts @@ -2,7 +2,7 @@ import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } f import { equipment } from "./equipment/equipment"; import { wearable } from "./wearable/wearable"; import { vehicle } from "./vehicle/vehicle"; -import { maintenance } from "./maintenance"; +import { repair } from "./repair"; @Entity() export class damageReport { @@ -34,7 +34,7 @@ export class damageReport { type: "text", transformer: { from(value: string): Array { - return value.split(",").filter((i) => !!i); + return (value ?? "").split(",").filter((i) => !!i); }, to(value: Array): string { return value.join(","); @@ -56,7 +56,7 @@ export class damageReport { wearableId?: string; @Column({ nullable: true, default: null }) - maintenanceId?: string; + repairId?: string; @ManyToOne(() => equipment, (e) => e.reports, { nullable: true, @@ -79,10 +79,10 @@ export class damageReport { }) wearable?: wearable; - @ManyToOne(() => maintenance, (m) => m.reports, { + @ManyToOne(() => repair, (m) => m.reports, { nullable: true, onDelete: "SET NULL", onUpdate: "RESTRICT", }) - maintenance?: maintenance; + repair?: repair; } diff --git a/src/entity/unit/equipment/equipment.ts b/src/entity/unit/equipment/equipment.ts index 2d3b022..21f5022 100644 --- a/src/entity/unit/equipment/equipment.ts +++ b/src/entity/unit/equipment/equipment.ts @@ -4,6 +4,7 @@ import { equipmentType } from "./equipmentType"; import { damageReport } from "../damageReport"; import { inspection } from "../inspection/inspection"; import { maintenance } from "../maintenance"; +import { repair } from "../repair"; @Entity() export class equipment { @@ -38,6 +39,9 @@ export class equipment { @OneToMany(() => damageReport, (d) => d.equipment, { cascade: ["insert"] }) reports: damageReport[]; + @OneToMany(() => repair, (d) => d.equipment, { cascade: ["insert"] }) + repairs: repair[]; + @OneToMany(() => maintenance, (m) => m.equipment, { cascade: ["insert"] }) maintenances: maintenance[]; diff --git a/src/entity/unit/maintenance.ts b/src/entity/unit/maintenance.ts index 3c9b7c0..15ce3c2 100644 --- a/src/entity/unit/maintenance.ts +++ b/src/entity/unit/maintenance.ts @@ -1,8 +1,9 @@ -import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { Column, ColumnType, 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"; +import { getTypeByORM } from "../../migrations/ormHelper"; @Entity() export class maintenance { @@ -12,12 +13,12 @@ export class maintenance { @CreateDateColumn() createdAt: Date; + @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true, default: null }) + finishedAt?: Date; + @Column({ type: "varchar", length: 255 }) status: string; - @Column({ type: "boolean", default: false }) - done: boolean; - @Column({ type: "text" }) description: string; @@ -50,7 +51,4 @@ export class maintenance { onUpdate: "RESTRICT", }) wearable?: wearable; - - @OneToMany(() => damageReport, (dr) => dr.maintenance) - reports: damageReport[]; } diff --git a/src/entity/unit/repair.ts b/src/entity/unit/repair.ts new file mode 100644 index 0000000..0eac000 --- /dev/null +++ b/src/entity/unit/repair.ts @@ -0,0 +1,76 @@ +import { Column, ColumnType, 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"; +import { getTypeByORM } from "../../migrations/ormHelper"; + +@Entity() +export class repair { + @PrimaryGeneratedColumn("uuid") + id: string; + + @CreateDateColumn() + createdAt: Date; + + @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true, default: null }) + finishedAt?: Date; + + @Column({ type: "varchar", length: 255 }) + status: string; + + @Column({ type: "varchar", length: 255 }) + responsible: string; + + @Column({ type: "text" }) + description: string; + + @Column({ + type: "text", + transformer: { + from(value: string): Array { + return (value ?? "").split(",").filter((i) => !!i); + }, + to(value: Array): string { + return value.join(","); + }, + }, + }) + images: string[]; + + @Column({ type: "varchar", length: 255 }) + reportDocument: 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.repair) + reports: damageReport[]; +} diff --git a/src/entity/unit/vehicle/vehicle.ts b/src/entity/unit/vehicle/vehicle.ts index 3f75651..8102bf0 100644 --- a/src/entity/unit/vehicle/vehicle.ts +++ b/src/entity/unit/vehicle/vehicle.ts @@ -4,6 +4,7 @@ import { vehicleType } from "./vehicleType"; import { damageReport } from "../damageReport"; import { inspection } from "../inspection/inspection"; import { maintenance } from "../maintenance"; +import { repair } from "../repair"; @Entity() export class vehicle { @@ -38,6 +39,9 @@ export class vehicle { @OneToMany(() => damageReport, (d) => d.vehicle, { cascade: ["insert"] }) reports: damageReport[]; + @OneToMany(() => repair, (d) => d.vehicle, { cascade: ["insert"] }) + repairs: repair[]; + @OneToMany(() => maintenance, (m) => m.vehicle, { cascade: ["insert"] }) maintenances: maintenance[]; diff --git a/src/entity/unit/wearable/wearable.ts b/src/entity/unit/wearable/wearable.ts index 2c5ce44..e4ecf22 100644 --- a/src/entity/unit/wearable/wearable.ts +++ b/src/entity/unit/wearable/wearable.ts @@ -5,6 +5,7 @@ import { damageReport } from "../damageReport"; import { member } from "../../club/member/member"; import { inspection } from "../inspection/inspection"; import { maintenance } from "../maintenance"; +import { repair } from "../repair"; @Entity() export class wearable { @@ -49,6 +50,9 @@ export class wearable { @OneToMany(() => damageReport, (d) => d.wearable, { cascade: ["insert"] }) reports: damageReport[]; + @OneToMany(() => repair, (d) => d.wearable, { cascade: ["insert"] }) + repairs: repair[]; + @OneToMany(() => maintenance, (m) => m.wearable, { cascade: ["insert"] }) maintenances: maintenance[]; diff --git a/src/factory/admin/unit/damageReport.ts b/src/factory/admin/unit/damageReport.ts index fe6ff61..969593b 100644 --- a/src/factory/admin/unit/damageReport.ts +++ b/src/factory/admin/unit/damageReport.ts @@ -2,6 +2,7 @@ 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 RepairFactory from "./repair"; import VehicleFactory from "./vehicle/vehicle"; import WearableFactory from "./wearable/wearable"; @@ -51,7 +52,7 @@ export default abstract class DamageReportFactory { images: record.images, reportedBy: record?.reportedBy, ...assigned, - maintenance: record.maintenance ? MaintenanceFactory.mapToSingle(record.maintenance) : null, + repair: record.repair ? RepairFactory.mapToSingle(record.repair) : null, }; } diff --git a/src/factory/admin/unit/maintenance.ts b/src/factory/admin/unit/maintenance.ts index 69f4c78..fb5a464 100644 --- a/src/factory/admin/unit/maintenance.ts +++ b/src/factory/admin/unit/maintenance.ts @@ -37,10 +37,8 @@ export default abstract class MaintenanceFactory { id: record.id, createdAt: record.createdAt, status: record.status, - done: record.done, description: record.description, ...assigned, - reports: record.reports ? DamageReportFactory.mapToBase(record.reports) : [], }; } diff --git a/src/factory/admin/unit/repair.ts b/src/factory/admin/unit/repair.ts new file mode 100644 index 0000000..2ff8206 --- /dev/null +++ b/src/factory/admin/unit/repair.ts @@ -0,0 +1,64 @@ +import { repair } from "../../../entity/unit/repair"; +import { RepairAssigned, RepairViewModel } from "../../../viewmodel/admin/unit/repair.models"; +import DamageReportFactory from "./damageReport"; +import EquipmentFactory from "./equipment/equipment"; +import VehicleFactory from "./vehicle/vehicle"; +import WearableFactory from "./wearable/wearable"; + +export default abstract class RepairFactory { + /** + * @description map record to repair + * @param {repair} record + * @returns {RepairViewModel} + */ + public static mapToSingle(record: repair): RepairViewModel { + let assigned: RepairAssigned; + 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 if (record?.wearableId) { + assigned = { + relatedId: record.wearableId, + assigned: "wearable", + related: WearableFactory.mapToSingle(record.wearable), + }; + } else { + assigned = { + relatedId: undefined, + assigned: undefined, + related: undefined, + }; + } + + return { + id: record.id, + createdAt: record.createdAt, + finishedAt: record.finishedAt, + status: record.status, + responsible: record.responsible, + description: record.description, + images: record.images, + reportDocument: record.reportDocument, + reports: record.reports ? DamageReportFactory.mapToBase(record.reports) : [], + ...assigned, + }; + } + + /** + * @description map records to repair + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts index d510f8d..b986cea 100644 --- a/src/helpers/backupHelper.ts +++ b/src/helpers/backupHelper.ts @@ -516,6 +516,7 @@ export default abstract class BackupHelper { wearable: await dataSource.getRepository("wearable").find(), maintenance: await dataSource.getRepository("maintenance").find(), damage_report: await dataSource.getRepository("damage_report").find(), + repair: await dataSource.getRepository("repair").find(), inspection: await dataSource.getRepository("inspection").find(), inspection_point_result: await dataSource.getRepository("inspection_point_result").find(), }; @@ -903,23 +904,26 @@ export default abstract class BackupHelper { 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("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"]); + 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("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("repair").save(data["repair"] ?? []); + 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"]); + await this.transactionManager.getRepository("inspection").save(data["inspection"] ?? []); + await this.transactionManager.getRepository("inspection_point_result").save(data["inspection_point_result"] ?? []); } } diff --git a/src/migrations/1749361405703-UnitBase.ts b/src/migrations/1752914551204-UnitBase.ts similarity index 90% rename from src/migrations/1749361405703-UnitBase.ts rename to src/migrations/1752914551204-UnitBase.ts index 97c657f..685bf42 100644 --- a/src/migrations/1749361405703-UnitBase.ts +++ b/src/migrations/1752914551204-UnitBase.ts @@ -14,12 +14,12 @@ import { wearable_type_table, wearable_table, } from "./baseSchemaTables/unit"; -import { maintenance_table, damage_report_table } from "./baseSchemaTables/unit_extend"; +import { maintenance_table, damage_report_table, repair_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"; +export class UnitBase1752914551204 implements MigrationInterface { + name = "UnitBase1752914551204"; public async up(queryRunner: QueryRunner): Promise { await queryRunner.createTable(equipment_type_table, true, true, true); @@ -30,6 +30,7 @@ export class UnitBase1749361405703 implements MigrationInterface { await queryRunner.createTable(wearable_table, true, true, true); await queryRunner.createTable(maintenance_table, true, true, true); + await queryRunner.createTable(repair_table, true, true, true); await queryRunner.createTable(damage_report_table, true, true, true); await queryRunner.createTable(inspection_plan_table, true, true, true); @@ -61,6 +62,7 @@ export class UnitBase1749361405703 implements MigrationInterface { await queryRunner.dropTable(inspection_plan_table, true, true, true); await queryRunner.dropTable(damage_report_table, true, true, true); + await queryRunner.dropTable(repair_table, true, true, true); await queryRunner.dropTable(maintenance_table, true, true, true); await queryRunner.dropTable(wearable_table, true, true, true); diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index 171c808..82eff28 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -17,7 +17,7 @@ export const damage_report_table = new Table({ { name: "equipmentId", ...getTypeByORM("uuid", true) }, { name: "vehicleId", ...getTypeByORM("uuid", true) }, { name: "wearableId", ...getTypeByORM("uuid", true) }, - { name: "maintenanceId", ...getTypeByORM("uuid", true) }, + { name: "repairId", ...getTypeByORM("uuid", true) }, ], foreignKeys: [ new TableForeignKey({ @@ -42,9 +42,9 @@ export const damage_report_table = new Table({ onUpdate: "RESTRICT", }), new TableForeignKey({ - columnNames: ["maintenanceId"], + columnNames: ["repairId"], referencedColumnNames: ["id"], - referencedTableName: "maintenance", + referencedTableName: "repair", onDelete: "SET NULL", onUpdate: "RESTRICT", }), @@ -56,8 +56,8 @@ export const maintenance_table = new Table({ columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, + { name: "finishedAt", ...getTypeByORM("datetime", true) }, { 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) }, @@ -87,3 +87,43 @@ export const maintenance_table = new Table({ }), ], }); + +export const repair_table = new Table({ + name: "repair", + columns: [ + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, + { name: "finishedAt", ...getTypeByORM("datetime", true) }, + { name: "status", ...getTypeByORM("varchar") }, + { name: "responsible", ...getTypeByORM("varchar") }, + { name: "description", ...getTypeByORM("text") }, + { name: "images", ...getTypeByORM("text") }, + { name: "reportDocument", ...getTypeByORM("varchar") }, + { 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 3a6edf2..a3e8b33 100644 --- a/src/routes/admin/index.ts +++ b/src/routes/admin/index.ts @@ -43,6 +43,7 @@ import inspection from "./unit/inspection"; import inspectionPlan from "./unit/inspectionPlan"; import damageReport from "./unit/damageReport"; import maintenance from "./unit/maintenance"; +import repair from "./unit/repair"; var router = express.Router({ mergeParams: true }); @@ -283,5 +284,15 @@ router.use( ]), maintenance ); +router.use( + "/repair", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermission: "read", section: "unit", module: "repair" }, + { requiredPermission: "read", section: "unit", module: "equipment" }, + { requiredPermission: "read", section: "unit", module: "vehicle" }, + { requiredPermission: "read", section: "unit", module: "wearable" }, + ]), + repair +); export default router; diff --git a/src/routes/admin/unit/repair.ts b/src/routes/admin/unit/repair.ts new file mode 100644 index 0000000..68bd58a --- /dev/null +++ b/src/routes/admin/unit/repair.ts @@ -0,0 +1,52 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import { + createRepair, + getAllRepairsByStatus, + getAllRepairsForRelated, + getRepairById, + updateRepairById, +} from "../../../controller/admin/unit/repairController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllRepairsByStatus(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 getAllRepairsForRelated(req, res); + } +); + +router.get("/:id", async (req: Request, res: Response) => { + await getRepairById(req, res); +}); + +router.post( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), + async (req: Request, res: Response) => { + await createRepair(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), + async (req: Request, res: Response) => { + await updateRepairById(req, res); + } +); + +export default router; diff --git a/src/service/unit/damageReportService.ts b/src/service/unit/damageReportService.ts index 21072c7..85b5e1d 100644 --- a/src/service/unit/damageReportService.ts +++ b/src/service/unit/damageReportService.ts @@ -10,7 +10,7 @@ export default abstract class DamageReportService { .leftJoinAndSelect("damageReport.equipment", "equipment") .leftJoinAndSelect("damageReport.vehicle", "vehicle") .leftJoinAndSelect("damageReport.wearable", "wearable") - .leftJoinAndSelect("damageReport.maintenance", "maintenance"); + .leftJoinAndSelect("damageReport.repair", "repair"); /** * @description get all damageReports By done diff --git a/src/service/unit/maintenanceService.ts b/src/service/unit/maintenanceService.ts index d9d997d..d2917e2 100644 --- a/src/service/unit/maintenanceService.ts +++ b/src/service/unit/maintenanceService.ts @@ -1,3 +1,4 @@ +import { Not, IsNull } from "typeorm"; import { dataSource } from "../../data-source"; import { maintenance } from "../../entity/unit/maintenance"; import DatabaseActionException from "../../exceptions/databaseActionException"; @@ -9,8 +10,7 @@ export default abstract class MaintenanceService { .createQueryBuilder("maintenance") .leftJoinAndSelect("maintenance.equipment", "equipment") .leftJoinAndSelect("maintenance.vehicle", "vehicle") - .leftJoinAndSelect("maintenance.wearable", "wearable") - .leftJoinAndSelect("maintenance.reports", "reports"); + .leftJoinAndSelect("maintenance.wearable", "wearable"); /** * @description get all maintenances @@ -28,7 +28,7 @@ export default abstract class MaintenanceService { noLimit?: boolean; } ): Promise<[Array, number]> { - let query = this.query().where({ done }); + let query = this.query().where({ finishedAt: done ? Not(IsNull()) : IsNull() }); if (!noLimit) { query = query.offset(offset).limit(count); } diff --git a/src/service/unit/repairService.ts b/src/service/unit/repairService.ts new file mode 100644 index 0000000..731b8ad --- /dev/null +++ b/src/service/unit/repairService.ts @@ -0,0 +1,96 @@ +import { IsNull, Not } from "typeorm"; +import { dataSource } from "../../data-source"; +import { repair } from "../../entity/unit/repair"; +import DatabaseActionException from "../../exceptions/databaseActionException"; + +export default abstract class RepairService { + private static query = () => + dataSource + .getRepository(repair) + .createQueryBuilder("repair") + .leftJoinAndSelect("repair.equipment", "equipment") + .leftJoinAndSelect("repair.vehicle", "vehicle") + .leftJoinAndSelect("repair.wearable", "wearable") + .leftJoinAndSelect("repair.reports", "reports"); + + /** + * @description get all repairs + * @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({ finishedAt: done ? Not(IsNull()) : IsNull() }); + if (!noLimit) { + query = query.offset(offset).limit(count); + } + + return await query + .orderBy("repair.createdAt", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "repair", err); + }); + } + + /** + * @description get all repairs 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("repair.createdAt", "ASC") + .getManyAndCount() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "repair", err); + }); + } + + /** + * @description get repair 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", "repair", err); + }); + } +} diff --git a/src/type/permissionTypes.ts b/src/type/permissionTypes.ts index 2442d9f..091c0c3 100644 --- a/src/type/permissionTypes.ts +++ b/src/type/permissionTypes.ts @@ -22,6 +22,7 @@ export type PermissionModule = | "respiratory_mission" | "damage_report" | "maintenance" + | "repair" // configuration | "qualification" | "award" @@ -97,6 +98,7 @@ export const permissionModules: Array = [ "respiratory_mission", "damage_report", "maintenance", + "repair", // configuration "qualification", "award", @@ -134,6 +136,7 @@ export const sectionsAndModules: SectionsAndModulesObject = { "respiratory_mission", "damage_report", "maintenance", + "repair", ], configuration: [ "qualification", diff --git a/src/viewmodel/admin/unit/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport.models.ts index 15b03f2..fc510cf 100644 --- a/src/viewmodel/admin/unit/damageReport.models.ts +++ b/src/viewmodel/admin/unit/damageReport.models.ts @@ -1,5 +1,6 @@ import { EquipmentViewModel } from "./equipment/equipment.models"; import { MaintenanceViewModel } from "./maintenance.models"; +import { RepairViewModel } from "./repair.models"; import { VehicleViewModel } from "./vehicle/vehicle.models"; import { WearableViewModel } from "./wearable/wearable.models"; @@ -31,5 +32,5 @@ export type DamageReportViewModel = { noteByWorker: string; images: string[]; reportedBy: string; - maintenance?: MaintenanceViewModel; + repair?: RepairViewModel; } & DamageReportAssigned; diff --git a/src/viewmodel/admin/unit/maintenance.models.ts b/src/viewmodel/admin/unit/maintenance.models.ts index 98f22f2..7f85029 100644 --- a/src/viewmodel/admin/unit/maintenance.models.ts +++ b/src/viewmodel/admin/unit/maintenance.models.ts @@ -1,4 +1,3 @@ -import { DamageReportViewModel } from "./damageReport.models"; import { EquipmentViewModel } from "./equipment/equipment.models"; import { VehicleViewModel } from "./vehicle/vehicle.models"; import { WearableViewModel } from "./wearable/wearable.models"; @@ -24,7 +23,5 @@ export type MaintenanceViewModel = { id: string; createdAt: Date; status: string; - done: boolean; description: string; - reports: DamageReportViewModel[]; } & MaintenanceAssigned; diff --git a/src/viewmodel/admin/unit/repair.models.ts b/src/viewmodel/admin/unit/repair.models.ts new file mode 100644 index 0000000..044b650 --- /dev/null +++ b/src/viewmodel/admin/unit/repair.models.ts @@ -0,0 +1,33 @@ +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 RepairAssigned = { + relatedId: string; +} & ( + | { + assigned: "equipment"; + related: EquipmentViewModel; + } + | { + assigned: "vehicle"; + related: VehicleViewModel; + } + | { + assigned: "wearable"; + related: WearableViewModel; + } +); + +export type RepairViewModel = { + id: string; + createdAt: Date; + finishedAt?: Date; + status: string; + responsible: string; + description: string; + images: string[]; + reportDocument: string; + reports: DamageReportViewModel[]; +} & RepairAssigned; From ecadd8431d56aece408a22ab32247c1d0cd1bc2e Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Mon, 21 Jul 2025 11:01:57 +0200 Subject: [PATCH 45/55] extend report by title --- src/command/unit/damageReportCommand.ts | 1 + src/command/unit/damageReportCommandHandler.ts | 1 + src/controller/admin/unit/damageReportController.ts | 2 ++ src/controller/publicController.ts | 2 ++ src/entity/unit/damageReport.ts | 3 +++ src/factory/admin/unit/damageReport.ts | 1 + src/migrations/baseSchemaTables/unit_extend.ts | 1 + src/viewmodel/admin/unit/damageReport.models.ts | 1 + 8 files changed, 12 insertions(+) diff --git a/src/command/unit/damageReportCommand.ts b/src/command/unit/damageReportCommand.ts index 1b397c2..60b8d17 100644 --- a/src/command/unit/damageReportCommand.ts +++ b/src/command/unit/damageReportCommand.ts @@ -1,4 +1,5 @@ export interface CreateDamageReportCommand { + title: string; description: string; location: string; noteByReporter: string; diff --git a/src/command/unit/damageReportCommandHandler.ts b/src/command/unit/damageReportCommandHandler.ts index d7d8485..e8f6835 100644 --- a/src/command/unit/damageReportCommandHandler.ts +++ b/src/command/unit/damageReportCommandHandler.ts @@ -21,6 +21,7 @@ export default abstract class DamageReportCommandHandler { .into(damageReport) .values({ status: "eingereicht", + title: createDamageReport.title, description: createDamageReport.description, location: createDamageReport.location, noteByReporter: createDamageReport.noteByReporter, diff --git a/src/controller/admin/unit/damageReportController.ts b/src/controller/admin/unit/damageReportController.ts index 055b3bc..e286927 100644 --- a/src/controller/admin/unit/damageReportController.ts +++ b/src/controller/admin/unit/damageReportController.ts @@ -94,6 +94,7 @@ export async function provideDamageReportImageUpload(req: Request, res: Response * @returns {Promise<*>} */ export async function createDamageReport(req: Request, res: Response): Promise { + const title = req.body.title; const description = req.body.description; const location = req.body.location; const note = req.body.note; @@ -106,6 +107,7 @@ export async function createDamageReport(req: Request, res: Response): Promise Date: Mon, 21 Jul 2025 12:58:19 +0200 Subject: [PATCH 46/55] repair create --- .../unit/damageReportCommandHandler.ts | 20 ++++++++ src/command/unit/repairCommand.ts | 2 + src/command/unit/repairCommandHandler.ts | 48 ++++++++++++------- .../admin/unit/damageReportController.ts | 21 +++++++- src/controller/admin/unit/repairController.ts | 4 ++ src/entity/unit/damageReport.ts | 2 +- src/entity/unit/repair.ts | 9 ++-- src/factory/admin/unit/damageReport.ts | 6 +-- src/factory/admin/unit/repair.ts | 7 +-- .../baseSchemaTables/unit_extend.ts | 2 +- src/routes/admin/unit/damageReport.ts | 5 ++ src/routes/admin/unit/repair.ts | 2 +- src/service/unit/damageReportService.ts | 38 ++++++++++++++- src/viewmodel/admin/unit/repair.models.ts | 3 +- 14 files changed, 138 insertions(+), 31 deletions(-) diff --git a/src/command/unit/damageReportCommandHandler.ts b/src/command/unit/damageReportCommandHandler.ts index e8f6835..1bf33ca 100644 --- a/src/command/unit/damageReportCommandHandler.ts +++ b/src/command/unit/damageReportCommandHandler.ts @@ -1,3 +1,4 @@ +import { EntityManager, UpdateResult } from "typeorm"; import { dataSource } from "../../data-source"; import { damageReport } from "../../entity/unit/damageReport"; import DatabaseActionException from "../../exceptions/databaseActionException"; @@ -82,6 +83,25 @@ export default abstract class DamageReportCommandHandler { }); } + /** + * @description update damageReport related maintenance in transaction + * @param {UpdateDamageReportCommand} updateDamageReport + * @returns {Promise} + */ + static async updateRelatedMaintenanceTransaction( + manager: EntityManager, + updateDamageReport: UpdateDamageReportRelatedRepairCommand + ): Promise { + return await manager + .createQueryBuilder() + .update(damageReport) + .set({ + repairId: updateDamageReport.repairId, + }) + .where("id = :id", { id: updateDamageReport.id }) + .execute(); + } + /** * @description delete damageReport * @param {DeleteDamageReportCommand} deleteDamageReport diff --git a/src/command/unit/repairCommand.ts b/src/command/unit/repairCommand.ts index bd7af10..998f62e 100644 --- a/src/command/unit/repairCommand.ts +++ b/src/command/unit/repairCommand.ts @@ -1,4 +1,5 @@ export interface CreateRepairCommand { + title: string; description: string; responsible: string; affectedId: string; @@ -9,6 +10,7 @@ export interface CreateRepairCommand { export interface UpdateRepairCommand { id: string; status: string; + title: string; description: string; responsible: string; reports: string[]; diff --git a/src/command/unit/repairCommandHandler.ts b/src/command/unit/repairCommandHandler.ts index b2713f5..00338ce 100644 --- a/src/command/unit/repairCommandHandler.ts +++ b/src/command/unit/repairCommandHandler.ts @@ -1,31 +1,46 @@ import { dataSource } from "../../data-source"; import { repair } from "../../entity/unit/repair"; import DatabaseActionException from "../../exceptions/databaseActionException"; +import DamageReportCommandHandler from "./damageReportCommandHandler"; import { CreateRepairCommand, UpdateRepairCommand, DeleteRepairCommand } from "./repairCommand"; export default abstract class RepairCommandHandler { /** * @description create repair * @param {CreateRepairCommand} createRepair - * @returns {Promise} + * @returns {Promise} */ - static async create(createRepair: CreateRepairCommand): Promise { + static async create(createRepair: CreateRepairCommand): Promise { + let resultId = ""; return await dataSource - .createQueryBuilder() - .insert() - .into(repair) - .values({ - status: "in Arbeit", - description: createRepair.description, - reports: createRepair.reports.map((r) => ({ id: r })), - responsible: createRepair.responsible, - equipmentId: createRepair.affected == "equipment" ? createRepair.affectedId : null, - vehicleId: createRepair.affected == "vehicle" ? createRepair.affectedId : null, - wearableId: createRepair.affected == "wearable" ? createRepair.affectedId : null, + .transaction(async (manager) => { + await manager + .createQueryBuilder() + .insert() + .into(repair) + .values({ + status: "in Arbeit", + title: createRepair.title, + description: createRepair.description, + responsible: createRepair.responsible, + equipmentId: createRepair.affected == "equipment" ? createRepair.affectedId : null, + vehicleId: createRepair.affected == "vehicle" ? createRepair.affectedId : null, + wearableId: createRepair.affected == "wearable" ? createRepair.affectedId : null, + }) + .execute() + .then((result) => { + resultId = result.identifiers[0].id; + }); + + for (const report of createRepair.reports) { + await DamageReportCommandHandler.updateRelatedMaintenanceTransaction(manager, { + id: report, + repairId: resultId, + }); + } }) - .execute() - .then((result) => { - return result.identifiers[0].id; + .then(() => { + return resultId; }) .catch((err) => { throw new DatabaseActionException("CREATE", "repair", err); @@ -43,6 +58,7 @@ export default abstract class RepairCommandHandler { .update(repair) .set({ status: updateRepair.status, + title: updateRepair.title, description: updateRepair.description, reports: updateRepair.reports.map((r) => ({ id: r })), responsible: updateRepair.responsible, diff --git a/src/controller/admin/unit/damageReportController.ts b/src/controller/admin/unit/damageReportController.ts index e286927..4db079c 100644 --- a/src/controller/admin/unit/damageReportController.ts +++ b/src/controller/admin/unit/damageReportController.ts @@ -18,7 +18,7 @@ export async function getAllDamageReportsByStatus(req: Request, res: Response): 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 }); + let [damageReports, total] = await DamageReportService.getAllByStatus(done, { offset, count, noLimit }); res.json({ damageReports: DamageReportFactory.mapToBase(damageReports), @@ -72,6 +72,25 @@ export async function getDamageReportById(req: Request, res: Response): Promise< res.json(DamageReportFactory.mapToSingle(damageReport)); } +/** + * @description get reports by Ids + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getDamageReportsByIds(req: Request, res: Response): Promise { + let ids = req.body.ids as Array; + + let [damageReports, total] = await DamageReportService.getAll({ noLimit: true, ids }); + + res.json({ + damageReports: DamageReportFactory.mapToBase(damageReports), + total: total, + offset: 0, + count: total, + }); +} + /** * @description provide uploaded image for damage report * @param req {Request} Express req object diff --git a/src/controller/admin/unit/repairController.ts b/src/controller/admin/unit/repairController.ts index 9e6a25e..514af21 100644 --- a/src/controller/admin/unit/repairController.ts +++ b/src/controller/admin/unit/repairController.ts @@ -79,6 +79,7 @@ export async function getRepairById(req: Request, res: Response): Promise { * @returns {Promise<*>} */ export async function createRepair(req: Request, res: Response): Promise { + const title = req.body.title; const description = req.body.description; const responsible = req.body.responsible; const reports = req.body.reports; @@ -89,6 +90,7 @@ export async function createRepair(req: Request, res: Response): Promise { throw new BadRequestException("set assigned to equipment or vehicle or wearable"); let createRepair: CreateRepairCommand = { + title, description, affectedId, affected, @@ -109,6 +111,7 @@ export async function createRepair(req: Request, res: Response): Promise { export async function updateRepairById(req: Request, res: Response): Promise { const repairId = req.params.id; const status = req.body.status; + const title = req.body.title; const description = req.body.description; const responsible = req.body.responsible; const reports = req.body.reports; @@ -116,6 +119,7 @@ export async function updateRepairById(req: Request, res: Response): Promise { return (value ?? "").split(",").filter((i) => !!i); }, - to(value: Array): string { + to(value: Array = []): string { return value.join(","); }, }, diff --git a/src/entity/unit/repair.ts b/src/entity/unit/repair.ts index 0eac000..707f652 100644 --- a/src/entity/unit/repair.ts +++ b/src/entity/unit/repair.ts @@ -22,6 +22,9 @@ export class repair { @Column({ type: "varchar", length: 255 }) responsible: string; + @Column({ type: "varchar", length: 255 }) + title: string; + @Column({ type: "text" }) description: string; @@ -31,15 +34,15 @@ export class repair { from(value: string): Array { return (value ?? "").split(",").filter((i) => !!i); }, - to(value: Array): string { + to(value: Array = []): string { return value.join(","); }, }, }) images: string[]; - @Column({ type: "varchar", length: 255 }) - reportDocument: string; + @Column({ type: "varchar", length: 255, nullable: true, default: null }) + reportDocument?: string; @Column({ nullable: true, default: null }) equipmentId?: string; diff --git a/src/factory/admin/unit/damageReport.ts b/src/factory/admin/unit/damageReport.ts index 341ed78..29a8a56 100644 --- a/src/factory/admin/unit/damageReport.ts +++ b/src/factory/admin/unit/damageReport.ts @@ -18,19 +18,19 @@ export default abstract class DamageReportFactory { assigned = { relatedId: record.equipmentId, assigned: "equipment", - related: EquipmentFactory.mapToSingle(record.equipment), + related: record.equipment ? EquipmentFactory.mapToSingle(record.equipment) : undefined, }; } else if (record?.vehicleId) { assigned = { relatedId: record.vehicleId, assigned: "vehicle", - related: VehicleFactory.mapToSingle(record.vehicle), + related: record.vehicle ? VehicleFactory.mapToSingle(record.vehicle) : undefined, }; } else if (record?.wearableId) { assigned = { relatedId: record.wearableId, assigned: "wearable", - related: WearableFactory.mapToSingle(record.wearable), + related: record.wearable ? WearableFactory.mapToSingle(record.wearable) : undefined, }; } else { assigned = { diff --git a/src/factory/admin/unit/repair.ts b/src/factory/admin/unit/repair.ts index 2ff8206..e656fc0 100644 --- a/src/factory/admin/unit/repair.ts +++ b/src/factory/admin/unit/repair.ts @@ -17,19 +17,19 @@ export default abstract class RepairFactory { assigned = { relatedId: record.equipmentId, assigned: "equipment", - related: EquipmentFactory.mapToSingle(record.equipment), + related: record.equipment ? EquipmentFactory.mapToSingle(record.equipment) : undefined, }; } else if (record?.vehicleId) { assigned = { relatedId: record.vehicleId, assigned: "vehicle", - related: VehicleFactory.mapToSingle(record.vehicle), + related: record.vehicle ? VehicleFactory.mapToSingle(record.vehicle) : undefined, }; } else if (record?.wearableId) { assigned = { relatedId: record.wearableId, assigned: "wearable", - related: WearableFactory.mapToSingle(record.wearable), + related: record.wearable ? WearableFactory.mapToSingle(record.wearable) : undefined, }; } else { assigned = { @@ -45,6 +45,7 @@ export default abstract class RepairFactory { finishedAt: record.finishedAt, status: record.status, responsible: record.responsible, + title: record.title, description: record.description, images: record.images, reportDocument: record.reportDocument, diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index 692be13..ad6c22a 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -99,7 +99,7 @@ export const repair_table = new Table({ { name: "responsible", ...getTypeByORM("varchar") }, { name: "description", ...getTypeByORM("text") }, { name: "images", ...getTypeByORM("text") }, - { name: "reportDocument", ...getTypeByORM("varchar") }, + { name: "reportDocument", ...getTypeByORM("varchar", true) }, { name: "equipmentId", ...getTypeByORM("uuid", true) }, { name: "vehicleId", ...getTypeByORM("uuid", true) }, { name: "wearableId", ...getTypeByORM("uuid", true) }, diff --git a/src/routes/admin/unit/damageReport.ts b/src/routes/admin/unit/damageReport.ts index 8a19e95..95d42b2 100644 --- a/src/routes/admin/unit/damageReport.ts +++ b/src/routes/admin/unit/damageReport.ts @@ -11,6 +11,7 @@ import { getAllDamageReportsByStatus, getAllDamageReportsForRelated, getDamageReportById, + getDamageReportsByIds, provideDamageReportImageUpload, updateDamageReportById, } from "../../../controller/admin/unit/damageReportController"; @@ -40,6 +41,10 @@ router.get("/:id", async (req: Request, res: Response) => { await getDamageReportById(req, res); }); +router.post("/ids", async (req: Request, res: Response) => { + await getDamageReportsByIds(req, res); +}); + router.get("/:id/:filename", async (req: Request, res: Response) => { await provideDamageReportImageUpload(req, res); }); diff --git a/src/routes/admin/unit/repair.ts b/src/routes/admin/unit/repair.ts index 68bd58a..7fbaf9e 100644 --- a/src/routes/admin/unit/repair.ts +++ b/src/routes/admin/unit/repair.ts @@ -34,7 +34,7 @@ router.get("/:id", async (req: Request, res: Response) => { }); router.post( - "/:id", + "/", PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), async (req: Request, res: Response) => { await createRepair(req, res); diff --git a/src/service/unit/damageReportService.ts b/src/service/unit/damageReportService.ts index 85b5e1d..3ad8c99 100644 --- a/src/service/unit/damageReportService.ts +++ b/src/service/unit/damageReportService.ts @@ -1,3 +1,4 @@ +import { In } from "typeorm"; import { dataSource } from "../../data-source"; import { damageReport } from "../../entity/unit/damageReport"; import DatabaseActionException from "../../exceptions/databaseActionException"; @@ -11,12 +12,47 @@ export default abstract class DamageReportService { .leftJoinAndSelect("damageReport.vehicle", "vehicle") .leftJoinAndSelect("damageReport.wearable", "wearable") .leftJoinAndSelect("damageReport.repair", "repair"); + /** + * @description get all damageReports By done + * @returns {Promise<[Array, number]>} + */ + static async getAll({ + offset = 0, + count = 25, + noLimit = false, + ids = [], + }: { + offset?: number; + count?: number; + noLimit?: boolean; + ids?: Array; + }): Promise<[Array, number]> { + let query = this.query(); + + if (ids.length != 0) { + query = query.where({ id: In(ids) }); + } + + 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 done * @returns {Promise<[Array, number]>} */ - static async getAll( + static async getAllByStatus( done = false, { offset = 0, diff --git a/src/viewmodel/admin/unit/repair.models.ts b/src/viewmodel/admin/unit/repair.models.ts index 044b650..c0b21b4 100644 --- a/src/viewmodel/admin/unit/repair.models.ts +++ b/src/viewmodel/admin/unit/repair.models.ts @@ -26,8 +26,9 @@ export type RepairViewModel = { finishedAt?: Date; status: string; responsible: string; + title: string; description: string; images: string[]; - reportDocument: string; + reportDocument?: string; reports: DamageReportViewModel[]; } & RepairAssigned; From 85fa912024dbac142efa6c9abcf214a08827dc7b Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Mon, 21 Jul 2025 12:59:39 +0200 Subject: [PATCH 47/55] add repair title to migration --- src/migrations/baseSchemaTables/unit_extend.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index ad6c22a..7ff9eee 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -97,6 +97,7 @@ export const repair_table = new Table({ { name: "finishedAt", ...getTypeByORM("datetime", true) }, { name: "status", ...getTypeByORM("varchar") }, { name: "responsible", ...getTypeByORM("varchar") }, + { name: "title", ...getTypeByORM("varchar") }, { name: "description", ...getTypeByORM("text") }, { name: "images", ...getTypeByORM("text") }, { name: "reportDocument", ...getTypeByORM("varchar", true) }, From f812298cb2c6047714ccd764f1fd381193fcc5ba Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 22 Jul 2025 13:10:13 +0200 Subject: [PATCH 48/55] edit repair --- .../unit/damageReportCommandHandler.ts | 38 ++++++++++++++- src/command/unit/repairCommand.ts | 8 +++- src/command/unit/repairCommandHandler.ts | 30 ++++++++++-- src/controller/admin/unit/repairController.ts | 48 +++++++++++++++++-- src/routes/admin/unit/repair.ts | 22 ++++++++- src/service/unit/damageReportService.ts | 33 +++++++++++++ 6 files changed, 166 insertions(+), 13 deletions(-) diff --git a/src/command/unit/damageReportCommandHandler.ts b/src/command/unit/damageReportCommandHandler.ts index 1bf33ca..7d5e599 100644 --- a/src/command/unit/damageReportCommandHandler.ts +++ b/src/command/unit/damageReportCommandHandler.ts @@ -1,4 +1,4 @@ -import { EntityManager, UpdateResult } from "typeorm"; +import { EntityManager, In, UpdateResult } from "typeorm"; import { dataSource } from "../../data-source"; import { damageReport } from "../../entity/unit/damageReport"; import DatabaseActionException from "../../exceptions/databaseActionException"; @@ -8,6 +8,7 @@ import { DeleteDamageReportCommand, UpdateDamageReportRelatedRepairCommand, } from "./damageReportCommand"; +import DamageReportService from "../../service/unit/damageReportService"; export default abstract class DamageReportCommandHandler { /** @@ -83,6 +84,41 @@ export default abstract class DamageReportCommandHandler { }); } + /** + * @description update damageReport related maintenance + * @returns {Promise} + */ + static async updateRelatedMaintenanceMulti(repairId: string, reports: Array): Promise { + let [related] = await DamageReportService.getAllForRepair(repairId, { noLimit: true }); + return await dataSource + .transaction(async (manager) => { + let added = reports.filter((id) => !related.some((r) => r.id === id)); + let removed = related.map((r) => r.id).filter((id) => !reports.includes(id)); + + await manager + .createQueryBuilder() + .update(damageReport) + .set({ + repairId: repairId, + }) + .where({ id: In(added) }) + .execute(); + + await manager + .createQueryBuilder() + .update(damageReport) + .set({ + repairId: null, + }) + .where({ id: In(removed) }) + .execute(); + }) + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "damageReport->maintenance", err); + }); + } + /** * @description update damageReport related maintenance in transaction * @param {UpdateDamageReportCommand} updateDamageReport diff --git a/src/command/unit/repairCommand.ts b/src/command/unit/repairCommand.ts index 998f62e..d455f16 100644 --- a/src/command/unit/repairCommand.ts +++ b/src/command/unit/repairCommand.ts @@ -9,11 +9,15 @@ export interface CreateRepairCommand { export interface UpdateRepairCommand { id: string; - status: string; title: string; description: string; responsible: string; - reports: string[]; +} + +export interface UpdateRepairStatusCommand { + id: string; + status: string; + done: boolean; } export interface DeleteRepairCommand { diff --git a/src/command/unit/repairCommandHandler.ts b/src/command/unit/repairCommandHandler.ts index 00338ce..d07dd95 100644 --- a/src/command/unit/repairCommandHandler.ts +++ b/src/command/unit/repairCommandHandler.ts @@ -2,7 +2,12 @@ import { dataSource } from "../../data-source"; import { repair } from "../../entity/unit/repair"; import DatabaseActionException from "../../exceptions/databaseActionException"; import DamageReportCommandHandler from "./damageReportCommandHandler"; -import { CreateRepairCommand, UpdateRepairCommand, DeleteRepairCommand } from "./repairCommand"; +import { + CreateRepairCommand, + UpdateRepairCommand, + DeleteRepairCommand, + UpdateRepairStatusCommand, +} from "./repairCommand"; export default abstract class RepairCommandHandler { /** @@ -57,10 +62,8 @@ export default abstract class RepairCommandHandler { .createQueryBuilder() .update(repair) .set({ - status: updateRepair.status, title: updateRepair.title, description: updateRepair.description, - reports: updateRepair.reports.map((r) => ({ id: r })), responsible: updateRepair.responsible, }) .where("id = :id", { id: updateRepair.id }) @@ -71,6 +74,27 @@ export default abstract class RepairCommandHandler { }); } + /** + * @description update repair + * @param {UpdateRepairStatusCommand} updateRepair + * @returns {Promise} + */ + static async updateStatus(updateRepair: UpdateRepairStatusCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(repair) + .set({ + status: updateRepair.status, + finishedAt: updateRepair.done ? new Date() : null, + }) + .where("id = :id", { id: updateRepair.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new DatabaseActionException("UPDATE", "repair", err); + }); + } + /** * @description delete repair * @param {DeleteRepairCommand} deleteRepair diff --git a/src/controller/admin/unit/repairController.ts b/src/controller/admin/unit/repairController.ts index 514af21..4eba861 100644 --- a/src/controller/admin/unit/repairController.ts +++ b/src/controller/admin/unit/repairController.ts @@ -1,10 +1,16 @@ import { Request, Response } from "express"; import RepairService from "../../../service/unit/repairService"; import RepairFactory from "../../../factory/admin/unit/repair"; -import { CreateRepairCommand, UpdateRepairCommand } from "../../../command/unit/repairCommand"; +import { + CreateRepairCommand, + UpdateRepairCommand, + UpdateRepairStatusCommand, +} from "../../../command/unit/repairCommand"; import RepairCommandHandler from "../../../command/unit/repairCommandHandler"; import BadRequestException from "../../../exceptions/badRequestException"; import { FileSystemHelper } from "../../../helpers/fileSystemHelper"; +import { UpdateDamageReportRelatedRepairCommand } from "../../../command/unit/damageReportCommand"; +import DamageReportCommandHandler from "../../../command/unit/damageReportCommandHandler"; /** * @description get all repairs by status @@ -110,21 +116,53 @@ export async function createRepair(req: Request, res: Response): Promise { */ export async function updateRepairById(req: Request, res: Response): Promise { const repairId = req.params.id; - const status = req.body.status; const title = req.body.title; const description = req.body.description; const responsible = req.body.responsible; - const reports = req.body.reports; let updateRepair: UpdateRepairCommand = { id: repairId, - status, title, description, responsible, - reports, }; await RepairCommandHandler.update(updateRepair); res.sendStatus(204); } + +/** + * @description update repair by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateRepairReportsById(req: Request, res: Response): Promise { + const repairId = req.params.id; + const reports = req.body.reports as Array; + + await DamageReportCommandHandler.updateRelatedMaintenanceMulti(repairId, reports); + + res.sendStatus(204); +} + +/** + * @description update repair by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateRepairStatusById(req: Request, res: Response): Promise { + const repairId = req.params.id; + const status = req.body.status; + const done = req.body.done; + + let updateRepair: UpdateRepairStatusCommand = { + id: repairId, + status, + done, + }; + await RepairCommandHandler.updateStatus(updateRepair); + + res.sendStatus(204); +} diff --git a/src/routes/admin/unit/repair.ts b/src/routes/admin/unit/repair.ts index 7fbaf9e..4f2499f 100644 --- a/src/routes/admin/unit/repair.ts +++ b/src/routes/admin/unit/repair.ts @@ -6,6 +6,8 @@ import { getAllRepairsForRelated, getRepairById, updateRepairById, + updateRepairReportsById, + updateRepairStatusById, } from "../../../controller/admin/unit/repairController"; var router = express.Router({ mergeParams: true }); @@ -35,7 +37,7 @@ router.get("/:id", async (req: Request, res: Response) => { router.post( "/", - PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), + PermissionHelper.passCheckMiddleware("update", "unit", "repair"), async (req: Request, res: Response) => { await createRepair(req, res); } @@ -43,10 +45,26 @@ router.post( router.patch( "/:id", - PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), + PermissionHelper.passCheckMiddleware("update", "unit", "repair"), async (req: Request, res: Response) => { await updateRepairById(req, res); } ); +router.patch( + "/:id/reports", + PermissionHelper.passCheckMiddleware("update", "unit", "repair"), + async (req: Request, res: Response) => { + await updateRepairReportsById(req, res); + } +); + +router.patch( + "/:id/status", + PermissionHelper.passCheckMiddleware("update", "unit", "repair"), + async (req: Request, res: Response) => { + await updateRepairStatusById(req, res); + } +); + export default router; diff --git a/src/service/unit/damageReportService.ts b/src/service/unit/damageReportService.ts index 3ad8c99..25c0c6b 100644 --- a/src/service/unit/damageReportService.ts +++ b/src/service/unit/damageReportService.ts @@ -114,6 +114,39 @@ export default abstract class DamageReportService { }); } + /** + * @description get all damageReport for repair + * @returns {Promise<[Array, number]>} + */ + static async getAllForRepair( + repairId: string, + { + offset = 0, + count = 25, + noLimit = false, + }: { + offset?: number; + count?: number; + noLimit?: boolean; + } + ): Promise<[Array, number]> { + let query = this.query().where({ repairId }); + + 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} From e9c3a30a413764d733645308d6205c27b2e393fd Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 23 Jul 2025 09:38:01 +0200 Subject: [PATCH 49/55] update permission handling --- src/routes/admin/unit/damageReport.ts | 2 +- src/routes/admin/unit/maintenance.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/admin/unit/damageReport.ts b/src/routes/admin/unit/damageReport.ts index 95d42b2..8b447d9 100644 --- a/src/routes/admin/unit/damageReport.ts +++ b/src/routes/admin/unit/damageReport.ts @@ -51,7 +51,7 @@ router.get("/:id/:filename", async (req: Request, res: Response) => { router.patch( "/:id", - PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), + PermissionHelper.passCheckMiddleware("update", "unit", "damage_report"), async (req: Request, res: Response) => { await updateDamageReportById(req, res); } diff --git a/src/routes/admin/unit/maintenance.ts b/src/routes/admin/unit/maintenance.ts index 76c80b1..3719eee 100644 --- a/src/routes/admin/unit/maintenance.ts +++ b/src/routes/admin/unit/maintenance.ts @@ -34,7 +34,7 @@ router.get("/:id", async (req: Request, res: Response) => { router.patch( "/:id", - PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), + PermissionHelper.passCheckMiddleware("update", "unit", "maintenance"), async (req: Request, res: Response) => { await updateMaintenanceById(req, res); } From b6b0f91d6020af98ad3b7c899aed7ab13ff26c72 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 23 Jul 2025 09:38:13 +0200 Subject: [PATCH 50/55] update factories to prevent undefined errors --- src/factory/admin/unit/inspection/inspection.ts | 14 ++++++++++---- .../admin/unit/inspection/inspectionPlan.ts | 14 ++++++++++---- src/factory/admin/unit/maintenance.ts | 14 ++++++++++---- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/factory/admin/unit/inspection/inspection.ts b/src/factory/admin/unit/inspection/inspection.ts index ac27226..f77f112 100644 --- a/src/factory/admin/unit/inspection/inspection.ts +++ b/src/factory/admin/unit/inspection/inspection.ts @@ -24,19 +24,25 @@ export default abstract class InspectionFactory { related = { relatedId: record.equipmentId, assigned: "equipment", - related: EquipmentFactory.mapToSingle(record.equipment), + related: record.equipment ? EquipmentFactory.mapToSingle(record.equipment) : undefined, }; } else if (record?.vehicleId) { related = { relatedId: record.vehicleId, assigned: "vehicle", - related: VehicleFactory.mapToSingle(record.vehicle), + related: record.vehicle ? VehicleFactory.mapToSingle(record.vehicle) : undefined, }; - } else { + } else if (record?.wearableId) { related = { relatedId: record.wearableId, assigned: "wearable", - related: WearableFactory.mapToSingle(record.wearable), + related: record.wearable ? WearableFactory.mapToSingle(record.wearable) : undefined, + }; + } else { + related = { + relatedId: undefined, + assigned: undefined, + related: undefined, }; } return related; diff --git a/src/factory/admin/unit/inspection/inspectionPlan.ts b/src/factory/admin/unit/inspection/inspectionPlan.ts index 6e48ed5..0d6e231 100644 --- a/src/factory/admin/unit/inspection/inspectionPlan.ts +++ b/src/factory/admin/unit/inspection/inspectionPlan.ts @@ -22,19 +22,25 @@ export default abstract class InspectionPlanFactory { related = { relatedId: record.equipmentTypeId, assigned: "equipment", - related: EquipmentTypeFactory.mapToSingle(record.equipmentType), + related: record.equipmentType ? EquipmentTypeFactory.mapToSingle(record.equipmentType) : undefined, }; } else if (record?.vehicleTypeId) { related = { relatedId: record.vehicleTypeId, assigned: "vehicle", - related: VehicleTypeFactory.mapToSingle(record.vehicleType), + related: record.vehicleType ? VehicleTypeFactory.mapToSingle(record.vehicleType) : undefined, }; - } else { + } else if (record?.wearableTypeId) { related = { relatedId: record.wearableTypeId, assigned: "wearable", - related: WearableTypeFactory.mapToSingle(record.wearableType), + related: record.wearableType ? WearableTypeFactory.mapToSingle(record.wearableType) : undefined, + }; + } else { + related = { + relatedId: undefined, + assigned: undefined, + related: undefined, }; } diff --git a/src/factory/admin/unit/maintenance.ts b/src/factory/admin/unit/maintenance.ts index fb5a464..a831097 100644 --- a/src/factory/admin/unit/maintenance.ts +++ b/src/factory/admin/unit/maintenance.ts @@ -17,19 +17,25 @@ export default abstract class MaintenanceFactory { assigned = { relatedId: record.equipmentId, assigned: "equipment", - related: EquipmentFactory.mapToSingle(record.equipment), + related: record.equipment ? EquipmentFactory.mapToSingle(record.equipment) : undefined, }; } else if (record?.vehicleId) { assigned = { relatedId: record.vehicleId, assigned: "vehicle", - related: VehicleFactory.mapToSingle(record.vehicle), + related: record.vehicle ? VehicleFactory.mapToSingle(record.vehicle) : undefined, }; - } else { + } else if (record?.wearableId) { assigned = { relatedId: record.wearableId, assigned: "wearable", - related: WearableFactory.mapToSingle(record.wearable), + related: record.wearable ? WearableFactory.mapToSingle(record.wearable) : undefined, + }; + } else { + assigned = { + relatedId: undefined, + assigned: undefined, + related: undefined, }; } From c2ee4fc6ef318b7a07918fc752be759c76f98937 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 23 Jul 2025 10:41:34 +0200 Subject: [PATCH 51/55] provide type related count in getById --- src/service/unit/equipment/equipmentTypeService.ts | 1 + src/service/unit/vehicle/vehicleTypeService.ts | 1 + src/service/unit/wearable/wearableTypeService.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/src/service/unit/equipment/equipmentTypeService.ts b/src/service/unit/equipment/equipmentTypeService.ts index 16a9527..6fa4bfd 100644 --- a/src/service/unit/equipment/equipmentTypeService.ts +++ b/src/service/unit/equipment/equipmentTypeService.ts @@ -53,6 +53,7 @@ export default abstract class EquipmentTypeService { return await dataSource .getRepository(equipmentType) .createQueryBuilder("equipmentType") + .loadRelationCountAndMap("equipmentType.equipmentCount", "equipmentType.equipment") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/vehicle/vehicleTypeService.ts b/src/service/unit/vehicle/vehicleTypeService.ts index 438d46b..c1aa7dd 100644 --- a/src/service/unit/vehicle/vehicleTypeService.ts +++ b/src/service/unit/vehicle/vehicleTypeService.ts @@ -53,6 +53,7 @@ export default abstract class VehicleTypeService { return await dataSource .getRepository(vehicleType) .createQueryBuilder("vehicleType") + .loadRelationCountAndMap("vehicleType.vehicleCount", "vehicleType.vehicle") .where({ id }) .getOneOrFail() .then((res) => { diff --git a/src/service/unit/wearable/wearableTypeService.ts b/src/service/unit/wearable/wearableTypeService.ts index a9f06f3..e699a2f 100644 --- a/src/service/unit/wearable/wearableTypeService.ts +++ b/src/service/unit/wearable/wearableTypeService.ts @@ -53,6 +53,7 @@ export default abstract class WearableTypeService { return await dataSource .getRepository(wearableType) .createQueryBuilder("wearableType") + .loadRelationCountAndMap("wearableType.wearableCount", "wearableType.wearable") .where({ id }) .getOneOrFail() .then((res) => { From 9b38c6a5e9ea4e9242a1f401ada3920f4e004075 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 25 Jul 2025 11:04:53 +0200 Subject: [PATCH 52/55] schema update --- src/command/unit/damageReportCommand.ts | 1 + .../unit/damageReportCommandHandler.ts | 6 ++- .../unit/inspection/inspectionCommand.ts | 1 + .../inspection/inspectionCommandHandler.ts | 2 + src/command/unit/repairCommand.ts | 1 + src/command/unit/repairCommandHandler.ts | 2 + .../admin/unit/damageReportController.ts | 5 +++ .../admin/unit/inspectionController.ts | 11 +++-- .../admin/unit/maintenanceController.ts | 2 +- src/controller/admin/unit/repairController.ts | 7 ++- src/entity/unit/damageReport.ts | 37 +++++++++++----- src/entity/unit/inspection/inspection.ts | 16 ++++++- src/entity/unit/maintenance.ts | 14 ++++++ src/entity/unit/repair.ts | 22 ++++++++-- src/factory/admin/unit/damageReport.ts | 3 +- .../admin/unit/inspection/inspection.ts | 5 ++- src/factory/admin/unit/repair.ts | 3 ++ src/migrations/baseSchemaTables/inspection.ts | 11 ++++- .../baseSchemaTables/unit_extend.ts | 43 +++++++++++++++---- src/routes/admin/unit/damageReport.ts | 2 + src/routes/admin/unit/inspection.ts | 2 + src/routes/admin/unit/repair.ts | 2 + src/service/unit/damageReportService.ts | 8 ++-- .../unit/inspection/inspectionService.ts | 3 +- src/service/unit/maintenanceService.ts | 5 ++- src/service/unit/repairService.ts | 5 ++- .../admin/unit/damageReport.models.ts | 4 +- .../unit/inspection/inspection.models.ts | 3 +- src/viewmodel/admin/unit/repair.models.ts | 1 + 29 files changed, 184 insertions(+), 43 deletions(-) diff --git a/src/command/unit/damageReportCommand.ts b/src/command/unit/damageReportCommand.ts index 60b8d17..aa47edf 100644 --- a/src/command/unit/damageReportCommand.ts +++ b/src/command/unit/damageReportCommand.ts @@ -13,6 +13,7 @@ export interface UpdateDamageReportCommand { id: string; status: string; noteByWorker: string; + user: { id: string; firstname: string; lastname: string }; done: boolean; } diff --git a/src/command/unit/damageReportCommandHandler.ts b/src/command/unit/damageReportCommandHandler.ts index 7d5e599..86771d5 100644 --- a/src/command/unit/damageReportCommandHandler.ts +++ b/src/command/unit/damageReportCommandHandler.ts @@ -54,7 +54,11 @@ export default abstract class DamageReportCommandHandler { .set({ status: updateDamageReport.status, noteByWorker: updateDamageReport.noteByWorker, - done: updateDamageReport.done, + closedAt: updateDamageReport.done ? new Date() : null, + closedById: updateDamageReport.done ? updateDamageReport.user.id : null, + closedByString: updateDamageReport.done + ? `${updateDamageReport.user.firstname} ${updateDamageReport.user.lastname}` + : null, }) .where("id = :id", { id: updateDamageReport.id }) .execute() diff --git a/src/command/unit/inspection/inspectionCommand.ts b/src/command/unit/inspection/inspectionCommand.ts index 254d690..16deedd 100644 --- a/src/command/unit/inspection/inspectionCommand.ts +++ b/src/command/unit/inspection/inspectionCommand.ts @@ -14,6 +14,7 @@ export interface UpdateInspectionCommand { export interface FinishInspectionCommand { id: string; + user: { id: string; firstname: string; lastname: string }; } export interface DeleteInspectionCommand { diff --git a/src/command/unit/inspection/inspectionCommandHandler.ts b/src/command/unit/inspection/inspectionCommandHandler.ts index f6156b3..8a17af2 100644 --- a/src/command/unit/inspection/inspectionCommandHandler.ts +++ b/src/command/unit/inspection/inspectionCommandHandler.ts @@ -96,6 +96,8 @@ export default abstract class InspectionCommandHandler { .update(inspection) .set({ finishedAt: new Date(), + finishedById: finishInspection.user.id, + finishedByString: `${finishInspection.user.firstname} ${finishInspection.user.lastname}`, }) .where("id = :id", { id: finishInspection.id }) .execute() diff --git a/src/command/unit/repairCommand.ts b/src/command/unit/repairCommand.ts index d455f16..4b071d5 100644 --- a/src/command/unit/repairCommand.ts +++ b/src/command/unit/repairCommand.ts @@ -17,6 +17,7 @@ export interface UpdateRepairCommand { export interface UpdateRepairStatusCommand { id: string; status: string; + user: { id: string; firstname: string; lastname: string }; done: boolean; } diff --git a/src/command/unit/repairCommandHandler.ts b/src/command/unit/repairCommandHandler.ts index d07dd95..711bb05 100644 --- a/src/command/unit/repairCommandHandler.ts +++ b/src/command/unit/repairCommandHandler.ts @@ -86,6 +86,8 @@ export default abstract class RepairCommandHandler { .set({ status: updateRepair.status, finishedAt: updateRepair.done ? new Date() : null, + finishedById: updateRepair.done ? updateRepair.user.id : null, + finishedByString: updateRepair.done ? `${updateRepair.user.firstname} ${updateRepair.user.lastname}` : null, }) .where("id = :id", { id: updateRepair.id }) .execute() diff --git a/src/controller/admin/unit/damageReportController.ts b/src/controller/admin/unit/damageReportController.ts index 4db079c..87f9a65 100644 --- a/src/controller/admin/unit/damageReportController.ts +++ b/src/controller/admin/unit/damageReportController.ts @@ -157,6 +157,11 @@ export async function updateDamageReportById(req: Request, res: Response): Promi status, noteByWorker, done, + user: { + id: req.userId, + firstname: req.firstname, + lastname: req.lastname, + }, }; await DamageReportCommandHandler.update(updateDamageReport); diff --git a/src/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts index f91b3d0..7307820 100644 --- a/src/controller/admin/unit/inspectionController.ts +++ b/src/controller/admin/unit/inspectionController.ts @@ -13,12 +13,10 @@ import ForbiddenRequestException from "../../../exceptions/forbiddenRequestExcep 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"; /** @@ -270,7 +268,7 @@ export async function finishInspection(req: Request, res: Response): Promise { return (value ?? "").split(",").filter((i) => !!i); @@ -46,9 +59,6 @@ export class damageReport { }) images: string[]; - @Column({ type: "varchar", length: 255 }) - reportedBy: string; - @Column({ nullable: true, default: null }) equipmentId?: string; @@ -61,6 +71,13 @@ export class damageReport { @Column({ nullable: true, default: null }) repairId?: string; + @ManyToOne(() => user, { + nullable: true, + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }) + closedBy?: user; + @ManyToOne(() => equipment, (e) => e.reports, { nullable: true, onDelete: "CASCADE", diff --git a/src/entity/unit/inspection/inspection.ts b/src/entity/unit/inspection/inspection.ts index eff01ec..68b5420 100644 --- a/src/entity/unit/inspection/inspection.ts +++ b/src/entity/unit/inspection/inspection.ts @@ -6,6 +6,7 @@ import { vehicle } from "../vehicle/vehicle"; import { equipment } from "../equipment/equipment"; import { inspectionPointResult } from "./inspectionPointResult"; import { wearable } from "../wearable/wearable"; +import { user } from "../../management/user"; @Entity() export class inspection { @@ -18,9 +19,15 @@ export class inspection { @CreateDateColumn() createdAt: Date; - @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) + @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true, default: null }) finishedAt?: Date; + @Column({ nullable: true, default: null }) + finishedById?: string; + + @Column({ type: "varchar", length: 255, nullable: true, default: null }) + finishedByString?: string; + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true, default: null }) nextInspection?: Date; @@ -42,6 +49,13 @@ export class inspection { @Column({ nullable: true, default: null }) wearableId?: string; + @ManyToOne(() => user, { + nullable: true, + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }) + finishedBy?: user; + @ManyToOne(() => inspectionPlan, { nullable: false, onDelete: "RESTRICT", diff --git a/src/entity/unit/maintenance.ts b/src/entity/unit/maintenance.ts index 15ce3c2..5a183cc 100644 --- a/src/entity/unit/maintenance.ts +++ b/src/entity/unit/maintenance.ts @@ -4,6 +4,7 @@ import { wearable } from "./wearable/wearable"; import { vehicle } from "./vehicle/vehicle"; import { damageReport } from "./damageReport"; import { getTypeByORM } from "../../migrations/ormHelper"; +import { user } from "../management/user"; @Entity() export class maintenance { @@ -16,6 +17,12 @@ export class maintenance { @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true, default: null }) finishedAt?: Date; + @Column({ nullable: true, default: null }) + finishedById?: string; + + @Column({ type: "varchar", length: 255, nullable: true, default: null }) + finishedByString?: string; + @Column({ type: "varchar", length: 255 }) status: string; @@ -31,6 +38,13 @@ export class maintenance { @Column({ nullable: true, default: null }) wearableId?: string; + @ManyToOne(() => user, { + nullable: true, + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }) + finishedBy?: user; + @ManyToOne(() => equipment, (e) => e.maintenances, { nullable: true, onDelete: "CASCADE", diff --git a/src/entity/unit/repair.ts b/src/entity/unit/repair.ts index 707f652..c79f448 100644 --- a/src/entity/unit/repair.ts +++ b/src/entity/unit/repair.ts @@ -4,6 +4,7 @@ import { wearable } from "./wearable/wearable"; import { vehicle } from "./vehicle/vehicle"; import { damageReport } from "./damageReport"; import { getTypeByORM } from "../../migrations/ormHelper"; +import { user } from "../management/user"; @Entity() export class repair { @@ -16,11 +17,14 @@ export class repair { @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true, default: null }) finishedAt?: Date; - @Column({ type: "varchar", length: 255 }) - status: string; + @Column({ nullable: true, default: null }) + finishedById?: string; + + @Column({ type: "varchar", length: 255, nullable: true, default: null }) + finishedByString?: string; @Column({ type: "varchar", length: 255 }) - responsible: string; + status: string; @Column({ type: "varchar", length: 255 }) title: string; @@ -28,8 +32,13 @@ export class repair { @Column({ type: "text" }) description: string; + @Column({ type: "varchar", length: 255, nullable: true, default: null }) + responsible: string; + @Column({ type: "text", + nullable: true, + default: null, transformer: { from(value: string): Array { return (value ?? "").split(",").filter((i) => !!i); @@ -53,6 +62,13 @@ export class repair { @Column({ nullable: true, default: null }) wearableId?: string; + @ManyToOne(() => user, { + nullable: true, + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }) + finishedBy?: user; + @ManyToOne(() => equipment, (e) => e.maintenances, { nullable: true, onDelete: "CASCADE", diff --git a/src/factory/admin/unit/damageReport.ts b/src/factory/admin/unit/damageReport.ts index 29a8a56..f9f2e31 100644 --- a/src/factory/admin/unit/damageReport.ts +++ b/src/factory/admin/unit/damageReport.ts @@ -44,7 +44,8 @@ export default abstract class DamageReportFactory { id: record.id, reportedAt: record.reportedAt, status: record.status, - done: record.done, + closedAt: record.closedAt, + closedBy: record?.closedBy ? record.closedBy.firstname + " " + record.closedBy.lastname : record.closedByString, title: record.title, description: record.description, location: record.location, diff --git a/src/factory/admin/unit/inspection/inspection.ts b/src/factory/admin/unit/inspection/inspection.ts index f77f112..7d6280b 100644 --- a/src/factory/admin/unit/inspection/inspection.ts +++ b/src/factory/admin/unit/inspection/inspection.ts @@ -64,7 +64,10 @@ export default abstract class InspectionFactory { inspectionVersionedPlan: InspectionVersionedPlanFactory.mapToSingle(record.inspectionVersionedPlan), context: record.context, created: record.createdAt, - finished: record?.finishedAt, + finishedAt: record?.finishedAt, + finishedBy: record?.finishedBy + ? record.finishedBy.firstname + " " + record.finishedBy.lastname + : record.finishedByString, isOpen: record?.finishedAt == undefined, nextInspection: record?.nextInspection, checks: InspectionPointResultFactory.mapToBase(record.pointResults), diff --git a/src/factory/admin/unit/repair.ts b/src/factory/admin/unit/repair.ts index e656fc0..0e2d8d4 100644 --- a/src/factory/admin/unit/repair.ts +++ b/src/factory/admin/unit/repair.ts @@ -43,6 +43,9 @@ export default abstract class RepairFactory { id: record.id, createdAt: record.createdAt, finishedAt: record.finishedAt, + finishedBy: record?.finishedBy + ? record.finishedBy.firstname + " " + record.finishedBy.lastname + : record.finishedByString, status: record.status, responsible: record.responsible, title: record.title, diff --git a/src/migrations/baseSchemaTables/inspection.ts b/src/migrations/baseSchemaTables/inspection.ts index fad995f..76ca627 100644 --- a/src/migrations/baseSchemaTables/inspection.ts +++ b/src/migrations/baseSchemaTables/inspection.ts @@ -93,7 +93,9 @@ export const inspection_table = new Table({ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, { name: "context", ...getTypeByORM("text") }, { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, - { name: "finishedAt", ...getTypeByORM("date", true) }, + { name: "finishedAt", ...getTypeByORM("datetime", true) }, + { name: "finishedById", ...getTypeByORM("uuid", true) }, + { name: "finishedByString", ...getTypeByORM("varchar", true) }, { name: "nextInspection", ...getTypeByORM("date", true) }, { name: "hasNewer", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, { name: "inspectionPlanId", ...getTypeByORM("uuid") }, @@ -103,6 +105,13 @@ export const inspection_table = new Table({ { name: "wearableId", ...getTypeByORM("uuid", true) }, ], foreignKeys: [ + new TableForeignKey({ + columnNames: ["finishedById"], + referencedColumnNames: ["id"], + referencedTableName: "user", + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }), new TableForeignKey({ columnNames: ["inspectionPlanId"], referencedColumnNames: ["id"], diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index 7ff9eee..85b7602 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -6,21 +6,30 @@ export const damage_report_table = new Table({ columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, { name: "reportedAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, + { name: "closedAt", ...getTypeByORM("datetime", true) }, + { name: "closedById", ...getTypeByORM("uuid", true) }, + { name: "closedByString", ...getTypeByORM("varchar", true) }, { name: "status", ...getTypeByORM("varchar") }, - { name: "done", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, { name: "title", ...getTypeByORM("varchar") }, { name: "description", ...getTypeByORM("text") }, - { name: "location", ...getTypeByORM("text") }, - { name: "noteByReporter", ...getTypeByORM("text") }, - { name: "noteByWorker", ...getTypeByORM("text") }, - { name: "reportedBy", ...getTypeByORM("varchar") }, - { name: "images", ...getTypeByORM("text") }, + { name: "location", ...getTypeByORM("text", true) }, + { name: "noteByReporter", ...getTypeByORM("text", true) }, + { name: "noteByWorker", ...getTypeByORM("text", true) }, + { name: "reportedBy", ...getTypeByORM("varchar", true) }, + { name: "images", ...getTypeByORM("text", true) }, { name: "equipmentId", ...getTypeByORM("uuid", true) }, { name: "vehicleId", ...getTypeByORM("uuid", true) }, { name: "wearableId", ...getTypeByORM("uuid", true) }, { name: "repairId", ...getTypeByORM("uuid", true) }, ], foreignKeys: [ + new TableForeignKey({ + columnNames: ["closedById"], + referencedColumnNames: ["id"], + referencedTableName: "user", + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }), new TableForeignKey({ columnNames: ["equipmentId"], referencedColumnNames: ["id"], @@ -58,6 +67,8 @@ export const maintenance_table = new Table({ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, { name: "finishedAt", ...getTypeByORM("datetime", true) }, + { name: "finishedById", ...getTypeByORM("uuid", true) }, + { name: "finishedByString", ...getTypeByORM("varchar", true) }, { name: "status", ...getTypeByORM("varchar") }, { name: "description", ...getTypeByORM("text") }, { name: "equipmentId", ...getTypeByORM("uuid", true) }, @@ -65,6 +76,13 @@ export const maintenance_table = new Table({ { name: "wearableId", ...getTypeByORM("uuid", true) }, ], foreignKeys: [ + new TableForeignKey({ + columnNames: ["finishedById"], + referencedColumnNames: ["id"], + referencedTableName: "user", + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }), new TableForeignKey({ columnNames: ["equipmentId"], referencedColumnNames: ["id"], @@ -95,17 +113,26 @@ export const repair_table = new Table({ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, { name: "finishedAt", ...getTypeByORM("datetime", true) }, + { name: "finishedById", ...getTypeByORM("uuid", true) }, + { name: "finishedByString", ...getTypeByORM("varchar", true) }, { name: "status", ...getTypeByORM("varchar") }, - { name: "responsible", ...getTypeByORM("varchar") }, { name: "title", ...getTypeByORM("varchar") }, { name: "description", ...getTypeByORM("text") }, - { name: "images", ...getTypeByORM("text") }, + { name: "responsible", ...getTypeByORM("varchar", true) }, + { name: "images", ...getTypeByORM("text", true) }, { name: "reportDocument", ...getTypeByORM("varchar", true) }, { name: "equipmentId", ...getTypeByORM("uuid", true) }, { name: "vehicleId", ...getTypeByORM("uuid", true) }, { name: "wearableId", ...getTypeByORM("uuid", true) }, ], foreignKeys: [ + new TableForeignKey({ + columnNames: ["finishedById"], + referencedColumnNames: ["id"], + referencedTableName: "user", + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }), new TableForeignKey({ columnNames: ["equipmentId"], referencedColumnNames: ["id"], diff --git a/src/routes/admin/unit/damageReport.ts b/src/routes/admin/unit/damageReport.ts index 8b447d9..85bd319 100644 --- a/src/routes/admin/unit/damageReport.ts +++ b/src/routes/admin/unit/damageReport.ts @@ -15,6 +15,7 @@ import { provideDamageReportImageUpload, updateDamageReportById, } from "../../../controller/admin/unit/damageReportController"; +import preventWebapiAccess from "../../../middleware/preventWebApiAccess"; var router = express.Router({ mergeParams: true }); @@ -51,6 +52,7 @@ router.get("/:id/:filename", async (req: Request, res: Response) => { router.patch( "/:id", + preventWebapiAccess, PermissionHelper.passCheckMiddleware("update", "unit", "damage_report"), async (req: Request, res: Response) => { await updateDamageReportById(req, res); diff --git a/src/routes/admin/unit/inspection.ts b/src/routes/admin/unit/inspection.ts index 6a43526..426e6c3 100644 --- a/src/routes/admin/unit/inspection.ts +++ b/src/routes/admin/unit/inspection.ts @@ -14,6 +14,7 @@ import { getInspectionPointUpload, } from "../../../controller/admin/unit/inspectionController"; import { inspectionFileUpload } from "../../../middleware/multer"; +import preventWebapiAccess from "../../../middleware/preventWebApiAccess"; var router = express.Router({ mergeParams: true }); @@ -79,6 +80,7 @@ router.patch( router.patch( "/:id/finish", + preventWebapiAccess, PermissionHelper.passCheckMiddleware("update", "unit", "inspection"), async (req: Request, res: Response) => { await finishInspection(req, res); diff --git a/src/routes/admin/unit/repair.ts b/src/routes/admin/unit/repair.ts index 4f2499f..89c0a82 100644 --- a/src/routes/admin/unit/repair.ts +++ b/src/routes/admin/unit/repair.ts @@ -9,6 +9,7 @@ import { updateRepairReportsById, updateRepairStatusById, } from "../../../controller/admin/unit/repairController"; +import preventWebapiAccess from "../../../middleware/preventWebApiAccess"; var router = express.Router({ mergeParams: true }); @@ -61,6 +62,7 @@ router.patch( router.patch( "/:id/status", + preventWebapiAccess, PermissionHelper.passCheckMiddleware("update", "unit", "repair"), async (req: Request, res: Response) => { await updateRepairStatusById(req, res); diff --git a/src/service/unit/damageReportService.ts b/src/service/unit/damageReportService.ts index 25c0c6b..2a56e27 100644 --- a/src/service/unit/damageReportService.ts +++ b/src/service/unit/damageReportService.ts @@ -1,4 +1,4 @@ -import { In } from "typeorm"; +import { In, IsNull, Not } from "typeorm"; import { dataSource } from "../../data-source"; import { damageReport } from "../../entity/unit/damageReport"; import DatabaseActionException from "../../exceptions/databaseActionException"; @@ -11,7 +11,9 @@ export default abstract class DamageReportService { .leftJoinAndSelect("damageReport.equipment", "equipment") .leftJoinAndSelect("damageReport.vehicle", "vehicle") .leftJoinAndSelect("damageReport.wearable", "wearable") - .leftJoinAndSelect("damageReport.repair", "repair"); + .leftJoinAndSelect("damageReport.repair", "repair") + .leftJoinAndSelect("damageReport.closedBy", "user"); + /** * @description get all damageReports By done * @returns {Promise<[Array, number]>} @@ -64,7 +66,7 @@ export default abstract class DamageReportService { noLimit?: boolean; } ): Promise<[Array, number]> { - let query = this.query().where({ done }); + let query = this.query().where({ closedAt: done ? Not(IsNull()) : IsNull() }); if (!noLimit) { query = query.offset(offset).limit(count); diff --git a/src/service/unit/inspection/inspectionService.ts b/src/service/unit/inspection/inspectionService.ts index 5e85f83..b4df294 100644 --- a/src/service/unit/inspection/inspectionService.ts +++ b/src/service/unit/inspection/inspectionService.ts @@ -18,7 +18,8 @@ export default abstract class InspectionService { .leftJoinAndSelect("pointResults.inspectionPoint", "inspectionPoint") .leftJoinAndSelect("inspection.equipment", "equipment") .leftJoinAndSelect("inspection.vehicle", "vehicle") - .leftJoinAndSelect("inspection.wearable", "wearable"); + .leftJoinAndSelect("inspection.wearable", "wearable") + .leftJoinAndSelect("inspection.finishedBy", "user"); private static minifiedQuery = () => dataSource diff --git a/src/service/unit/maintenanceService.ts b/src/service/unit/maintenanceService.ts index d2917e2..fa2adbe 100644 --- a/src/service/unit/maintenanceService.ts +++ b/src/service/unit/maintenanceService.ts @@ -10,13 +10,14 @@ export default abstract class MaintenanceService { .createQueryBuilder("maintenance") .leftJoinAndSelect("maintenance.equipment", "equipment") .leftJoinAndSelect("maintenance.vehicle", "vehicle") - .leftJoinAndSelect("maintenance.wearable", "wearable"); + .leftJoinAndSelect("maintenance.wearable", "wearable") + .leftJoinAndSelect("maintenance.finishedBy", "user"); /** * @description get all maintenances * @returns {Promise<[Array, number]>} */ - static async getAll( + static async getAllByDone( done = false, { offset = 0, diff --git a/src/service/unit/repairService.ts b/src/service/unit/repairService.ts index 731b8ad..5e443ea 100644 --- a/src/service/unit/repairService.ts +++ b/src/service/unit/repairService.ts @@ -11,13 +11,14 @@ export default abstract class RepairService { .leftJoinAndSelect("repair.equipment", "equipment") .leftJoinAndSelect("repair.vehicle", "vehicle") .leftJoinAndSelect("repair.wearable", "wearable") - .leftJoinAndSelect("repair.reports", "reports"); + .leftJoinAndSelect("repair.reports", "reports") + .leftJoinAndSelect("repair.finishedBy", "user"); /** * @description get all repairs * @returns {Promise<[Array, number]>} */ - static async getAll( + static async getAllByDone( done = false, { offset = 0, diff --git a/src/viewmodel/admin/unit/damageReport.models.ts b/src/viewmodel/admin/unit/damageReport.models.ts index 02f5ce1..6e798cb 100644 --- a/src/viewmodel/admin/unit/damageReport.models.ts +++ b/src/viewmodel/admin/unit/damageReport.models.ts @@ -1,5 +1,4 @@ import { EquipmentViewModel } from "./equipment/equipment.models"; -import { MaintenanceViewModel } from "./maintenance.models"; import { RepairViewModel } from "./repair.models"; import { VehicleViewModel } from "./vehicle/vehicle.models"; import { WearableViewModel } from "./wearable/wearable.models"; @@ -25,8 +24,9 @@ export type DamageReportViewModel = { id: string; title: string; reportedAt: Date; + closedAt?: Date; + closedBy?: string; status: string; - done: boolean; description: string; location: string; noteByReporter: string; diff --git a/src/viewmodel/admin/unit/inspection/inspection.models.ts b/src/viewmodel/admin/unit/inspection/inspection.models.ts index 87af590..47dc888 100644 --- a/src/viewmodel/admin/unit/inspection/inspection.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspection.models.ts @@ -32,7 +32,8 @@ export type InspectionViewModel = { inspectionVersionedPlan: InspectionVersionedPlanViewModel; context: string; created: Date; - finished?: Date; + finishedAt?: Date; + finishedBy?: string; isOpen: boolean; nextInspection?: Date; checks: Array; diff --git a/src/viewmodel/admin/unit/repair.models.ts b/src/viewmodel/admin/unit/repair.models.ts index c0b21b4..74c983d 100644 --- a/src/viewmodel/admin/unit/repair.models.ts +++ b/src/viewmodel/admin/unit/repair.models.ts @@ -24,6 +24,7 @@ export type RepairViewModel = { id: string; createdAt: Date; finishedAt?: Date; + finishedBy?: string; status: string; responsible: string; title: string; From 063b949ae1a3e5498e2e34f7eb9acd619b8965d9 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 25 Jul 2025 12:21:40 +0200 Subject: [PATCH 53/55] extend inspection_plan by wearable and enable optional interval --- .../unit/inspection/inspectionPlanCommand.ts | 2 +- .../inspectionPlanCommandHandler.ts | 1 + .../admin/unit/inspectionController.ts | 7 +++++++ .../admin/unit/inspectionPlanController.ts | 20 ++++++++++--------- src/entity/unit/inspection/inspectionPlan.ts | 8 ++++---- src/helpers/typeTester.ts | 6 +++++- src/migrations/baseSchemaTables/inspection.ts | 4 ++-- 7 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/command/unit/inspection/inspectionPlanCommand.ts b/src/command/unit/inspection/inspectionPlanCommand.ts index 0daac13..58593ae 100644 --- a/src/command/unit/inspection/inspectionPlanCommand.ts +++ b/src/command/unit/inspection/inspectionPlanCommand.ts @@ -5,7 +5,7 @@ export interface CreateInspectionPlanCommand { inspectionInterval: PlanTimeDefinition; remindTime: PlanTimeDefinition; relatedId: string; - assigned: "vehicle" | "equipment"; + assigned: "vehicle" | "equipment" | "wearable"; } export interface UpdateInspectionPlanCommand { diff --git a/src/command/unit/inspection/inspectionPlanCommandHandler.ts b/src/command/unit/inspection/inspectionPlanCommandHandler.ts index 179f8a7..e140f64 100644 --- a/src/command/unit/inspection/inspectionPlanCommandHandler.ts +++ b/src/command/unit/inspection/inspectionPlanCommandHandler.ts @@ -24,6 +24,7 @@ export default abstract class InspectionPlanCommandHandler { remindTime: createInspectionPlan.remindTime, equipmentTypeId: createInspectionPlan.assigned == "equipment" ? createInspectionPlan.relatedId : null, vehicleTypeId: createInspectionPlan.assigned == "vehicle" ? createInspectionPlan.relatedId : null, + wearableTypeId: createInspectionPlan.assigned == "wearable" ? createInspectionPlan.relatedId : null, }) .execute() .then((result) => { diff --git a/src/controller/admin/unit/inspectionController.ts b/src/controller/admin/unit/inspectionController.ts index 7307820..211ea10 100644 --- a/src/controller/admin/unit/inspectionController.ts +++ b/src/controller/admin/unit/inspectionController.ts @@ -18,6 +18,7 @@ import { PdfExport } from "../../../helpers/pdfExport"; import { PDFDocument } from "pdf-lib"; import sharp from "sharp"; import InspectionPointResultService from "../../../service/unit/inspection/inspectionPointResultService"; +import InspectionPlanService from "../../../service/unit/inspection/inspectionPlanService"; /** * @description get all inspections sorted by id not having newer inspection @@ -168,9 +169,15 @@ export async function createInspection(req: Request, res: Response): Promise { const title = req.body.title; - const inspectionInterval = req.body.inspectionInterval; - const remindTime = req.body.remindTime; + const inspectionInterval = req.body.inspectionInterval || null; + const remindTime = req.body.remindTime || null; const relatedId = req.body.relatedId; const assigned = req.body.assigned; - TypeTester.testPlanTimeDefinition(inspectionInterval, "inspectionInterval", true); - TypeTester.testPlanTimeDefinition(remindTime, "remindTime", true); + TypeTester.testPlanTimeDefinition(inspectionInterval, { key: "inspectionInterval", throwErr: true, allowNull: true }); + TypeTester.testPlanTimeDefinition(remindTime, { key: "inspectionInterval", throwErr: true, allowNull: true }); if (assigned != "equipment" && assigned != "vehicle" && assigned != "wearable") - throw new BadRequestException("set assigned to equipment or vehicle or wearable"); + throw new BadRequestException("set assigned to equipmenttype or vehicletype or wearabletype"); + + if (relatedId == null) throw new BadRequestException("provide related equipmenttype or vehicletype or wearabletype"); let createInspectionPlan: CreateInspectionPlanCommand = { title, @@ -155,11 +157,11 @@ export async function createInspectionPlan(req: Request, res: Response): 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; + const inspectionInterval = req.body.inspectionInterval || null; + const remindTime = req.body.remindTime || null; - TypeTester.testPlanTimeDefinition(inspectionInterval, "inspectionInterval", true); - TypeTester.testPlanTimeDefinition(remindTime, "remindTime", true); + TypeTester.testPlanTimeDefinition(inspectionInterval, { key: "inspectionInterval", throwErr: true, allowNull: true }); + TypeTester.testPlanTimeDefinition(remindTime, { key: "inspectionInterval", throwErr: true, allowNull: true }); let updateInspectionPlan: UpdateInspectionPlanCommand = { id: inspectionPlanId, diff --git a/src/entity/unit/inspection/inspectionPlan.ts b/src/entity/unit/inspection/inspectionPlan.ts index 9fa6fad..3b954a4 100644 --- a/src/entity/unit/inspection/inspectionPlan.ts +++ b/src/entity/unit/inspection/inspectionPlan.ts @@ -13,11 +13,11 @@ export class inspectionPlan { @Column({ type: "varchar", length: 255 }) title: string; - @Column({ type: "varchar", length: 255 }) - inspectionInterval: PlanTimeDefinition; + @Column({ type: "varchar", length: 255, nullable: true, default: null }) + inspectionInterval?: PlanTimeDefinition; - @Column({ type: "varchar", length: 255 }) - remindTime: PlanTimeDefinition; + @Column({ type: "varchar", length: 255, nullable: true, default: null }) + remindTime?: PlanTimeDefinition; @CreateDateColumn() createdAt: Date; diff --git a/src/helpers/typeTester.ts b/src/helpers/typeTester.ts index ab37808..d81f8eb 100644 --- a/src/helpers/typeTester.ts +++ b/src/helpers/typeTester.ts @@ -1,7 +1,11 @@ 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 { + static testPlanTimeDefinition( + val: string | null, + { key = "", throwErr = false, allowNull = false }: { key?: string; throwErr?: boolean; allowNull?: boolean } + ): PlanTimeDefinition | null { + if (val == null && allowNull) return null; if (/^(\d+-(d|m|y)|\d+\/(\d+|\*))$/.test(val)) { return val as PlanTimeDefinition; } else if (throwErr) { diff --git a/src/migrations/baseSchemaTables/inspection.ts b/src/migrations/baseSchemaTables/inspection.ts index 76ca627..6a71127 100644 --- a/src/migrations/baseSchemaTables/inspection.ts +++ b/src/migrations/baseSchemaTables/inspection.ts @@ -6,8 +6,8 @@ export const inspection_plan_table = new Table({ columns: [ { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, { name: "title", ...getTypeByORM("varchar") }, - { name: "inspectionInterval", ...getTypeByORM("varchar") }, - { name: "remindTime", ...getTypeByORM("varchar") }, + { name: "inspectionInterval", ...getTypeByORM("varchar", true) }, + { name: "remindTime", ...getTypeByORM("varchar", true) }, { name: "createdAt", ...getTypeByORM("datetime"), default: getDefaultByORM("currentTimestamp") }, { name: "equipmentTypeId", ...getTypeByORM("uuid", true) }, { name: "vehicleTypeId", ...getTypeByORM("uuid", true) }, From f05d03ccee5e3eeda6a5c1f72b3ad335fb92696b Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 25 Jul 2025 12:36:31 +0200 Subject: [PATCH 54/55] extend maintenance table --- src/entity/unit/maintenance.ts | 24 +++++++++++++++++++ .../admin/unit/inspection/inspection.ts | 5 +++- .../baseSchemaTables/unit_extend.ts | 4 ++++ .../unit/inspection/inspection.models.ts | 2 ++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/entity/unit/maintenance.ts b/src/entity/unit/maintenance.ts index 5a183cc..ff1da10 100644 --- a/src/entity/unit/maintenance.ts +++ b/src/entity/unit/maintenance.ts @@ -26,9 +26,33 @@ export class maintenance { @Column({ type: "varchar", length: 255 }) status: string; + @Column({ type: "varchar", length: 255 }) + title: string; + @Column({ type: "text" }) description: string; + @Column({ type: "varchar", length: 255, nullable: true, default: null }) + responsible: string; + + @Column({ + type: "text", + nullable: true, + default: null, + transformer: { + from(value: string): Array { + return (value ?? "").split(",").filter((i) => !!i); + }, + to(value: Array = []): string { + return value.join(","); + }, + }, + }) + images: string[]; + + @Column({ type: "varchar", length: 255, nullable: true, default: null }) + reportDocument?: string; + @Column({ nullable: true, default: null }) equipmentId?: string; diff --git a/src/factory/admin/unit/inspection/inspection.ts b/src/factory/admin/unit/inspection/inspection.ts index 7d6280b..fc38634 100644 --- a/src/factory/admin/unit/inspection/inspection.ts +++ b/src/factory/admin/unit/inspection/inspection.ts @@ -98,7 +98,10 @@ export default abstract class InspectionFactory { inspectionPlan: InspectionPlanFactory.mapToSingle(record.inspectionPlan), context: record.context, created: record.createdAt, - finished: record?.finishedAt, + finishedAt: record?.finishedAt, + finishedBy: record?.finishedBy + ? record.finishedBy.firstname + " " + record.finishedBy.lastname + : record.finishedByString, isOpen: record?.finishedAt == undefined, nextInspection: record?.nextInspection, ...related, diff --git a/src/migrations/baseSchemaTables/unit_extend.ts b/src/migrations/baseSchemaTables/unit_extend.ts index 85b7602..65d0738 100644 --- a/src/migrations/baseSchemaTables/unit_extend.ts +++ b/src/migrations/baseSchemaTables/unit_extend.ts @@ -70,7 +70,11 @@ export const maintenance_table = new Table({ { name: "finishedById", ...getTypeByORM("uuid", true) }, { name: "finishedByString", ...getTypeByORM("varchar", true) }, { name: "status", ...getTypeByORM("varchar") }, + { name: "title", ...getTypeByORM("varchar") }, { name: "description", ...getTypeByORM("text") }, + { name: "responsible", ...getTypeByORM("varchar", true) }, + { name: "images", ...getTypeByORM("text", true) }, + { name: "reportDocument", ...getTypeByORM("varchar", true) }, { name: "equipmentId", ...getTypeByORM("uuid", true) }, { name: "vehicleId", ...getTypeByORM("uuid", true) }, { name: "wearableId", ...getTypeByORM("uuid", true) }, diff --git a/src/viewmodel/admin/unit/inspection/inspection.models.ts b/src/viewmodel/admin/unit/inspection/inspection.models.ts index 47dc888..4923701 100644 --- a/src/viewmodel/admin/unit/inspection/inspection.models.ts +++ b/src/viewmodel/admin/unit/inspection/inspection.models.ts @@ -47,6 +47,8 @@ export type MinifiedInspectionViewModel = { created: Date; finished?: Date; isOpen: boolean; + finishedAt?: Date; + finishedBy?: string; nextInspection?: Date; relatedId: string; } & InspectionRelated; From fcf37bb4c1bb233b4c6bae8f812235469956c2a6 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 25 Jul 2025 12:55:55 +0200 Subject: [PATCH 55/55] 1.8.0-beta1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 187ab36..cccda29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ff-admin-server", - "version": "1.7.5", + "version": "1.8.0-beta1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ff-admin-server", - "version": "1.7.5", + "version": "1.8.0-beta1", "license": "AGPL-3.0-only", "dependencies": { "cors": "^2.8.5", diff --git a/package.json b/package.json index ba16412..078c151 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ff-admin-server", - "version": "1.7.5", + "version": "1.8.0-beta1", "description": "Feuerwehr/Verein Mitgliederverwaltung Server", "main": "dist/index.js", "scripts": {