From a29f380a9c793c437ebab06386fb6f97c2b8d479 Mon Sep 17 00:00:00 2001 From: Dan Gowans Date: Wed, 28 Sep 2022 11:04:39 -0400 Subject: [PATCH] basic work order print --- handlers/print-get/pdf.js | 5 +- handlers/print-get/pdf.ts | 7 +- helpers/functions.config.d.ts | 1 + helpers/functions.config.js | 1 + helpers/functions.config.ts | 2 + helpers/functions.print.js | 13 ++ helpers/functions.print.ts | 18 ++ helpers/lotOccupancyDB/getWorkOrder.js | 2 +- helpers/lotOccupancyDB/getWorkOrder.ts | 2 +- views/print/pdf/ssm.cemetery.burialPermit.ejs | 4 +- views/print/pdf/style.css | 105 ++++++++++++ views/print/pdf/workOrder.ejs | 156 ++++++++++++++++++ views/workOrder-edit.ejs | 53 +++++- views/workOrder-view.ejs | 45 ++++- 14 files changed, 400 insertions(+), 14 deletions(-) create mode 100644 views/print/pdf/style.css create mode 100644 views/print/pdf/workOrder.ejs diff --git a/handlers/print-get/pdf.js b/handlers/print-get/pdf.js index 72e63c79..4024c765 100644 --- a/handlers/print-get/pdf.js +++ b/handlers/print-get/pdf.js @@ -1,6 +1,7 @@ import path from "path"; import * as ejs from "ejs"; import * as configFunctions from "../../helpers/functions.config.js"; +import * as dateTimeFunctions from "@cityssm/expressjs-server-js/dateTimeFns.js"; import { getReportData, getPdfPrintConfig } from "../../helpers/functions.print.js"; import convertHTMLToPDF from "pdf-puppeteer"; import camelcase from "camelcase"; @@ -14,10 +15,12 @@ export const handler = async (request, response, next) => { const reportData = getReportData(printConfig, request.query); const reportPath = path.join("views", "print", "pdf", printName + ".ejs"); const pdfCallbackFunction = (pdf) => { - response.setHeader("Content-Disposition", "attachment;" + " filename=" + camelcase(printConfig.title) + ".pdf"); + response.setHeader("Content-Disposition", "inline;" + " filename=" + camelcase(printConfig.title) + ".pdf"); response.setHeader("Content-Type", "application/pdf"); response.send(pdf); }; + reportData.configFunctions = configFunctions; + reportData.dateTimeFunctions = dateTimeFunctions; await ejs.renderFile(reportPath, reportData, {}, async (ejsError, ejsData) => { if (ejsError) { return next(ejsError); diff --git a/handlers/print-get/pdf.ts b/handlers/print-get/pdf.ts index dd8b301c..a9b2d690 100644 --- a/handlers/print-get/pdf.ts +++ b/handlers/print-get/pdf.ts @@ -4,6 +4,8 @@ import path from "path"; import * as ejs from "ejs"; import * as configFunctions from "../../helpers/functions.config.js"; +import * as dateTimeFunctions from "@cityssm/expressjs-server-js/dateTimeFns.js"; + import { getReportData, getPdfPrintConfig } from "../../helpers/functions.print.js"; import convertHTMLToPDF from "pdf-puppeteer"; @@ -28,7 +30,7 @@ export const handler: RequestHandler = async (request, response, next) => { const pdfCallbackFunction = (pdf: Buffer) => { response.setHeader( "Content-Disposition", - "attachment;" + " filename=" + camelcase(printConfig.title) + ".pdf" + "inline;" + " filename=" + camelcase(printConfig.title) + ".pdf" ); response.setHeader("Content-Type", "application/pdf"); @@ -36,6 +38,9 @@ export const handler: RequestHandler = async (request, response, next) => { response.send(pdf); }; + reportData.configFunctions = configFunctions; + reportData.dateTimeFunctions = dateTimeFunctions; + await ejs.renderFile(reportPath, reportData, {}, async (ejsError, ejsData) => { if (ejsError) { return next(ejsError); diff --git a/helpers/functions.config.d.ts b/helpers/functions.config.d.ts index be7d2f0c..d2efc417 100644 --- a/helpers/functions.config.d.ts +++ b/helpers/functions.config.d.ts @@ -38,5 +38,6 @@ export declare function getProperty(propertyName: "settings.workOrders.workOrder export declare function getProperty(propertyName: "settings.workOrders.workOrderMilestoneDateRecentBeforeDays"): number; export declare function getProperty(propertyName: "settings.workOrders.workOrderMilestoneDateRecentAfterDays"): number; export declare function getProperty(propertyName: "settings.workOrders.calendarEmailAddress"): string; +export declare function getProperty(propertyName: "settings.workOrders.prints"): string[]; export declare function getProperty(propertyName: "settings.adminCleanup.recordDeleteAgeDays"): number; export declare const keepAliveMillis: number; diff --git a/helpers/functions.config.js b/helpers/functions.config.js index c3a8169d..1dd1d142 100644 --- a/helpers/functions.config.js +++ b/helpers/functions.config.js @@ -37,6 +37,7 @@ configFallbackValues.set("settings.workOrders.workOrderNumberLength", 6); configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentBeforeDays", 5); configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentAfterDays", 60); configFallbackValues.set("settings.workOrders.calendarEmailAddress", "no-reply@127.0.0.1"); +configFallbackValues.set("settings.workOrders.prints", ["pdf/workOrder"]); configFallbackValues.set("settings.adminCleanup.recordDeleteAgeDays", 60); export function getProperty(propertyName) { const propertyNameSplit = propertyName.split("."); diff --git a/helpers/functions.config.ts b/helpers/functions.config.ts index a39200b6..c00fa76c 100644 --- a/helpers/functions.config.ts +++ b/helpers/functions.config.ts @@ -56,6 +56,7 @@ configFallbackValues.set("settings.workOrders.workOrderNumberLength", 6); configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentBeforeDays", 5); configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentAfterDays", 60); configFallbackValues.set("settings.workOrders.calendarEmailAddress", "no-reply@127.0.0.1"); +configFallbackValues.set("settings.workOrders.prints", ["pdf/workOrder"]); configFallbackValues.set("settings.adminCleanup.recordDeleteAgeDays", 60); @@ -129,6 +130,7 @@ export function getProperty( ): number; export function getProperty(propertyName: "settings.workOrders.calendarEmailAddress"): string; +export function getProperty(propertyName: "settings.workOrders.prints"): string[]; export function getProperty(propertyName: "settings.adminCleanup.recordDeleteAgeDays"): number; diff --git a/helpers/functions.print.js b/helpers/functions.print.js index ef026e2c..0bfda04f 100644 --- a/helpers/functions.print.js +++ b/helpers/functions.print.js @@ -1,6 +1,7 @@ import * as configFunctions from "./functions.config.js"; import { getLot } from "./lotOccupancyDB/getLot.js"; import { getLotOccupancy } from "./lotOccupancyDB/getLotOccupancy.js"; +import { getWorkOrder } from "./lotOccupancyDB/getWorkOrder.js"; const screenPrintConfigs = { lotOccupancy: { title: configFunctions.getProperty("aliases.lot") + @@ -14,6 +15,10 @@ export const getScreenPrintConfig = (printName) => { return screenPrintConfigs[printName]; }; const pdfPrintConfigs = { + "workOrder": { + title: "Work Order Field Sheet", + params: ["workOrderId"] + }, "ssm.cemetery.burialPermit": { title: "Burial Permit", params: ["lotOccupancyId"] @@ -43,5 +48,13 @@ export const getReportData = (printConfig, requestQuery) => { reportData.lot = getLot(reportData.lotOccupancy.lotId); } } + if (printConfig.params.includes("workOrderId") && + typeof requestQuery.workOrderId === "string") { + reportData.workOrder = getWorkOrder(requestQuery.workOrderId, { + includeLotsAndLotOccupancies: true, + includeComments: true, + includeMilestones: true + }); + } return reportData; }; diff --git a/helpers/functions.print.ts b/helpers/functions.print.ts index 4f45612b..6d14fffc 100644 --- a/helpers/functions.print.ts +++ b/helpers/functions.print.ts @@ -2,6 +2,7 @@ import * as configFunctions from "./functions.config.js"; import { getLot } from "./lotOccupancyDB/getLot.js"; import { getLotOccupancy } from "./lotOccupancyDB/getLotOccupancy.js"; +import { getWorkOrder } from "./lotOccupancyDB/getWorkOrder.js"; import type * as recordTypes from "../types/recordTypes"; @@ -26,6 +27,12 @@ export const getScreenPrintConfig = (printName: string): PrintConfig => { }; const pdfPrintConfigs: { [printName: string]: PrintConfig } = { + "workOrder": { + title: "Work Order Field Sheet", + params: ["workOrderId"] + }, + + // Occupancy "ssm.cemetery.burialPermit": { title: "Burial Permit", params: ["lotOccupancyId"] @@ -70,5 +77,16 @@ export const getReportData = ( } } + if ( + printConfig.params.includes("workOrderId") && + typeof requestQuery.workOrderId === "string" + ) { + reportData.workOrder = getWorkOrder(requestQuery.workOrderId, { + includeLotsAndLotOccupancies: true, + includeComments: true, + includeMilestones: true + }); + } + return reportData; }; diff --git a/helpers/lotOccupancyDB/getWorkOrder.js b/helpers/lotOccupancyDB/getWorkOrder.js index 26f37829..601966f8 100644 --- a/helpers/lotOccupancyDB/getWorkOrder.js +++ b/helpers/lotOccupancyDB/getWorkOrder.js @@ -10,7 +10,7 @@ const baseSQL = "select w.workOrderId," + " w.workOrderNumber, w.workOrderDescription," + " w.workOrderOpenDate, userFn_dateIntegerToString(w.workOrderOpenDate) as workOrderOpenDateString," + " w.workOrderCloseDate, userFn_dateIntegerToString(w.workOrderCloseDate) as workOrderCloseDateString," + - " w.recordUpdate_timeMillis" + + " w.recordCreate_timeMillis, w.recordUpdate_timeMillis" + " from WorkOrders w" + " left join WorkOrderTypes t on w.workOrderTypeId = t.workOrderTypeId" + " where w.recordDelete_timeMillis is null"; diff --git a/helpers/lotOccupancyDB/getWorkOrder.ts b/helpers/lotOccupancyDB/getWorkOrder.ts index 285d6513..93ff7df4 100644 --- a/helpers/lotOccupancyDB/getWorkOrder.ts +++ b/helpers/lotOccupancyDB/getWorkOrder.ts @@ -26,7 +26,7 @@ const baseSQL = " w.workOrderNumber, w.workOrderDescription," + " w.workOrderOpenDate, userFn_dateIntegerToString(w.workOrderOpenDate) as workOrderOpenDateString," + " w.workOrderCloseDate, userFn_dateIntegerToString(w.workOrderCloseDate) as workOrderCloseDateString," + - " w.recordUpdate_timeMillis" + + " w.recordCreate_timeMillis, w.recordUpdate_timeMillis" + " from WorkOrders w" + " left join WorkOrderTypes t on w.workOrderTypeId = t.workOrderTypeId" + " where w.recordDelete_timeMillis is null"; diff --git a/views/print/pdf/ssm.cemetery.burialPermit.ejs b/views/print/pdf/ssm.cemetery.burialPermit.ejs index 3588624e..bbf98173 100644 --- a/views/print/pdf/ssm.cemetery.burialPermit.ejs +++ b/views/print/pdf/ssm.cemetery.burialPermit.ejs @@ -1,6 +1,8 @@ - +

Burial Permit

diff --git a/views/print/pdf/style.css b/views/print/pdf/style.css new file mode 100644 index 00000000..142971a9 --- /dev/null +++ b/views/print/pdf/style.css @@ -0,0 +1,105 @@ +* { + font-family: Arial, Helvetica, sans-serif; +} + +body { + margin: 20px; +} + +/* Elements */ + +.box { + border: 1px solid black; + padding: 10px; +} + +.checkbox { + height: 1.5em; + width: 1.5em; + display: inline-block; + border: 1px solid black; + text-align: center; +} + +.checkbox::before { + content: ' '; +} + +.checkbox.is-checked::before { + content: '\2714'; +} + +/* Data Table */ + +.data-table { + width: 100%; +} + +.data-table thead th { + padding: 5px; + text-align: left; +} + +.data-table tbody td { + padding: 5px; + text-align: left; + border-top: 1px solid black; + vertical-align: top; +} + +/* Layout Table */ + +.layout-table { + width: 100%; + border: 0; +} + +.layout-table td { + vertical-align: top; +} + +/* Table Helpers */ + +td.is-vcentered { + vertical-align: middle; +} + +td.is-width-1 { + width: 1px; +} + +/* Padding / Margins */ + +.m-0 { + margin: 0; +} + +.mt-1 { + margin-top: 10px; +} + +.mb-0 { + margin-bottom: 0; +} + +.pl-1 { + padding-left: 10px; +} + +/* Text Utilities */ + +.is-8pt { + font-size: 8pt; +} + +.has-text-right { + text-align: right; +} + +.has-text-italicized { + font-style: italic; +} + +.has-text-nowrap { + white-space: nowrap; +} \ No newline at end of file diff --git a/views/print/pdf/workOrder.ejs b/views/print/pdf/workOrder.ejs new file mode 100644 index 00000000..592f2d8b --- /dev/null +++ b/views/print/pdf/workOrder.ejs @@ -0,0 +1,156 @@ + + + + + + + + + + +
+

Work Order #<%= workOrder.workOrderNumber %>

+
+ + <% if (workOrder.workOrderCloseDate) { %> + CLOSED
+ <% } %> + <%= workOrder.workOrderType %> +
+
+
+ + + + + +
+ +

+ Description
+ <%= workOrder.workOrderDescription %> +

+
+

+ Open Date
+ <%= workOrder.workOrderOpenDateString %> +

+ <% if (workOrder.workOrderCloseDate) { %> +

+ Close Date
+ <%= workOrder.workOrderCloseDateString %> +

+ <% } %> +
+
+ + <% if (workOrder.workOrderLots.length > 0) { %> +

<%= configFunctions.getProperty("aliases.lots") %>

+ + + + + + + + + + + + <% for (const lot of workOrder.workOrderLots) { %> + + + + + + + <% } %> + +
<%= configFunctions.getProperty("aliases.lot") %><%= configFunctions.getProperty("aliases.map") %><%= configFunctions.getProperty("aliases.lot") %> TypeStatus
<%= lot.lotName %><%= lot.mapName %><%= lot.lotType %><%= lot.lotStatus %>
+ <% } %> + + <% if (workOrder.workOrderLotOccupancies.length > 0) { %> +

<%= configFunctions.getProperty("aliases.occupancies") %>

+ + + + + + + + + + + + + <% for (const occupancy of workOrder.workOrderLotOccupancies) { %> + + + + + + + + <% } %> + +
<%= configFunctions.getProperty("aliases.occupancy") %> Type<%= configFunctions.getProperty("aliases.lot") %>Start DateEnd Date<%= configFunctions.getProperty("aliases.occupants") %>
<%= occupancy.occupancyType %><%= occupancy.lotName %><%= occupancy.occupancyStartDateString %><%= occupancy.occupancyStartEndString %> + <% for (const occupant of occupancy.lotOccupancyOccupants) { %> + <%= occupant.occupantName %>
+ <% } %> +
+ <% } %> + + <% if (workOrder.workOrderMilestones.length > 0) { %> +

Milestones

+ + + + + + + + + + + <% for (const milestone of workOrder.workOrderMilestones) { %> + + + + + + <% } %> + +
Milestone DescriptionDue Date
+ <% if (milestone.workOrderMilestoneCompletionDate) { %> + + <% } else { %> + + <% } %> + + <% if (milestone.workOrderMilestoneTypeId) { %> + <%= milestone.workOrderMilestoneType %>
+ <% } %> + <%= milestone.workOrderMilestoneDescription %> +
+ <%= milestone.workOrderMilestoneDateString %> + <% if (milestone.workOrderMilestoneTime) { %> + <%= milestone.workOrderMilestoneTimeString %> + <% } %> +
+ <% } %> + +

Notes

+ +

+ <% + const recordCreateDate = new Date(workOrder.recordCreate_timeMillis); + const currentDate = new Date(); + %> + Work order created <%= dateTimeFunctions.dateToString(recordCreateDate) %> at <%= dateTimeFunctions.dateToTimeString(recordCreateDate) %>. + Printed <%= dateTimeFunctions.dateToString(currentDate) %> at <%= dateTimeFunctions.dateToTimeString(currentDate) %>.
+ workOrderId = <%= workOrder.workOrderId %> +

+ + \ No newline at end of file diff --git a/views/workOrder-edit.ejs b/views/workOrder-edit.ejs index e100ede0..d54133d4 100644 --- a/views/workOrder-edit.ejs +++ b/views/workOrder-edit.ejs @@ -30,13 +30,54 @@ -

- <% if (isCreate) { %> +<% if (isCreate) { %> +

Create a New Work Order - <% } else { %> - Work Order #<%= workOrder.workOrderNumber || "(No Number)" %> - <% } %> -

+ +<% } else { %> +
+
+
+

+ Work Order #<%= workOrder.workOrderNumber || "(No Number)" %> +

+
+
+ <% if (configFunctions.getProperty("settings.workOrders.prints").length > 0) { %> +
+
+ <% if (configFunctions.getProperty("settings.workOrders.prints").length === 1) { %> + /?workOrderId=<%= workOrder.workOrderId %>" target="_blank"> + + Print + + <% } else { %> + + <% } %> +
+
+ <% } %> +
+<% } %>
diff --git a/views/workOrder-view.ejs b/views/workOrder-view.ejs index 43c4a8a0..ded5e8f3 100644 --- a/views/workOrder-view.ejs +++ b/views/workOrder-view.ejs @@ -17,9 +17,48 @@ -

- Work Order #<%= workOrder.workOrderNumber || "(No Number)" %> -

+
+
+
+

+ Work Order #<%= workOrder.workOrderNumber || "(No Number)" %> +

+
+
+ <% if (configFunctions.getProperty("settings.workOrders.prints").length > 0) { %> +
+
+ <% if (configFunctions.getProperty("settings.workOrders.prints").length === 1) { %> + /?workOrderId=<%= workOrder.workOrderId %>" target="_blank"> + + Print + + <% } else { %> + + <% } %> +
+
+ <% } %> +
<% if (user.userProperties.canUpdate && !workOrder.workOrderCloseDate) { %>