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", ];