basic work order print

deepsource-autofix-76c6eb20
Dan Gowans 2022-09-28 11:04:39 -04:00
parent 10c374c470
commit a29f380a9c
14 changed files with 400 additions and 14 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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(".");

View File

@ -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;

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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";

View File

@ -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";

View File

@ -1,6 +1,8 @@
<html>
<head>
<style>
<%- include('style.css'); %>
</style>
</head>
<body>
<h1>Burial Permit</h1>

View File

@ -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;
}

View File

@ -0,0 +1,156 @@
<html>
<head>
<style>
<%- include('style.css'); %>
</style>
</head>
<body>
<table class="layout-table">
<tr>
<td>
<h1 class="m-0">Work Order #<%= workOrder.workOrderNumber %></h1>
</td>
<td class="has-text-right is-vcentered">
<strong>
<% if (workOrder.workOrderCloseDate) { %>
CLOSED<br />
<% } %>
<%= workOrder.workOrderType %>
</strong>
</td>
</tr>
</table>
<div class="box mt-1">
<table class="layout-table">
<tr>
<td>
<p>
<strong>Description</strong><br />
<%= workOrder.workOrderDescription %>
</p>
</td>
<td class="pl-1 is-width-1 has-text-nowrap">
<p>
<strong>Open Date</strong><br />
<%= workOrder.workOrderOpenDateString %>
</p>
<% if (workOrder.workOrderCloseDate) { %>
<p>
<strong>Close Date</strong><br />
<%= workOrder.workOrderCloseDateString %>
</p>
<% } %>
</td>
</tr>
</table>
</div>
<% if (workOrder.workOrderLots.length > 0) { %>
<h2 class="mb-0"><%= configFunctions.getProperty("aliases.lots") %></h2>
<table class="data-table">
<thead>
<tr>
<th><%= configFunctions.getProperty("aliases.lot") %></th>
<th><%= configFunctions.getProperty("aliases.map") %></th>
<th><%= configFunctions.getProperty("aliases.lot") %> Type</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<% for (const lot of workOrder.workOrderLots) { %>
<tr>
<td><%= lot.lotName %></td>
<td><%= lot.mapName %></td>
<td><%= lot.lotType %></td>
<td><%= lot.lotStatus %></td>
</tr>
<% } %>
</tbody>
</table>
<% } %>
<% if (workOrder.workOrderLotOccupancies.length > 0) { %>
<h2 class="mb-0"><%= configFunctions.getProperty("aliases.occupancies") %></h2>
<table class="data-table">
<thead>
<tr>
<th><%= configFunctions.getProperty("aliases.occupancy") %> Type</th>
<th><%= configFunctions.getProperty("aliases.lot") %></th>
<th>Start Date</th>
<th>End Date</th>
<th><%= configFunctions.getProperty("aliases.occupants") %></th>
</tr>
</thead>
<tbody>
<% for (const occupancy of workOrder.workOrderLotOccupancies) { %>
<tr>
<td><%= occupancy.occupancyType %></td>
<td><%= occupancy.lotName %></td>
<td><%= occupancy.occupancyStartDateString %></td>
<td><%= occupancy.occupancyStartEndString %></td>
<td>
<% for (const occupant of occupancy.lotOccupancyOccupants) { %>
<%= occupant.occupantName %><br />
<% } %>
</td>
</tr>
<% } %>
</tbody>
</table>
<% } %>
<% if (workOrder.workOrderMilestones.length > 0) { %>
<h2 class="mb-0">Milestones</h2>
<table class="data-table">
<thead>
<tr>
<th class="is-width-1"></th>
<th>Milestone Description</th>
<th>Due Date</th>
</tr>
</thead>
<tbody>
<% for (const milestone of workOrder.workOrderMilestones) { %>
<tr>
<td class="is-width-1">
<% if (milestone.workOrderMilestoneCompletionDate) { %>
<span class="checkbox is-checked"></span>
<% } else { %>
<span class="checkbox"></span>
<% } %>
</td>
<td>
<% if (milestone.workOrderMilestoneTypeId) { %>
<strong><%= milestone.workOrderMilestoneType %></strong><br />
<% } %>
<%= milestone.workOrderMilestoneDescription %>
</td>
<td>
<%= milestone.workOrderMilestoneDateString %>
<% if (milestone.workOrderMilestoneTime) { %>
<%= milestone.workOrderMilestoneTimeString %>
<% } %>
</td>
</tr>
<% } %>
</tbody>
</table>
<% } %>
<h2 class="mb-0">Notes</h2>
<p class="has-text-right has-text-italicized is-8pt" style="position:absolute;bottom:10px;right:10px">
<%
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) %>.<br />
workOrderId = <%= workOrder.workOrderId %>
</p>
</body>
</html>

View File

@ -30,13 +30,54 @@
</ul>
</nav>
<h1 class="title is-1">
<% if (isCreate) { %>
<% if (isCreate) { %>
<h1 class="title is-1">
Create a New Work Order
<% } else { %>
Work Order #<%= workOrder.workOrderNumber || "(No Number)" %>
<% } %>
</h1>
</h1>
<% } else { %>
<div class="level">
<div class="level-left">
<div class="level-item is-justify-content-left">
<h1 class="title is-1">
Work Order #<%= workOrder.workOrderNumber || "(No Number)" %>
</h1>
</div>
</div>
<% if (configFunctions.getProperty("settings.workOrders.prints").length > 0) { %>
<div class="level-right is-hidden-print">
<div class="level-item is-justify-content-right">
<% if (configFunctions.getProperty("settings.workOrders.prints").length === 1) { %>
<a class="button is-link" href="<%= urlPrefix %>/print/<%= configFunctions.getProperty("settings.workOrders.prints")[0] %>/?workOrderId=<%= workOrder.workOrderId %>" target="_blank">
<span class="icon is-small"><i class="fas fa-print" aria-hidden="true"></i></span>
<span>Print</span>
</a>
<% } else { %>
<div class="dropdown is-right">
<div class="dropdown-trigger">
<button class="button is-link" type="button">
<span class="icon is-small"><i class="fas fa-print" aria-hidden="true"></i></span>
<span>Print</span>
<span class="icon is-small"><i class="fas fa-caret-down" aria-hidden="true"></i></span>
</button>
</div>
<div class="dropdown-menu">
<div class="dropdown-content">
<% for (const printName of configFunctions.getProperty("settings.workOrders.prints")) { %>
<% const printConfig = printFunctions.getPrintConfig(printName); %>
<a class="dropdown-item" href="<%= urlPrefix %>/print/<%= printName %>/?workOrderId=<%= workOrder.workOrderId %>">
<span class="icon is-small"><i class="fas fa-print" aria-hidden="true"></i></span>
<span><%= printConfig.title %></span>
</a>
<% } %>
</div>
</div>
</div>
<% } %>
</div>
</div>
<% } %>
</div>
<% } %>
<div class="columns is-desktop">
<div class="column">

View File

@ -17,9 +17,48 @@
</ul>
</nav>
<h1 class="title is-1">
Work Order #<%= workOrder.workOrderNumber || "(No Number)" %>
</h1>
<div class="level">
<div class="level-left">
<div class="level-item is-justify-content-left">
<h1 class="title is-1">
Work Order #<%= workOrder.workOrderNumber || "(No Number)" %>
</h1>
</div>
</div>
<% if (configFunctions.getProperty("settings.workOrders.prints").length > 0) { %>
<div class="level-right is-hidden-print">
<div class="level-item is-justify-content-right">
<% if (configFunctions.getProperty("settings.workOrders.prints").length === 1) { %>
<a class="button is-link" href="<%= urlPrefix %>/print/<%= configFunctions.getProperty("settings.workOrders.prints")[0] %>/?workOrderId=<%= workOrder.workOrderId %>" target="_blank">
<span class="icon is-small"><i class="fas fa-print" aria-hidden="true"></i></span>
<span>Print</span>
</a>
<% } else { %>
<div class="dropdown is-right">
<div class="dropdown-trigger">
<button class="button is-link" type="button">
<span class="icon is-small"><i class="fas fa-print" aria-hidden="true"></i></span>
<span>Print</span>
<span class="icon is-small"><i class="fas fa-caret-down" aria-hidden="true"></i></span>
</button>
</div>
<div class="dropdown-menu">
<div class="dropdown-content">
<% for (const printName of configFunctions.getProperty("settings.workOrders.prints")) { %>
<% const printConfig = printFunctions.getPrintConfig(printName); %>
<a class="dropdown-item" href="<%= urlPrefix %>/print/<%= printName %>/?workOrderId=<%= workOrder.workOrderId %>">
<span class="icon is-small"><i class="fas fa-print" aria-hidden="true"></i></span>
<span><%= printConfig.title %></span>
</a>
<% } %>
</div>
</div>
</div>
<% } %>
</div>
</div>
<% } %>
</div>
<% if (user.userProperties.canUpdate && !workOrder.workOrderCloseDate) { %>
<div class="fixed-container is-fixed-bottom-right mx-4 my-4 has-text-right is-hidden-print">