include comments

deepsource-autofix-76c6eb20
Dan Gowans 2024-06-27 15:09:31 -04:00
parent ed2a14ee5f
commit 82906b0133
38 changed files with 383 additions and 9 deletions

24
app.js
View File

@ -27,13 +27,20 @@ import routerReports from './routes/reports.js';
import routerWorkOrders from './routes/workOrders.js'; import routerWorkOrders from './routes/workOrders.js';
import { version } from './version.js'; import { version } from './version.js';
const debug = Debug(`lot-occupancy-system:app:${process.pid}`); const debug = Debug(`lot-occupancy-system:app:${process.pid}`);
/*
* INITIALIZE THE DATABASE
*/
initializeDatabase(); initializeDatabase();
/*
* INITIALIZE APP
*/
const _dirname = '.'; const _dirname = '.';
export const app = express(); export const app = express();
app.disable('X-Powered-By'); app.disable('X-Powered-By');
if (!configFunctions.getConfigProperty('reverseProxy.disableEtag')) { if (!configFunctions.getConfigProperty('reverseProxy.disableEtag')) {
app.set('etag', false); app.set('etag', false);
} }
// View engine setup
app.set('views', path.join(_dirname, 'views')); app.set('views', path.join(_dirname, 'views'));
app.set('view engine', 'ejs'); app.set('view engine', 'ejs');
if (!configFunctions.getConfigProperty('reverseProxy.disableCompression')) { if (!configFunctions.getConfigProperty('reverseProxy.disableCompression')) {
@ -51,10 +58,16 @@ app.use(cookieParser());
app.use(csurf({ app.use(csurf({
cookie: true cookie: true
})); }));
/*
* Rate Limiter
*/
app.use(rateLimit({ app.use(rateLimit({
windowMs: 10_000, windowMs: 10_000,
max: useTestDatabases ? 1_000_000 : 200 max: useTestDatabases ? 1_000_000 : 200
})); }));
/*
* STATIC ROUTES
*/
const urlPrefix = configFunctions.getConfigProperty('reverseProxy.urlPrefix'); const urlPrefix = configFunctions.getConfigProperty('reverseProxy.urlPrefix');
if (urlPrefix !== '') { if (urlPrefix !== '') {
debug(`urlPrefix = ${urlPrefix}`); debug(`urlPrefix = ${urlPrefix}`);
@ -66,8 +79,12 @@ app.use(`${urlPrefix}/lib/cityssm-bulma-webapp-js`, express.static(path.join('no
app.use(`${urlPrefix}/lib/fa`, express.static(path.join('node_modules', '@fortawesome', 'fontawesome-free'))); app.use(`${urlPrefix}/lib/fa`, express.static(path.join('node_modules', '@fortawesome', 'fontawesome-free')));
app.use(`${urlPrefix}/lib/leaflet`, express.static(path.join('node_modules', 'leaflet', 'dist'))); app.use(`${urlPrefix}/lib/leaflet`, express.static(path.join('node_modules', 'leaflet', 'dist')));
app.use(`${urlPrefix}/lib/randomcolor/randomColor.js`, express.static(path.join('node_modules', 'randomcolor', 'randomColor.js'))); app.use(`${urlPrefix}/lib/randomcolor/randomColor.js`, express.static(path.join('node_modules', 'randomcolor', 'randomColor.js')));
/*
* SESSION MANAGEMENT
*/
const sessionCookieName = configFunctions.getConfigProperty('session.cookieName'); const sessionCookieName = configFunctions.getConfigProperty('session.cookieName');
const FileStoreSession = FileStore(session); const FileStoreSession = FileStore(session);
// Initialize session
app.use(session({ app.use(session({
store: new FileStoreSession({ store: new FileStoreSession({
path: './data/sessions', path: './data/sessions',
@ -84,6 +101,7 @@ app.use(session({
sameSite: 'strict' sameSite: 'strict'
} }
})); }));
// Clear cookie if no corresponding session
app.use((request, response, next) => { app.use((request, response, next) => {
if (Object.hasOwn(request.cookies, sessionCookieName) && if (Object.hasOwn(request.cookies, sessionCookieName) &&
!Object.hasOwn(request.session, 'user')) { !Object.hasOwn(request.session, 'user')) {
@ -91,6 +109,7 @@ app.use((request, response, next) => {
} }
next(); next();
}); });
// Redirect logged in users
const sessionChecker = (request, response, next) => { const sessionChecker = (request, response, next) => {
if (Object.hasOwn(request.session, 'user') && if (Object.hasOwn(request.session, 'user') &&
Object.hasOwn(request.cookies, sessionCookieName)) { Object.hasOwn(request.cookies, sessionCookieName)) {
@ -100,6 +119,10 @@ const sessionChecker = (request, response, next) => {
const redirectUrl = getSafeRedirectURL(request.originalUrl); const redirectUrl = getSafeRedirectURL(request.originalUrl);
response.redirect(`${urlPrefix}/login?redirect=${encodeURIComponent(redirectUrl)}`); response.redirect(`${urlPrefix}/login?redirect=${encodeURIComponent(redirectUrl)}`);
}; };
/*
* ROUTES
*/
// Make the user and config objects available to the templates
app.use((request, response, next) => { app.use((request, response, next) => {
response.locals.buildNumber = version; response.locals.buildNumber = version;
response.locals.user = request.session.user; response.locals.user = request.session.user;
@ -140,6 +163,7 @@ app.get(`${urlPrefix}/logout`, (request, response) => {
response.redirect(`${urlPrefix}/login`); response.redirect(`${urlPrefix}/login`);
} }
}); });
// Catch 404 and forward to error handler
app.use((request, _response, next) => { app.use((request, _response, next) => {
debug(request.url); debug(request.url);
next(createError(404, `File not found: ${request.url}`)); next(createError(404, `File not found: ${request.url}`));

View File

@ -68,6 +68,7 @@ if (process.env.STARTUP_TEST === 'true') {
debug(`Killing processes in ${killSeconds} seconds...`); debug(`Killing processes in ${killSeconds} seconds...`);
setTimeout(() => { setTimeout(() => {
debug('Killing processes'); debug('Killing processes');
// eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit
process.exit(0); process.exit(0);
}, 10_000); }, 10_000);
} }

View File

@ -1,3 +1,5 @@
// eslint-disable-next-line @eslint-community/eslint-comments/disable-enable-pair
/* eslint-disable n/no-process-exit, unicorn/no-process-exit */
import http from 'node:http'; import http from 'node:http';
import Debug from 'debug'; import Debug from 'debug';
import exitHook from 'exit-hook'; import exitHook from 'exit-hook';
@ -8,15 +10,21 @@ function onError(error) {
if (error.syscall !== 'listen') { if (error.syscall !== 'listen') {
throw error; throw error;
} }
// handle specific listen errors with friendly messages
switch (error.code) { switch (error.code) {
// eslint-disable-next-line no-fallthrough
case 'EACCES': { case 'EACCES': {
debug('Requires elevated privileges'); debug('Requires elevated privileges');
process.exit(1); process.exit(1);
// break;
} }
// eslint-disable-next-line no-fallthrough
case 'EADDRINUSE': { case 'EADDRINUSE': {
debug('Port is already in use.'); debug('Port is already in use.');
process.exit(1); process.exit(1);
// break;
} }
// eslint-disable-next-line no-fallthrough
default: { default: {
throw error; throw error;
} }
@ -29,6 +37,9 @@ function onListening(server) {
debug(`HTTP Listening on ${bind}`); debug(`HTTP Listening on ${bind}`);
} }
} }
/*
* Initialize HTTP
*/
process.title = `${getConfigProperty('application.applicationName')} (Worker)`; process.title = `${getConfigProperty('application.applicationName')} (Worker)`;
const httpPort = getConfigProperty('application.httpPort'); const httpPort = getConfigProperty('application.httpPort');
const httpServer = http.createServer(app); const httpServer = http.createServer(app);

View File

@ -18,6 +18,7 @@ describe('Update - Maps', () => {
cy.injectAxe(); cy.injectAxe();
cy.checkA11y(); cy.checkA11y();
cy.log('Populate the fields'); cy.log('Populate the fields');
// eslint-disable-next-line promise/catch-or-return, promise/always-return
cy.fixture('map.json').then((mapJSON) => { cy.fixture('map.json').then((mapJSON) => {
cy.get("input[name='mapName']") cy.get("input[name='mapName']")
.clear() .clear()
@ -53,6 +54,7 @@ describe('Update - Maps', () => {
cy.location('pathname') cy.location('pathname')
.should('not.contain', '/new') .should('not.contain', '/new')
.should('contain', '/edit'); .should('contain', '/edit');
// eslint-disable-next-line promise/catch-or-return, promise/always-return
cy.fixture('map.json').then((mapJSON) => { cy.fixture('map.json').then((mapJSON) => {
cy.get("input[name='mapName']").should('have.value', mapJSON.mapName); cy.get("input[name='mapName']").should('have.value', mapJSON.mapName);
cy.get("textarea[name='mapDescription']").should('have.value', mapJSON.mapDescription); cy.get("textarea[name='mapDescription']").should('have.value', mapJSON.mapDescription);

View File

@ -1,3 +1,4 @@
/* eslint-disable node/no-unpublished-import */
import 'cypress-axe'; import 'cypress-axe';
export const logout = () => { export const logout = () => {
cy.visit('/logout'); cy.visit('/logout');
@ -11,6 +12,7 @@ export const login = (userName) => {
cy.get("form [name='password']").type(userName); cy.get("form [name='password']").type(userName);
cy.get('form').submit(); cy.get('form').submit();
cy.location('pathname').should('not.contain', '/login'); cy.location('pathname').should('not.contain', '/login');
// Logged in pages have a navbar
cy.get('.navbar').should('have.length', 1); cy.get('.navbar').should('have.length', 1);
}; };
export const ajaxDelayMillis = 800; export const ajaxDelayMillis = 800;

View File

@ -36,6 +36,7 @@ export const configDefaultValues = {
'settings.lot.lotNamePattern': undefined, 'settings.lot.lotNamePattern': undefined,
'settings.lot.lotNameHelpText': '', 'settings.lot.lotNameHelpText': '',
'settings.lot.lotNameSortNameFunction': (lotName) => lotName, 'settings.lot.lotNameSortNameFunction': (lotName) => lotName,
// eslint-disable-next-line no-secrets/no-secrets
'settings.lotOccupancy.occupancyEndDateIsRequired': true, 'settings.lotOccupancy.occupancyEndDateIsRequired': true,
'settings.lotOccupancy.occupantCityDefault': '', 'settings.lotOccupancy.occupantCityDefault': '',
'settings.lotOccupancy.occupantProvinceDefault': '', 'settings.lotOccupancy.occupantProvinceDefault': '',
@ -50,6 +51,7 @@ export const configDefaultValues = {
'settings.printPdf.contentDisposition': 'attachment', 'settings.printPdf.contentDisposition': 'attachment',
'settings.dynamicsGP.integrationIsEnabled': false, 'settings.dynamicsGP.integrationIsEnabled': false,
'settings.dynamicsGP.mssqlConfig': undefined, 'settings.dynamicsGP.mssqlConfig': undefined,
// eslint-disable-next-line no-secrets/no-secrets
'settings.dynamicsGP.lookupOrder': ['invoice'], 'settings.dynamicsGP.lookupOrder': ['invoice'],
'settings.dynamicsGP.accountCodes': [], 'settings.dynamicsGP.accountCodes': [],
'settings.dynamicsGP.itemNumbers': [], 'settings.dynamicsGP.itemNumbers': [],

View File

@ -1,6 +1,7 @@
import Debug from 'debug'; import Debug from 'debug';
import { getConfigProperty } from '../helpers/functions.config.js'; import { getConfigProperty } from '../helpers/functions.config.js';
const debug = Debug('lot-occupancy-system:databasePaths'); const debug = Debug('lot-occupancy-system:databasePaths');
// Determine if test databases should be used
export const useTestDatabases = getConfigProperty('application.useTestDatabases') || export const useTestDatabases = getConfigProperty('application.useTestDatabases') ||
process.env.TEST_DATABASES === 'true'; process.env.TEST_DATABASES === 'true';
if (useTestDatabases) { if (useTestDatabases) {

View File

@ -5,6 +5,7 @@ import { acquireConnection } from './pool.js';
export default async function addLotOccupancyFee(lotOccupancyFeeForm, user) { export default async function addLotOccupancyFee(lotOccupancyFeeForm, user) {
const database = await acquireConnection(); const database = await acquireConnection();
const rightNowMillis = Date.now(); const rightNowMillis = Date.now();
// Calculate fee and tax (if not set)
let feeAmount; let feeAmount;
let taxAmount; let taxAmount;
if ((lotOccupancyFeeForm.feeAmount ?? '') === '') { if ((lotOccupancyFeeForm.feeAmount ?? '') === '') {
@ -23,6 +24,7 @@ export default async function addLotOccupancyFee(lotOccupancyFeeForm, user) {
? Number.parseFloat(lotOccupancyFeeForm.taxAmount) ? Number.parseFloat(lotOccupancyFeeForm.taxAmount)
: 0; : 0;
} }
// Check if record already exists
const record = database const record = database
.prepare(`select feeAmount, taxAmount, recordDelete_timeMillis .prepare(`select feeAmount, taxAmount, recordDelete_timeMillis
from LotOccupancyFees from LotOccupancyFees
@ -69,6 +71,7 @@ export default async function addLotOccupancyFee(lotOccupancyFeeForm, user) {
return true; return true;
} }
} }
// Create new record
const result = database const result = database
.prepare(`insert into LotOccupancyFees ( .prepare(`insert into LotOccupancyFees (
lotOccupancyId, feeId, lotOccupancyId, feeId,

View File

@ -9,6 +9,9 @@ export default async function cleanupDatabase(user) {
1000; 1000;
let inactivatedRecordCount = 0; let inactivatedRecordCount = 0;
let purgedRecordCount = 0; let purgedRecordCount = 0;
/*
* Work Order Comments
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update WorkOrderComments .prepare(`update WorkOrderComments
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -20,6 +23,9 @@ export default async function cleanupDatabase(user) {
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from WorkOrderComments where recordDelete_timeMillis <= ?') .prepare('delete from WorkOrderComments where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Work Order Lot Occupancies
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update WorkOrderLotOccupancies .prepare(`update WorkOrderLotOccupancies
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -31,6 +37,9 @@ export default async function cleanupDatabase(user) {
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from WorkOrderLotOccupancies where recordDelete_timeMillis <= ?') .prepare('delete from WorkOrderLotOccupancies where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Work Order Lots
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update WorkOrderLots .prepare(`update WorkOrderLots
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -42,6 +51,9 @@ export default async function cleanupDatabase(user) {
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from WorkOrderLots where recordDelete_timeMillis <= ?') .prepare('delete from WorkOrderLots where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Work Order Milestones
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update WorkOrderMilestones .prepare(`update WorkOrderMilestones
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -53,6 +65,9 @@ export default async function cleanupDatabase(user) {
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from WorkOrderMilestones where recordDelete_timeMillis <= ?') .prepare('delete from WorkOrderMilestones where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Work Orders
*/
purgedRecordCount += database purgedRecordCount += database
.prepare(`delete from WorkOrders .prepare(`delete from WorkOrders
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?
@ -61,17 +76,26 @@ export default async function cleanupDatabase(user) {
and workOrderId not in (select workOrderId from WorkOrderLots) and workOrderId not in (select workOrderId from WorkOrderLots)
and workOrderId not in (select workOrderId from WorkOrderMilestones)`) and workOrderId not in (select workOrderId from WorkOrderMilestones)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Work Order Milestone Types
*/
purgedRecordCount += database purgedRecordCount += database
.prepare(`delete from WorkOrderMilestoneTypes .prepare(`delete from WorkOrderMilestoneTypes
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?
and workOrderMilestoneTypeId not in ( and workOrderMilestoneTypeId not in (
select workOrderMilestoneTypeId from WorkOrderMilestones)`) select workOrderMilestoneTypeId from WorkOrderMilestones)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Work Order Types
*/
purgedRecordCount += database purgedRecordCount += database
.prepare(`delete from WorkOrderTypes .prepare(`delete from WorkOrderTypes
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?
and workOrderTypeId not in (select workOrderTypeId from WorkOrders)`) and workOrderTypeId not in (select workOrderTypeId from WorkOrders)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lot Occupancy Comments
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update LotOccupancyComments .prepare(`update LotOccupancyComments
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -83,6 +107,9 @@ export default async function cleanupDatabase(user) {
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from LotOccupancyComments where recordDelete_timeMillis <= ?') .prepare('delete from LotOccupancyComments where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lot Occupancy Fields
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update LotOccupancyFields .prepare(`update LotOccupancyFields
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -93,6 +120,9 @@ export default async function cleanupDatabase(user) {
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from LotOccupancyFields where recordDelete_timeMillis <= ?') .prepare('delete from LotOccupancyFields where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lot Occupancy Occupants
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update LotOccupancyOccupants .prepare(`update LotOccupancyOccupants
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -103,12 +133,19 @@ export default async function cleanupDatabase(user) {
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from LotOccupancyOccupants where recordDelete_timeMillis <= ?') .prepare('delete from LotOccupancyOccupants where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lot Occupancy Fees/Transactions
* - Maintain financials, do not delete related.
*/
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from LotOccupancyFees where recordDelete_timeMillis <= ?') .prepare('delete from LotOccupancyFees where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from LotOccupancyTransactions where recordDelete_timeMillis <= ?') .prepare('delete from LotOccupancyTransactions where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lot Occupancies
*/
purgedRecordCount += database purgedRecordCount += database
.prepare(`delete from LotOccupancies .prepare(`delete from LotOccupancies
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?
@ -119,6 +156,9 @@ export default async function cleanupDatabase(user) {
and lotOccupancyId not in (select lotOccupancyId from LotOccupancyTransactions) and lotOccupancyId not in (select lotOccupancyId from LotOccupancyTransactions)
and lotOccupancyId not in (select lotOccupancyId from WorkOrderLotOccupancies)`) and lotOccupancyId not in (select lotOccupancyId from WorkOrderLotOccupancies)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Fees
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update Fees .prepare(`update Fees
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -131,11 +171,17 @@ export default async function cleanupDatabase(user) {
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?
and feeId not in (select feeId from LotOccupancyFees)`) and feeId not in (select feeId from LotOccupancyFees)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Fee Categories
*/
purgedRecordCount += database purgedRecordCount += database
.prepare(`delete from FeeCategories .prepare(`delete from FeeCategories
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?
and feeCategoryId not in (select feeCategoryId from Fees)`) and feeCategoryId not in (select feeCategoryId from Fees)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Occupancy Type Fields
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update OccupancyTypeFields .prepare(`update OccupancyTypeFields
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -148,6 +194,9 @@ export default async function cleanupDatabase(user) {
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?
and occupancyTypeFieldId not in (select occupancyTypeFieldId from LotOccupancyFields)`) and occupancyTypeFieldId not in (select occupancyTypeFieldId from LotOccupancyFields)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Occupancy Type Prints
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update OccupancyTypePrints .prepare(`update OccupancyTypePrints
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -158,6 +207,9 @@ export default async function cleanupDatabase(user) {
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from OccupancyTypePrints where recordDelete_timeMillis <= ?') .prepare('delete from OccupancyTypePrints where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Occupancy Types
*/
purgedRecordCount += database purgedRecordCount += database
.prepare(`delete from OccupancyTypes .prepare(`delete from OccupancyTypes
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?
@ -166,11 +218,17 @@ export default async function cleanupDatabase(user) {
and occupancyTypeId not in (select occupancyTypeId from LotOccupancies) and occupancyTypeId not in (select occupancyTypeId from LotOccupancies)
and occupancyTypeId not in (select occupancyTypeId from Fees)`) and occupancyTypeId not in (select occupancyTypeId from Fees)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lot Occupant Types
*/
purgedRecordCount += database purgedRecordCount += database
.prepare(`delete from LotOccupantTypes .prepare(`delete from LotOccupantTypes
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?
and lotOccupantTypeId not in (select lotOccupantTypeId from LotOccupancyOccupants)`) and lotOccupantTypeId not in (select lotOccupantTypeId from LotOccupancyOccupants)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lot Comments
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update LotComments .prepare(`update LotComments
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -181,6 +239,9 @@ export default async function cleanupDatabase(user) {
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from LotComments where recordDelete_timeMillis <= ?') .prepare('delete from LotComments where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lot Fields
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update LotFields .prepare(`update LotFields
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -191,6 +252,9 @@ export default async function cleanupDatabase(user) {
purgedRecordCount += database purgedRecordCount += database
.prepare('delete from LotFields where recordDelete_timeMillis <= ?') .prepare('delete from LotFields where recordDelete_timeMillis <= ?')
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lots
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update Lots .prepare(`update Lots
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -206,11 +270,17 @@ export default async function cleanupDatabase(user) {
and lotId not in (select lotId from LotOccupancies) and lotId not in (select lotId from LotOccupancies)
and lotId not in (select lotId from WorkOrderLots)`) and lotId not in (select lotId from WorkOrderLots)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lot Statuses
*/
purgedRecordCount += database purgedRecordCount += database
.prepare(`delete from LotStatuses .prepare(`delete from LotStatuses
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?
and lotStatusId not in (select lotStatusId from Lots)`) and lotStatusId not in (select lotStatusId from Lots)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lot Type Fields
*/
inactivatedRecordCount += database inactivatedRecordCount += database
.prepare(`update LotTypeFields .prepare(`update LotTypeFields
set recordDelete_userName = ?, set recordDelete_userName = ?,
@ -223,6 +293,9 @@ export default async function cleanupDatabase(user) {
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?
and lotTypeFieldId not in (select lotTypeFieldId from LotFields)`) and lotTypeFieldId not in (select lotTypeFieldId from LotFields)`)
.run(recordDeleteTimeMillisMin).changes; .run(recordDeleteTimeMillisMin).changes;
/*
* Lot Types
*/
purgedRecordCount += database purgedRecordCount += database
.prepare(`delete from LotTypes .prepare(`delete from LotTypes
where recordDelete_timeMillis <= ? where recordDelete_timeMillis <= ?

View File

@ -12,6 +12,9 @@ export default async function copyLotOccupancy(oldLotOccupancyId, user) {
occupancyStartDateString: dateToString(new Date()), occupancyStartDateString: dateToString(new Date()),
occupancyEndDateString: '' occupancyEndDateString: ''
}, user, database); }, user, database);
/*
* Copy Fields
*/
const rightNowMillis = Date.now(); const rightNowMillis = Date.now();
for (const occupancyField of oldLotOccupancy.lotOccupancyFields ?? []) { for (const occupancyField of oldLotOccupancy.lotOccupancyFields ?? []) {
database database
@ -22,6 +25,9 @@ export default async function copyLotOccupancy(oldLotOccupancyId, user) {
values (?, ?, ?, ?, ?, ?, ?)`) values (?, ?, ?, ?, ?, ?, ?)`)
.run(newLotOccupancyId, occupancyField.occupancyTypeFieldId, occupancyField.lotOccupancyFieldValue, user.userName, rightNowMillis, user.userName, rightNowMillis); .run(newLotOccupancyId, occupancyField.occupancyTypeFieldId, occupancyField.lotOccupancyFieldValue, user.userName, rightNowMillis, user.userName, rightNowMillis);
} }
/*
* Copy Occupants
*/
for (const occupant of oldLotOccupancy.lotOccupancyOccupants ?? []) { for (const occupant of oldLotOccupancy.lotOccupancyOccupants ?? []) {
await addLotOccupancyOccupant({ await addLotOccupancyOccupant({
lotOccupancyId: newLotOccupancyId, lotOccupancyId: newLotOccupancyId,

View File

@ -6,7 +6,9 @@ export default async function getLotOccupancyComments(lotOccupancyId, connectedD
database.function('userFn_timeIntegerToString', timeIntegerToString); database.function('userFn_timeIntegerToString', timeIntegerToString);
database.function('userFn_timeIntegerToPeriodString', timeIntegerToPeriodString); database.function('userFn_timeIntegerToPeriodString', timeIntegerToPeriodString);
const lotComments = database const lotComments = database
.prepare(`select lotOccupancyCommentId, .prepare(
// eslint-disable-next-line no-secrets/no-secrets
`select lotOccupancyCommentId,
lotOccupancyCommentDate, userFn_dateIntegerToString(lotOccupancyCommentDate) as lotOccupancyCommentDateString, lotOccupancyCommentDate, userFn_dateIntegerToString(lotOccupancyCommentDate) as lotOccupancyCommentDateString,
lotOccupancyCommentTime, lotOccupancyCommentTime,
userFn_timeIntegerToString(lotOccupancyCommentTime) as lotOccupancyCommentTimeString, userFn_timeIntegerToString(lotOccupancyCommentTime) as lotOccupancyCommentTimeString,

View File

@ -5,11 +5,15 @@ export default async function getNextWorkOrderNumber(connectedDatabase) {
const paddingLength = getConfigProperty('settings.workOrders.workOrderNumberLength'); const paddingLength = getConfigProperty('settings.workOrders.workOrderNumberLength');
const currentYearString = new Date().getFullYear().toString(); const currentYearString = new Date().getFullYear().toString();
const regex = new RegExp(`^${currentYearString}-\\d+$`); const regex = new RegExp(`^${currentYearString}-\\d+$`);
database.function('userFn_matchesWorkOrderNumberSyntax', (workOrderNumber) => { database.function(
// eslint-disable-next-line no-secrets/no-secrets
'userFn_matchesWorkOrderNumberSyntax', (workOrderNumber) => {
return regex.test(workOrderNumber) ? 1 : 0; return regex.test(workOrderNumber) ? 1 : 0;
}); });
const workOrderNumberRecord = database const workOrderNumberRecord = database
.prepare(`select workOrderNumber from WorkOrders .prepare(
// eslint-disable-next-line no-secrets/no-secrets
`select workOrderNumber from WorkOrders
where userFn_matchesWorkOrderNumberSyntax(workOrderNumber) = 1 where userFn_matchesWorkOrderNumberSyntax(workOrderNumber) = 1
order by cast(substr(workOrderNumber, instr(workOrderNumber, '-') + 1) as integer) desc`) order by cast(substr(workOrderNumber, instr(workOrderNumber, '-') + 1) as integer) desc`)
.get(); .get();

View File

@ -1,6 +1,7 @@
import { getConfigProperty } from '../helpers/functions.config.js'; import { getConfigProperty } from '../helpers/functions.config.js';
import { acquireConnection } from './pool.js'; import { acquireConnection } from './pool.js';
const availablePrints = getConfigProperty('settings.lotOccupancy.prints'); const availablePrints = getConfigProperty('settings.lotOccupancy.prints');
// eslint-disable-next-line @typescript-eslint/naming-convention
const userFunction_configContainsPrintEJS = (printEJS) => { const userFunction_configContainsPrintEJS = (printEJS) => {
if (printEJS === '*' || availablePrints.includes(printEJS)) { if (printEJS === '*' || availablePrints.includes(printEJS)) {
return 1; return 1;
@ -9,7 +10,9 @@ const userFunction_configContainsPrintEJS = (printEJS) => {
}; };
export default async function getOccupancyTypePrints(occupancyTypeId, connectedDatabase) { export default async function getOccupancyTypePrints(occupancyTypeId, connectedDatabase) {
const database = connectedDatabase ?? (await acquireConnection()); const database = connectedDatabase ?? (await acquireConnection());
database.function('userFn_configContainsPrintEJS', userFunction_configContainsPrintEJS); database.function(
// eslint-disable-next-line no-secrets/no-secrets
'userFn_configContainsPrintEJS', userFunction_configContainsPrintEJS);
const results = database const results = database
.prepare(`select printEJS, orderNumber .prepare(`select printEJS, orderNumber
from OccupancyTypePrints from OccupancyTypePrints

View File

@ -1,3 +1,5 @@
// eslint-disable-next-line @eslint-community/eslint-comments/disable-enable-pair
/* eslint-disable no-case-declarations */
import { dateIntegerToString, dateStringToInteger, dateToInteger, timeIntegerToString } from '@cityssm/utils-datetime'; import { dateIntegerToString, dateStringToInteger, dateToInteger, timeIntegerToString } from '@cityssm/utils-datetime';
import camelCase from 'camelcase'; import camelCase from 'camelcase';
import { getConfigProperty } from '../helpers/functions.config.js'; import { getConfigProperty } from '../helpers/functions.config.js';
@ -38,6 +40,7 @@ const occupantCommentAlias = `${occupantCamelCase}Comment`;
export default async function getReportData(reportName, reportParameters = {}) { export default async function getReportData(reportName, reportParameters = {}) {
let sql; let sql;
const sqlParameters = []; const sqlParameters = [];
// eslint-disable-next-line sonarjs/max-switch-cases
switch (reportName) { switch (reportName) {
case 'maps-all': { case 'maps-all': {
sql = 'select * from Maps'; sql = 'select * from Maps';

View File

@ -3,6 +3,7 @@ import { getConfigProperty } from '../helpers/functions.config.js';
import getLotOccupancies from './getLotOccupancies.js'; import getLotOccupancies from './getLotOccupancies.js';
import getLots from './getLots.js'; import getLots from './getLots.js';
import { acquireConnection } from './pool.js'; import { acquireConnection } from './pool.js';
// eslint-disable-next-line security/detect-unsafe-regex
const commaSeparatedNumbersRegex = /^\d+(?:,\d+)*$/; const commaSeparatedNumbersRegex = /^\d+(?:,\d+)*$/;
function buildWhereClause(filters) { function buildWhereClause(filters) {
let sqlWhereClause = ' where m.recordDelete_timeMillis is null and w.recordDelete_timeMillis is null'; let sqlWhereClause = ' where m.recordDelete_timeMillis is null and w.recordDelete_timeMillis is null';
@ -66,7 +67,9 @@ export default async function getWorkOrderMilestones(filters, options, connected
database.function('userFn_dateIntegerToString', dateIntegerToString); database.function('userFn_dateIntegerToString', dateIntegerToString);
database.function('userFn_timeIntegerToString', timeIntegerToString); database.function('userFn_timeIntegerToString', timeIntegerToString);
database.function('userFn_timeIntegerToPeriodString', timeIntegerToPeriodString); database.function('userFn_timeIntegerToPeriodString', timeIntegerToPeriodString);
// Filters
const { sqlWhereClause, sqlParameters } = buildWhereClause(filters); const { sqlWhereClause, sqlParameters } = buildWhereClause(filters);
// Order By
let orderByClause = ''; let orderByClause = '';
switch (options.orderBy) { switch (options.orderBy) {
case 'completion': { case 'completion': {
@ -84,6 +87,8 @@ export default async function getWorkOrderMilestones(filters, options, connected
break; break;
} }
} }
// Query
// eslint-disable-next-line no-secrets/no-secrets
const sql = `select m.workOrderMilestoneId, const sql = `select m.workOrderMilestoneId,
m.workOrderMilestoneTypeId, t.workOrderMilestoneType, m.workOrderMilestoneTypeId, t.workOrderMilestoneType,
m.workOrderMilestoneDate, m.workOrderMilestoneDate,

View File

@ -1,3 +1,5 @@
// eslint-disable-next-line @eslint-community/eslint-comments/disable-enable-pair
/* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, n/no-unpublished-import */
import gulp from 'gulp'; import gulp from 'gulp';
import changed from 'gulp-changed'; import changed from 'gulp-changed';
import include from 'gulp-include'; import include from 'gulp-include';
@ -5,14 +7,22 @@ import minify from 'gulp-minify';
import gulpSass from 'gulp-sass'; import gulpSass from 'gulp-sass';
import dartSass from 'sass'; import dartSass from 'sass';
const sass = gulpSass(dartSass); const sass = gulpSass(dartSass);
/*
* Compile SASS
*/
const publicSCSSDestination = 'public/stylesheets'; const publicSCSSDestination = 'public/stylesheets';
function publicSCSSFunction() { function publicSCSSFunction() {
return gulp return gulp
.src('public-scss/*.scss') .src('public-scss/*.scss')
.pipe(sass({ outputStyle: 'compressed', includePaths: ['node_modules'] }).on('error', sass.logError)) .pipe(sass({ outputStyle: 'compressed', includePaths: ['node_modules'] }).on('error',
// eslint-disable-next-line @typescript-eslint/unbound-method
sass.logError))
.pipe(gulp.dest(publicSCSSDestination)); .pipe(gulp.dest(publicSCSSDestination));
} }
gulp.task('public-scss', publicSCSSFunction); gulp.task('public-scss', publicSCSSFunction);
/*
* Minify public/javascripts
*/
const publicJavascriptsDestination = 'public/javascripts'; const publicJavascriptsDestination = 'public/javascripts';
function publicJavascriptsMinFunction() { function publicJavascriptsMinFunction() {
return gulp return gulp
@ -45,6 +55,9 @@ gulp.task('public-javascript-adminTables', publicJavascriptsAdminTablesFunction)
gulp.task('public-javascript-lotOccupancyEdit', publicJavascriptsLotOccupancyEditFunction); gulp.task('public-javascript-lotOccupancyEdit', publicJavascriptsLotOccupancyEditFunction);
gulp.task('public-javascript-workOrderEdit', publicJavascriptsWorkOrderEditFunction); gulp.task('public-javascript-workOrderEdit', publicJavascriptsWorkOrderEditFunction);
gulp.task('public-javascript-min', publicJavascriptsMinFunction); gulp.task('public-javascript-min', publicJavascriptsMinFunction);
/*
* Watch
*/
function watchFunction() { function watchFunction() {
gulp.watch('public-scss/*.scss', publicSCSSFunction); gulp.watch('public-scss/*.scss', publicSCSSFunction);
gulp.watch('public-typescript/adminTables/*.js', publicJavascriptsAdminTablesFunction); gulp.watch('public-typescript/adminTables/*.js', publicJavascriptsAdminTablesFunction);
@ -53,6 +66,9 @@ function watchFunction() {
gulp.watch('public-typescript/*.js', publicJavascriptsMinFunction); gulp.watch('public-typescript/*.js', publicJavascriptsMinFunction);
} }
gulp.task('watch', watchFunction); gulp.task('watch', watchFunction);
/*
* Initialize default
*/
gulp.task('default', () => { gulp.task('default', () => {
publicJavascriptsAdminTablesFunction(); publicJavascriptsAdminTablesFunction();
publicJavascriptsLotOccupancyEditFunction(); publicJavascriptsLotOccupancyEditFunction();

View File

@ -1,3 +1,4 @@
/* eslint-disable unicorn/filename-case, @eslint-community/eslint-comments/disable-enable-pair */
import ical, { ICalEventStatus } from 'ical-generator'; import ical, { ICalEventStatus } from 'ical-generator';
import getWorkOrderMilestones from '../../database/getWorkOrderMilestones.js'; import getWorkOrderMilestones from '../../database/getWorkOrderMilestones.js';
import { getConfigProperty } from '../../helpers/functions.config.js'; import { getConfigProperty } from '../../helpers/functions.config.js';
@ -38,6 +39,7 @@ function buildEventSummary(milestone) {
} }
return summary; return summary;
} }
// eslint-disable-next-line @typescript-eslint/naming-convention
function buildEventDescriptionHTML_occupancies(request, milestone) { function buildEventDescriptionHTML_occupancies(request, milestone) {
let descriptionHTML = ''; let descriptionHTML = '';
if (milestone.workOrderLotOccupancies.length > 0) { if (milestone.workOrderLotOccupancies.length > 0) {
@ -82,6 +84,7 @@ function buildEventDescriptionHTML_occupancies(request, milestone) {
} }
return descriptionHTML; return descriptionHTML;
} }
// eslint-disable-next-line @typescript-eslint/naming-convention
function buildEventDescriptionHTML_lots(request, milestone) { function buildEventDescriptionHTML_lots(request, milestone) {
let descriptionHTML = ''; let descriptionHTML = '';
if (milestone.workOrderLots.length > 0) { if (milestone.workOrderLots.length > 0) {
@ -118,6 +121,7 @@ function buildEventDescriptionHTML_lots(request, milestone) {
} }
return descriptionHTML; return descriptionHTML;
} }
// eslint-disable-next-line @typescript-eslint/naming-convention
function buildEventDescriptionHTML_prints(request, milestone) { function buildEventDescriptionHTML_prints(request, milestone) {
let descriptionHTML = ''; let descriptionHTML = '';
const prints = getConfigProperty('settings.workOrders.prints'); const prints = getConfigProperty('settings.workOrders.prints');
@ -169,6 +173,9 @@ function buildEventLocation(milestone) {
} }
export default async function handler(request, response) { export default async function handler(request, response) {
const urlRoot = getUrlRoot(request); const urlRoot = getUrlRoot(request);
/*
* Get work order milestones
*/
const workOrderMilestoneFilters = { const workOrderMilestoneFilters = {
workOrderTypeIds: request.query.workOrderTypeIds, workOrderTypeIds: request.query.workOrderTypeIds,
workOrderMilestoneTypeIds: request.query.workOrderMilestoneTypeIds workOrderMilestoneTypeIds: request.query.workOrderMilestoneTypeIds
@ -184,6 +191,9 @@ export default async function handler(request, response) {
includeWorkOrders: true, includeWorkOrders: true,
orderBy: 'date' orderBy: 'date'
}); });
/*
* Create calendar object
*/
const calendar = ical({ const calendar = ical({
name: 'Work Order Milestone Calendar', name: 'Work Order Milestone Calendar',
url: `${urlRoot}/workOrders` url: `${urlRoot}/workOrders`
@ -196,13 +206,19 @@ export default async function handler(request, response) {
company: calendarCompany, company: calendarCompany,
product: calendarProduct product: calendarProduct
}); });
/*
* Loop through milestones
*/
for (const milestone of workOrderMilestones) { for (const milestone of workOrderMilestones) {
const milestoneTimePieces = `${milestone.workOrderMilestoneDateString} ${milestone.workOrderMilestoneTimeString}`.split(timeStringSplitRegex); const milestoneTimePieces = `${milestone.workOrderMilestoneDateString} ${milestone.workOrderMilestoneTimeString}`.split(timeStringSplitRegex);
const milestoneDate = new Date(Number.parseInt(milestoneTimePieces[0], 10), Number.parseInt(milestoneTimePieces[1], 10) - 1, Number.parseInt(milestoneTimePieces[2], 10), Number.parseInt(milestoneTimePieces[3], 10), Number.parseInt(milestoneTimePieces[4], 10)); const milestoneDate = new Date(Number.parseInt(milestoneTimePieces[0], 10), Number.parseInt(milestoneTimePieces[1], 10) - 1, Number.parseInt(milestoneTimePieces[2], 10), Number.parseInt(milestoneTimePieces[3], 10), Number.parseInt(milestoneTimePieces[4], 10));
const milestoneEndDate = new Date(milestoneDate.getTime()); const milestoneEndDate = new Date(milestoneDate.getTime());
milestoneEndDate.setHours(milestoneEndDate.getHours() + 1); milestoneEndDate.setHours(milestoneEndDate.getHours() + 1);
// Build summary (title in Outlook)
const summary = buildEventSummary(milestone); const summary = buildEventSummary(milestone);
// Build URL
const workOrderUrl = getWorkOrderUrl(request, milestone); const workOrderUrl = getWorkOrderUrl(request, milestone);
// Create event
const eventData = { const eventData = {
start: milestoneDate, start: milestoneDate,
created: new Date(milestone.recordCreate_timeMillis), created: new Date(milestone.recordCreate_timeMillis),
@ -216,22 +232,27 @@ export default async function handler(request, response) {
eventData.end = milestoneEndDate; eventData.end = milestoneEndDate;
} }
const calendarEvent = calendar.createEvent(eventData); const calendarEvent = calendar.createEvent(eventData);
// Build description
const descriptionHTML = buildEventDescriptionHTML(request, milestone); const descriptionHTML = buildEventDescriptionHTML(request, milestone);
calendarEvent.description({ calendarEvent.description({
plain: workOrderUrl, plain: workOrderUrl,
html: descriptionHTML html: descriptionHTML
}); });
// Set status
if (milestone.workOrderMilestoneCompletionDate) { if (milestone.workOrderMilestoneCompletionDate) {
calendarEvent.status(ICalEventStatus.CONFIRMED); calendarEvent.status(ICalEventStatus.CONFIRMED);
} }
// Add categories
const categories = buildEventCategoryList(milestone); const categories = buildEventCategoryList(milestone);
for (const category of categories) { for (const category of categories) {
calendarEvent.createCategory({ calendarEvent.createCategory({
name: category name: category
}); });
} }
// Set location
const location = buildEventLocation(milestone); const location = buildEventLocation(milestone);
calendarEvent.location(location); calendarEvent.location(location);
// Set organizer / attendees
if (milestone.workOrderLotOccupancies.length > 0) { if (milestone.workOrderLotOccupancies.length > 0) {
let organizerSet = false; let organizerSet = false;
for (const lotOccupancy of milestone.workOrderLotOccupancies ?? []) { for (const lotOccupancy of milestone.workOrderLotOccupancies ?? []) {

View File

@ -14,13 +14,13 @@ export default async function handler(_request, response) {
const workOrderResults = await getWorkOrders({ const workOrderResults = await getWorkOrders({
workOrderOpenDateString: currentDateString workOrderOpenDateString: currentDateString
}, { }, {
limit: 1, limit: 1, // only using the count
offset: 0 offset: 0
}); });
const lotOccupancyResults = await getLotOccupancies({ const lotOccupancyResults = await getLotOccupancies({
occupancyStartDateString: currentDateString occupancyStartDateString: currentDateString
}, { }, {
limit: 1, limit: 1, // only using the count
offset: 0, offset: 0,
includeFees: false, includeFees: false,
includeOccupants: false, includeOccupants: false,

View File

@ -1,3 +1,4 @@
/* eslint-disable unicorn/filename-case */
import { getDynamicsGPDocument } from '../../helpers/functions.dynamicsGP.js'; import { getDynamicsGPDocument } from '../../helpers/functions.dynamicsGP.js';
export default async function handler(request, response) { export default async function handler(request, response) {
const externalReceiptNumber = request.body.externalReceiptNumber; const externalReceiptNumber = request.body.externalReceiptNumber;

View File

@ -9,6 +9,9 @@ import getWorkOrderMilestoneTypesFromDatabase from '../database/getWorkOrderMile
import getWorkOrderTypesFromDatabase from '../database/getWorkOrderTypes.js'; import getWorkOrderTypesFromDatabase from '../database/getWorkOrderTypes.js';
import { getConfigProperty } from './functions.config.js'; import { getConfigProperty } from './functions.config.js';
const debug = Debug(`lot-occupancy-system:functions.cache:${process.pid}`); const debug = Debug(`lot-occupancy-system:functions.cache:${process.pid}`);
/*
* Lot Occupant Types
*/
let lotOccupantTypes; let lotOccupantTypes;
export async function getLotOccupantTypes() { export async function getLotOccupantTypes() {
if (lotOccupantTypes === undefined) { if (lotOccupantTypes === undefined) {
@ -33,6 +36,9 @@ export async function getLotOccupantTypeByLotOccupantType(lotOccupantType) {
function clearLotOccupantTypesCache() { function clearLotOccupantTypesCache() {
lotOccupantTypes = undefined; lotOccupantTypes = undefined;
} }
/*
* Lot Statuses
*/
let lotStatuses; let lotStatuses;
export async function getLotStatuses() { export async function getLotStatuses() {
if (lotStatuses === undefined) { if (lotStatuses === undefined) {
@ -56,6 +62,9 @@ export async function getLotStatusByLotStatus(lotStatus) {
function clearLotStatusesCache() { function clearLotStatusesCache() {
lotStatuses = undefined; lotStatuses = undefined;
} }
/*
* Lot Types
*/
let lotTypes; let lotTypes;
export async function getLotTypes() { export async function getLotTypes() {
if (lotTypes === undefined) { if (lotTypes === undefined) {
@ -79,6 +88,9 @@ export async function getLotTypesByLotType(lotType) {
function clearLotTypesCache() { function clearLotTypesCache() {
lotTypes = undefined; lotTypes = undefined;
} }
/*
* Occupancy Types
*/
let occupancyTypes; let occupancyTypes;
let allOccupancyTypeFields; let allOccupancyTypeFields;
export async function getOccupancyTypes() { export async function getOccupancyTypes() {
@ -122,6 +134,9 @@ function clearOccupancyTypesCache() {
occupancyTypes = undefined; occupancyTypes = undefined;
allOccupancyTypeFields = undefined; allOccupancyTypeFields = undefined;
} }
/*
* Work Order Types
*/
let workOrderTypes; let workOrderTypes;
export async function getWorkOrderTypes() { export async function getWorkOrderTypes() {
if (workOrderTypes === undefined) { if (workOrderTypes === undefined) {
@ -138,6 +153,9 @@ export async function getWorkOrderTypeById(workOrderTypeId) {
function clearWorkOrderTypesCache() { function clearWorkOrderTypesCache() {
workOrderTypes = undefined; workOrderTypes = undefined;
} }
/*
* Work Order Milestone Types
*/
let workOrderMilestoneTypes; let workOrderMilestoneTypes;
export async function getWorkOrderMilestoneTypes() { export async function getWorkOrderMilestoneTypes() {
if (workOrderMilestoneTypes === undefined) { if (workOrderMilestoneTypes === undefined) {

View File

@ -1,3 +1,4 @@
/* eslint-disable unicorn/filename-case, @eslint-community/eslint-comments/disable-enable-pair */
import { DynamicsGP } from '@cityssm/dynamics-gp'; import { DynamicsGP } from '@cityssm/dynamics-gp';
import { getConfigProperty } from './functions.config.js'; import { getConfigProperty } from './functions.config.js';
let gp; let gp;

View File

@ -5,7 +5,7 @@ import getNextLotIdFromDatabase from '../database/getNextLotId.js';
import getPreviousLotIdFromDatabase from '../database/getPreviousLotId.js'; import getPreviousLotIdFromDatabase from '../database/getPreviousLotId.js';
const debug = Debug(`lot-occupancy-system:functions.lots:${process.pid}`); const debug = Debug(`lot-occupancy-system:functions.lots:${process.pid}`);
const cacheOptions = { const cacheOptions = {
stdTTL: 2 * 60, stdTTL: 2 * 60, // two minutes
useClones: false useClones: false
}; };
const previousLotIdCache = new NodeCache(cacheOptions); const previousLotIdCache = new NodeCache(cacheOptions);

View File

@ -1,8 +1,11 @@
// skipcq: JS-C1003 - Added to ReportData
import * as dateTimeFunctions from '@cityssm/utils-datetime'; import * as dateTimeFunctions from '@cityssm/utils-datetime';
import getLot from '../database/getLot.js'; import getLot from '../database/getLot.js';
import getLotOccupancy from '../database/getLotOccupancy.js'; import getLotOccupancy from '../database/getLotOccupancy.js';
import getWorkOrder from '../database/getWorkOrder.js'; import getWorkOrder from '../database/getWorkOrder.js';
// skipcq: JS-C1003 - Added to ReportData
import * as configFunctions from './functions.config.js'; import * as configFunctions from './functions.config.js';
// skipcq: JS-C1003 - Added to ReportData
import * as lotOccupancyFunctions from './functions.lotOccupancy.js'; import * as lotOccupancyFunctions from './functions.lotOccupancy.js';
const screenPrintConfigs = { const screenPrintConfigs = {
lotOccupancy: { lotOccupancy: {
@ -22,6 +25,7 @@ const pdfPrintConfigs = {
title: 'Work Order Field Sheet - Comment Log', title: 'Work Order Field Sheet - Comment Log',
params: ['workOrderId'] params: ['workOrderId']
}, },
// Occupancy
'ssm.cemetery.burialPermit': { 'ssm.cemetery.burialPermit': {
title: 'Burial Permit', title: 'Burial Permit',
params: ['lotOccupancyId'] params: ['lotOccupancyId']

View File

@ -14,6 +14,9 @@ const user = {
} }
}; };
export async function initializeCemeteryDatabase() { export async function initializeCemeteryDatabase() {
/*
* Ensure database does not already exist
*/
debug(`Checking for ${databasePath}...`); debug(`Checking for ${databasePath}...`);
const databaseInitialized = initializeDatabase(); const databaseInitialized = initializeDatabase();
if (!databaseInitialized) { if (!databaseInitialized) {
@ -22,15 +25,24 @@ export async function initializeCemeteryDatabase() {
return false; return false;
} }
debug('New database file created. Proceeding with initialization.'); debug('New database file created. Proceeding with initialization.');
/*
* Lot Types
*/
await addRecord('LotTypes', 'Casket Grave', 1, user); await addRecord('LotTypes', 'Casket Grave', 1, user);
await addRecord('LotTypes', 'Columbarium', 2, user); await addRecord('LotTypes', 'Columbarium', 2, user);
await addRecord('LotTypes', 'Mausoleum', 2, user); await addRecord('LotTypes', 'Mausoleum', 2, user);
await addRecord('LotTypes', 'Niche Wall', 2, user); await addRecord('LotTypes', 'Niche Wall', 2, user);
await addRecord('LotTypes', 'Urn Garden', 2, user); await addRecord('LotTypes', 'Urn Garden', 2, user);
await addRecord('LotTypes', 'Crematorium', 2, user); await addRecord('LotTypes', 'Crematorium', 2, user);
/*
* Lot Statuses
*/
await addRecord('LotStatuses', 'Available', 1, user); await addRecord('LotStatuses', 'Available', 1, user);
await addRecord('LotStatuses', 'Reserved', 2, user); await addRecord('LotStatuses', 'Reserved', 2, user);
await addRecord('LotStatuses', 'Taken', 3, user); await addRecord('LotStatuses', 'Taken', 3, user);
/*
* Lot Occupant Types
*/
await addLotOccupantType({ await addLotOccupantType({
lotOccupantType: 'Deceased', lotOccupantType: 'Deceased',
fontAwesomeIconClass: 'cross', fontAwesomeIconClass: 'cross',
@ -52,9 +64,13 @@ export async function initializeCemeteryDatabase() {
occupantCommentTitle: 'Relationship to Owner/Deceased', occupantCommentTitle: 'Relationship to Owner/Deceased',
orderNumber: 4 orderNumber: 4
}, user); }, user);
/*
* Occupancy Types
*/
await addRecord('OccupancyTypes', 'Preneed', 1, user); await addRecord('OccupancyTypes', 'Preneed', 1, user);
const intermentOccupancyTypeId = await addRecord('OccupancyTypes', 'Interment', 2, user); const intermentOccupancyTypeId = await addRecord('OccupancyTypes', 'Interment', 2, user);
const cremationOccupancyTypeId = await addRecord('OccupancyTypes', 'Cremation', 3, user); const cremationOccupancyTypeId = await addRecord('OccupancyTypes', 'Cremation', 3, user);
// Death Date
const deathDateField = { const deathDateField = {
occupancyTypeId: intermentOccupancyTypeId, occupancyTypeId: intermentOccupancyTypeId,
occupancyTypeField: 'Death Date', occupancyTypeField: 'Death Date',
@ -69,6 +85,7 @@ export async function initializeCemeteryDatabase() {
await addOccupancyTypeField(Object.assign(deathDateField, { await addOccupancyTypeField(Object.assign(deathDateField, {
occupancyTypeId: cremationOccupancyTypeId occupancyTypeId: cremationOccupancyTypeId
}), user); }), user);
// Death Age
const deathAgeField = { const deathAgeField = {
occupancyTypeId: intermentOccupancyTypeId, occupancyTypeId: intermentOccupancyTypeId,
occupancyTypeField: 'Death Age', occupancyTypeField: 'Death Age',
@ -81,6 +98,7 @@ export async function initializeCemeteryDatabase() {
}; };
await addOccupancyTypeField(deathAgeField, user); await addOccupancyTypeField(deathAgeField, user);
await addOccupancyTypeField(Object.assign(deathAgeField, { occupancyTypeId: cremationOccupancyTypeId }), user); await addOccupancyTypeField(Object.assign(deathAgeField, { occupancyTypeId: cremationOccupancyTypeId }), user);
// Death Age Period
const deathAgePeriod = { const deathAgePeriod = {
occupancyTypeId: intermentOccupancyTypeId, occupancyTypeId: intermentOccupancyTypeId,
occupancyTypeField: 'Death Age Period', occupancyTypeField: 'Death Age Period',
@ -95,6 +113,7 @@ export async function initializeCemeteryDatabase() {
await addOccupancyTypeField(Object.assign(deathAgePeriod, { await addOccupancyTypeField(Object.assign(deathAgePeriod, {
occupancyTypeId: cremationOccupancyTypeId occupancyTypeId: cremationOccupancyTypeId
}), user); }), user);
// Death Place
const deathPlace = { const deathPlace = {
occupancyTypeId: intermentOccupancyTypeId, occupancyTypeId: intermentOccupancyTypeId,
occupancyTypeField: 'Death Place', occupancyTypeField: 'Death Place',
@ -107,6 +126,7 @@ export async function initializeCemeteryDatabase() {
}; };
await addOccupancyTypeField(deathPlace, user); await addOccupancyTypeField(deathPlace, user);
await addOccupancyTypeField(Object.assign(deathPlace, { occupancyTypeId: cremationOccupancyTypeId }), user); await addOccupancyTypeField(Object.assign(deathPlace, { occupancyTypeId: cremationOccupancyTypeId }), user);
// Funeral Home
const funeralHome = { const funeralHome = {
occupancyTypeId: intermentOccupancyTypeId, occupancyTypeId: intermentOccupancyTypeId,
occupancyTypeField: 'Funeral Home', occupancyTypeField: 'Funeral Home',
@ -119,6 +139,7 @@ export async function initializeCemeteryDatabase() {
}; };
await addOccupancyTypeField(funeralHome, user); await addOccupancyTypeField(funeralHome, user);
await addOccupancyTypeField(Object.assign(funeralHome, { occupancyTypeId: cremationOccupancyTypeId }), user); await addOccupancyTypeField(Object.assign(funeralHome, { occupancyTypeId: cremationOccupancyTypeId }), user);
// Funeral Date
const funeralDate = { const funeralDate = {
occupancyTypeId: intermentOccupancyTypeId, occupancyTypeId: intermentOccupancyTypeId,
occupancyTypeField: 'Funeral Date', occupancyTypeField: 'Funeral Date',
@ -131,6 +152,7 @@ export async function initializeCemeteryDatabase() {
}; };
await addOccupancyTypeField(funeralDate, user); await addOccupancyTypeField(funeralDate, user);
await addOccupancyTypeField(Object.assign(funeralDate, { occupancyTypeId: cremationOccupancyTypeId }), user); await addOccupancyTypeField(Object.assign(funeralDate, { occupancyTypeId: cremationOccupancyTypeId }), user);
// Container Type
const containerType = { const containerType = {
occupancyTypeId: intermentOccupancyTypeId, occupancyTypeId: intermentOccupancyTypeId,
occupancyTypeField: 'Container Type', occupancyTypeField: 'Container Type',
@ -143,6 +165,7 @@ export async function initializeCemeteryDatabase() {
}; };
await addOccupancyTypeField(containerType, user); await addOccupancyTypeField(containerType, user);
await addOccupancyTypeField(Object.assign(containerType, { occupancyTypeId: cremationOccupancyTypeId }), user); await addOccupancyTypeField(Object.assign(containerType, { occupancyTypeId: cremationOccupancyTypeId }), user);
// Committal Type
const committalType = { const committalType = {
occupancyTypeId: intermentOccupancyTypeId, occupancyTypeId: intermentOccupancyTypeId,
occupancyTypeField: 'Committal Type', occupancyTypeField: 'Committal Type',
@ -155,11 +178,17 @@ export async function initializeCemeteryDatabase() {
}; };
await addOccupancyTypeField(committalType, user); await addOccupancyTypeField(committalType, user);
await addOccupancyTypeField(Object.assign(committalType, { occupancyTypeId: cremationOccupancyTypeId }), user); await addOccupancyTypeField(Object.assign(committalType, { occupancyTypeId: cremationOccupancyTypeId }), user);
/*
* Fee Categories
*/
await addRecord('FeeCategories', 'Interment Rights', 1, user); await addRecord('FeeCategories', 'Interment Rights', 1, user);
await addRecord('FeeCategories', 'Cremation Services', 2, user); await addRecord('FeeCategories', 'Cremation Services', 2, user);
await addRecord('FeeCategories', 'Burial Charges', 3, user); await addRecord('FeeCategories', 'Burial Charges', 3, user);
await addRecord('FeeCategories', 'Disinterment of Human Remains', 4, user); await addRecord('FeeCategories', 'Disinterment of Human Remains', 4, user);
await addRecord('FeeCategories', 'Additional Services', 5, user); await addRecord('FeeCategories', 'Additional Services', 5, user);
/*
* Work Orders
*/
await addRecord('WorkOrderTypes', 'Cemetery Work Order', 1, user); await addRecord('WorkOrderTypes', 'Cemetery Work Order', 1, user);
await addRecord('WorkOrderMilestoneTypes', 'Funeral', 1, user); await addRecord('WorkOrderMilestoneTypes', 'Funeral', 1, user);
await addRecord('WorkOrderMilestoneTypes', 'Arrival', 2, user); await addRecord('WorkOrderMilestoneTypes', 'Arrival', 2, user);

View File

@ -9,20 +9,33 @@ const recordColumns = `recordCreate_userName varchar(30) not null,
recordDelete_userName varchar(30), recordDelete_userName varchar(30),
recordDelete_timeMillis integer`; recordDelete_timeMillis integer`;
const createStatements = [ const createStatements = [
/*
* Lot Types
*/
`create table if not exists LotTypes (lotTypeId integer not null primary key autoincrement, lotType varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns})`, `create table if not exists LotTypes (lotTypeId integer not null primary key autoincrement, lotType varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns})`,
'create index if not exists idx_lottypes_ordernumber on LotTypes (orderNumber, lotType)', 'create index if not exists idx_lottypes_ordernumber on LotTypes (orderNumber, lotType)',
`create table if not exists LotTypeFields (lotTypeFieldId integer not null primary key autoincrement, lotTypeId integer not null, lotTypeField varchar(100) not null, lotTypeFieldValues text, isRequired bit not null default 0, pattern varchar(100), minimumLength smallint not null default 1 check (minimumLength >= 0), maximumLength smallint not null default 100 check (maximumLength >= 0), orderNumber smallint not null default 0, ${recordColumns}, foreign key (lotTypeId) references LotTypes (lotTypeId))`, `create table if not exists LotTypeFields (lotTypeFieldId integer not null primary key autoincrement, lotTypeId integer not null, lotTypeField varchar(100) not null, lotTypeFieldValues text, isRequired bit not null default 0, pattern varchar(100), minimumLength smallint not null default 1 check (minimumLength >= 0), maximumLength smallint not null default 100 check (maximumLength >= 0), orderNumber smallint not null default 0, ${recordColumns}, foreign key (lotTypeId) references LotTypes (lotTypeId))`,
'create index if not exists idx_lottypefields_ordernumber on LotTypeFields (lotTypeId, orderNumber, lotTypeField)', 'create index if not exists idx_lottypefields_ordernumber on LotTypeFields (lotTypeId, orderNumber, lotTypeField)',
/*
* Lot Statuses
*/
`create table if not exists LotStatuses (lotStatusId integer not null primary key autoincrement, lotStatus varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns})`, `create table if not exists LotStatuses (lotStatusId integer not null primary key autoincrement, lotStatus varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns})`,
'create index if not exists idx_lotstatuses_ordernumber on LotStatuses (orderNumber, lotStatus)', 'create index if not exists idx_lotstatuses_ordernumber on LotStatuses (orderNumber, lotStatus)',
/*
* Maps and Lots
*/
`create table if not exists Maps (mapId integer not null primary key autoincrement, mapName varchar(200) not null, mapDescription text, mapLatitude decimal(10, 8) check (mapLatitude between -90 and 90), mapLongitude decimal(11, 8) check (mapLongitude between -180 and 180), mapSVG varchar(50), mapAddress1 varchar(50), mapAddress2 varchar(50), mapCity varchar(20), mapProvince varchar(2), mapPostalCode varchar(7), mapPhoneNumber varchar(30), ${recordColumns})`, `create table if not exists Maps (mapId integer not null primary key autoincrement, mapName varchar(200) not null, mapDescription text, mapLatitude decimal(10, 8) check (mapLatitude between -90 and 90), mapLongitude decimal(11, 8) check (mapLongitude between -180 and 180), mapSVG varchar(50), mapAddress1 varchar(50), mapAddress2 varchar(50), mapCity varchar(20), mapProvince varchar(2), mapPostalCode varchar(7), mapPhoneNumber varchar(30), ${recordColumns})`,
`create table if not exists Lots (lotId integer not null primary key autoincrement, lotTypeId integer not null, lotName varchar(100), mapId integer, mapKey varchar(100), lotLatitude decimal(10, 8) check (lotLatitude between -90 and 90), lotLongitude decimal(11, 8) check (lotLongitude between -180 and 180), lotStatusId integer, ${recordColumns}, foreign key (lotTypeId) references LotTypes (lotTypeId), foreign key (mapId) references Maps (mapId), foreign key (lotStatusId) references LotStatuses (lotStatusId))`, `create table if not exists Lots (lotId integer not null primary key autoincrement, lotTypeId integer not null, lotName varchar(100), mapId integer, mapKey varchar(100), lotLatitude decimal(10, 8) check (lotLatitude between -90 and 90), lotLongitude decimal(11, 8) check (lotLongitude between -180 and 180), lotStatusId integer, ${recordColumns}, foreign key (lotTypeId) references LotTypes (lotTypeId), foreign key (mapId) references Maps (mapId), foreign key (lotStatusId) references LotStatuses (lotStatusId))`,
`create table if not exists LotFields (lotId integer not null, lotTypeFieldId integer not null, lotFieldValue text not null, ${recordColumns}, primary key (lotId, lotTypeFieldId), foreign key (lotId) references Lots (lotId), foreign key (lotTypeFieldId) references LotTypeFields (lotTypeFieldId)) without rowid`, `create table if not exists LotFields (lotId integer not null, lotTypeFieldId integer not null, lotFieldValue text not null, ${recordColumns}, primary key (lotId, lotTypeFieldId), foreign key (lotId) references Lots (lotId), foreign key (lotTypeFieldId) references LotTypeFields (lotTypeFieldId)) without rowid`,
`create table if not exists LotComments (lotCommentId integer not null primary key autoincrement, lotId integer not null, lotCommentDate integer not null check (lotCommentDate > 0), lotCommentTime integer not null check (lotCommentTime >= 0), lotComment text not null, ${recordColumns}, foreign key (lotId) references Lots (lotId))`, `create table if not exists LotComments (lotCommentId integer not null primary key autoincrement, lotId integer not null, lotCommentDate integer not null check (lotCommentDate > 0), lotCommentTime integer not null check (lotCommentTime >= 0), lotComment text not null, ${recordColumns}, foreign key (lotId) references Lots (lotId))`,
'create index if not exists idx_lotcomments_datetime on LotComments (lotId, lotCommentDate, lotCommentTime)', 'create index if not exists idx_lotcomments_datetime on LotComments (lotId, lotCommentDate, lotCommentTime)',
/*
* Occupancies
*/
`create table if not exists OccupancyTypes (occupancyTypeId integer not null primary key autoincrement, occupancyType varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns})`, `create table if not exists OccupancyTypes (occupancyTypeId integer not null primary key autoincrement, occupancyType varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns})`,
'create index if not exists idx_occupancytypes_ordernumber on OccupancyTypes (orderNumber, occupancyType)', 'create index if not exists idx_occupancytypes_ordernumber on OccupancyTypes (orderNumber, occupancyType)',
`create table if not exists OccupancyTypeFields (occupancyTypeFieldId integer not null primary key autoincrement, occupancyTypeId integer, occupancyTypeField varchar(100) not null, occupancyTypeFieldValues text, isRequired bit not null default 0, pattern varchar(100), minimumLength smallint not null default 1 check (minimumLength >= 0), maximumLength smallint not null default 100 check (maximumLength >= 0), orderNumber smallint not null default 0, ${recordColumns}, foreign key (occupancyTypeId) references OccupancyTypes (occupancyTypeId))`, `create table if not exists OccupancyTypeFields (occupancyTypeFieldId integer not null primary key autoincrement, occupancyTypeId integer, occupancyTypeField varchar(100) not null, occupancyTypeFieldValues text, isRequired bit not null default 0, pattern varchar(100), minimumLength smallint not null default 1 check (minimumLength >= 0), maximumLength smallint not null default 100 check (maximumLength >= 0), orderNumber smallint not null default 0, ${recordColumns}, foreign key (occupancyTypeId) references OccupancyTypes (occupancyTypeId))`,
// eslint-disable-next-line no-secrets/no-secrets
'create index if not exists idx_occupancytypefields_ordernumber on OccupancyTypeFields (occupancyTypeId, orderNumber, occupancyTypeField)', 'create index if not exists idx_occupancytypefields_ordernumber on OccupancyTypeFields (occupancyTypeId, orderNumber, occupancyTypeField)',
`create table if not exists OccupancyTypePrints (occupancyTypeId integer not null, printEJS varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns}, primary key (occupancyTypeId, printEJS), foreign key (occupancyTypeId) references OccupancyTypes (occupancyTypeId))`, `create table if not exists OccupancyTypePrints (occupancyTypeId integer not null, printEJS varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns}, primary key (occupancyTypeId, printEJS), foreign key (occupancyTypeId) references OccupancyTypes (occupancyTypeId))`,
'create index if not exists idx_occupancytypeprints_ordernumber on OccupancyTypePrints (occupancyTypeId, orderNumber, printEJS)', 'create index if not exists idx_occupancytypeprints_ordernumber on OccupancyTypePrints (occupancyTypeId, orderNumber, printEJS)',
@ -33,12 +46,16 @@ const createStatements = [
occupantCommentTitle varchar(50) not null default '', occupantCommentTitle varchar(50) not null default '',
orderNumber smallint not null default 0, orderNumber smallint not null default 0,
${recordColumns})`, ${recordColumns})`,
// eslint-disable-next-line no-secrets/no-secrets
'create index if not exists idx_lotoccupanttypes_ordernumber on LotOccupantTypes (orderNumber, lotOccupantType)', 'create index if not exists idx_lotoccupanttypes_ordernumber on LotOccupantTypes (orderNumber, lotOccupantType)',
`create table if not exists LotOccupancies (lotOccupancyId integer not null primary key autoincrement, occupancyTypeId integer not null, lotId integer, occupancyStartDate integer not null check (occupancyStartDate > 0), occupancyEndDate integer check (occupancyEndDate > 0), ${recordColumns}, foreign key (lotId) references Lots (lotId), foreign key (occupancyTypeId) references OccupancyTypes (occupancyTypeId))`, `create table if not exists LotOccupancies (lotOccupancyId integer not null primary key autoincrement, occupancyTypeId integer not null, lotId integer, occupancyStartDate integer not null check (occupancyStartDate > 0), occupancyEndDate integer check (occupancyEndDate > 0), ${recordColumns}, foreign key (lotId) references Lots (lotId), foreign key (occupancyTypeId) references OccupancyTypes (occupancyTypeId))`,
`create table if not exists LotOccupancyOccupants (lotOccupancyId integer not null, lotOccupantIndex integer not null, occupantName varchar(200) not null, occupantAddress1 varchar(50), occupantAddress2 varchar(50), occupantCity varchar(20), occupantProvince varchar(2), occupantPostalCode varchar(7), occupantPhoneNumber varchar(30), occupantEmailAddress varchar(200), lotOccupantTypeId integer not null, occupantComment text not null default '', ${recordColumns}, primary key (lotOccupancyId, lotOccupantIndex), foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId), foreign key (lotOccupantTypeId) references LotOccupantTypes (lotOccupantTypeId)) without rowid`, `create table if not exists LotOccupancyOccupants (lotOccupancyId integer not null, lotOccupantIndex integer not null, occupantName varchar(200) not null, occupantAddress1 varchar(50), occupantAddress2 varchar(50), occupantCity varchar(20), occupantProvince varchar(2), occupantPostalCode varchar(7), occupantPhoneNumber varchar(30), occupantEmailAddress varchar(200), lotOccupantTypeId integer not null, occupantComment text not null default '', ${recordColumns}, primary key (lotOccupancyId, lotOccupantIndex), foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId), foreign key (lotOccupantTypeId) references LotOccupantTypes (lotOccupantTypeId)) without rowid`,
`create table if not exists LotOccupancyFields (lotOccupancyId integer not null, occupancyTypeFieldId integer not null, lotOccupancyFieldValue text not null, ${recordColumns}, primary key (lotOccupancyId, occupancyTypeFieldId), foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId), foreign key (occupancyTypeFieldId) references OccupancyTypeFields (occupancyTypeFieldId)) without rowid`, `create table if not exists LotOccupancyFields (lotOccupancyId integer not null, occupancyTypeFieldId integer not null, lotOccupancyFieldValue text not null, ${recordColumns}, primary key (lotOccupancyId, occupancyTypeFieldId), foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId), foreign key (occupancyTypeFieldId) references OccupancyTypeFields (occupancyTypeFieldId)) without rowid`,
`create table if not exists LotOccupancyComments (lotOccupancyCommentId integer not null primary key autoincrement, lotOccupancyId integer not null, lotOccupancyCommentDate integer not null check (lotOccupancyCommentDate > 0), lotOccupancyCommentTime integer not null check (lotOccupancyCommentTime >= 0), lotOccupancyComment text not null, ${recordColumns}, foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId))`, `create table if not exists LotOccupancyComments (lotOccupancyCommentId integer not null primary key autoincrement, lotOccupancyId integer not null, lotOccupancyCommentDate integer not null check (lotOccupancyCommentDate > 0), lotOccupancyCommentTime integer not null check (lotOccupancyCommentTime >= 0), lotOccupancyComment text not null, ${recordColumns}, foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId))`,
'create index if not exists idx_lotoccupancycomments_datetime on LotOccupancyComments (lotOccupancyId, lotOccupancyCommentDate, lotOccupancyCommentTime)', 'create index if not exists idx_lotoccupancycomments_datetime on LotOccupancyComments (lotOccupancyId, lotOccupancyCommentDate, lotOccupancyCommentTime)',
/*
* Fees and Transactions
*/
`create table if not exists FeeCategories (feeCategoryId integer not null primary key autoincrement, feeCategory varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns})`, `create table if not exists FeeCategories (feeCategoryId integer not null primary key autoincrement, feeCategory varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns})`,
`create table if not exists Fees ( `create table if not exists Fees (
feeId integer not null primary key autoincrement, feeId integer not null primary key autoincrement,
@ -64,6 +81,9 @@ const createStatements = [
`create table if not exists LotOccupancyFees (lotOccupancyId integer not null, feeId integer not null, quantity decimal(4, 1) not null default 1, feeAmount decimal(8, 2) not null, taxAmount decmial(8, 2) not null, ${recordColumns}, primary key (lotOccupancyId, feeId), foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId), foreign key (feeId) references Fees (feeId)) without rowid`, `create table if not exists LotOccupancyFees (lotOccupancyId integer not null, feeId integer not null, quantity decimal(4, 1) not null default 1, feeAmount decimal(8, 2) not null, taxAmount decmial(8, 2) not null, ${recordColumns}, primary key (lotOccupancyId, feeId), foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId), foreign key (feeId) references Fees (feeId)) without rowid`,
`create table if not exists LotOccupancyTransactions (lotOccupancyId integer not null, transactionIndex integer not null, transactionDate integer not null check (transactionDate > 0), transactionTime integer not null check (transactionTime >= 0), transactionAmount decimal(8, 2) not null, externalReceiptNumber varchar(100), transactionNote text, ${recordColumns}, primary key (lotOccupancyId, transactionIndex), foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId)) without rowid`, `create table if not exists LotOccupancyTransactions (lotOccupancyId integer not null, transactionIndex integer not null, transactionDate integer not null check (transactionDate > 0), transactionTime integer not null check (transactionTime >= 0), transactionAmount decimal(8, 2) not null, externalReceiptNumber varchar(100), transactionNote text, ${recordColumns}, primary key (lotOccupancyId, transactionIndex), foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId)) without rowid`,
'create index if not exists idx_lotoccupancytransactions_ordernumber on LotOccupancyTransactions (lotOccupancyId, transactionDate, transactionTime)', 'create index if not exists idx_lotoccupancytransactions_ordernumber on LotOccupancyTransactions (lotOccupancyId, transactionDate, transactionTime)',
/*
* Work Orders
*/
`create table if not exists WorkOrderTypes (workOrderTypeId integer not null primary key autoincrement, workOrderType varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns})`, `create table if not exists WorkOrderTypes (workOrderTypeId integer not null primary key autoincrement, workOrderType varchar(100) not null, orderNumber smallint not null default 0, ${recordColumns})`,
'create index if not exists idx_workordertypes_ordernumber on WorkOrderTypes (orderNumber, workOrderType)', 'create index if not exists idx_workordertypes_ordernumber on WorkOrderTypes (orderNumber, workOrderType)',
`create table if not exists WorkOrders (workOrderId integer not null primary key autoincrement, workOrderTypeId integer not null, workOrderNumber varchar(50) not null, workOrderDescription text, workOrderOpenDate integer check (workOrderOpenDate > 0), workOrderCloseDate integer check (workOrderCloseDate > 0), ${recordColumns}, foreign key (workOrderTypeId) references WorkOrderTypes (workOrderTypeId))`, `create table if not exists WorkOrders (workOrderId integer not null primary key autoincrement, workOrderTypeId integer not null, workOrderNumber varchar(50) not null, workOrderDescription text, workOrderOpenDate integer check (workOrderOpenDate > 0), workOrderCloseDate integer check (workOrderCloseDate > 0), ${recordColumns}, foreign key (workOrderTypeId) references WorkOrderTypes (workOrderTypeId))`,

View File

@ -61,7 +61,11 @@ import handler_doUpdateOccupancyType from '../handlers/admin-post/doUpdateOccupa
import handler_doUpdateOccupancyTypeField from '../handlers/admin-post/doUpdateOccupancyTypeField.js'; import handler_doUpdateOccupancyTypeField from '../handlers/admin-post/doUpdateOccupancyTypeField.js';
import handler_doUpdateWorkOrderMilestoneType from '../handlers/admin-post/doUpdateWorkOrderMilestoneType.js'; import handler_doUpdateWorkOrderMilestoneType from '../handlers/admin-post/doUpdateWorkOrderMilestoneType.js';
import handler_doUpdateWorkOrderType from '../handlers/admin-post/doUpdateWorkOrderType.js'; import handler_doUpdateWorkOrderType from '../handlers/admin-post/doUpdateWorkOrderType.js';
// Ntfy Startup
export const router = Router(); export const router = Router();
/*
* Fees
*/
router.get('/fees', handler_fees); router.get('/fees', handler_fees);
router.post('/doAddFeeCategory', handler_doAddFeeCategory); router.post('/doAddFeeCategory', handler_doAddFeeCategory);
router.post('/doUpdateFeeCategory', handler_doUpdateFeeCategory); router.post('/doUpdateFeeCategory', handler_doUpdateFeeCategory);
@ -73,55 +77,73 @@ router.post('/doUpdateFee', handler_doUpdateFee);
router.post('/doMoveFeeUp', handler_doMoveFeeUp); router.post('/doMoveFeeUp', handler_doMoveFeeUp);
router.post('/doMoveFeeDown', handler_doMoveFeeDown); router.post('/doMoveFeeDown', handler_doMoveFeeDown);
router.post('/doDeleteFee', handler_doDeleteFee); router.post('/doDeleteFee', handler_doDeleteFee);
/*
* Occupancy Type Management
*/
router.get('/occupancyTypes', handler_occupancyTypes); router.get('/occupancyTypes', handler_occupancyTypes);
router.post('/doAddOccupancyType', handler_doAddOccupancyType); router.post('/doAddOccupancyType', handler_doAddOccupancyType);
router.post('/doUpdateOccupancyType', handler_doUpdateOccupancyType); router.post('/doUpdateOccupancyType', handler_doUpdateOccupancyType);
router.post('/doMoveOccupancyTypeUp', handler_doMoveOccupancyTypeUp); router.post('/doMoveOccupancyTypeUp', handler_doMoveOccupancyTypeUp);
router.post('/doMoveOccupancyTypeDown', handler_doMoveOccupancyTypeDown); router.post('/doMoveOccupancyTypeDown', handler_doMoveOccupancyTypeDown);
router.post('/doDeleteOccupancyType', handler_doDeleteOccupancyType); router.post('/doDeleteOccupancyType', handler_doDeleteOccupancyType);
// Occupancy Type Fields
router.post('/doAddOccupancyTypeField', handler_doAddOccupancyTypeField); router.post('/doAddOccupancyTypeField', handler_doAddOccupancyTypeField);
router.post('/doUpdateOccupancyTypeField', handler_doUpdateOccupancyTypeField); router.post('/doUpdateOccupancyTypeField', handler_doUpdateOccupancyTypeField);
router.post('/doMoveOccupancyTypeFieldUp', handler_doMoveOccupancyTypeFieldUp); router.post('/doMoveOccupancyTypeFieldUp', handler_doMoveOccupancyTypeFieldUp);
router.post('/doMoveOccupancyTypeFieldDown', handler_doMoveOccupancyTypeFieldDown); router.post('/doMoveOccupancyTypeFieldDown', handler_doMoveOccupancyTypeFieldDown);
router.post('/doDeleteOccupancyTypeField', handler_doDeleteOccupancyTypeField); router.post('/doDeleteOccupancyTypeField', handler_doDeleteOccupancyTypeField);
// Occupancy Type Prints
router.post('/doAddOccupancyTypePrint', handler_doAddOccupancyTypePrint); router.post('/doAddOccupancyTypePrint', handler_doAddOccupancyTypePrint);
router.post('/doMoveOccupancyTypePrintUp', handler_doMoveOccupancyTypePrintUp); router.post('/doMoveOccupancyTypePrintUp', handler_doMoveOccupancyTypePrintUp);
router.post('/doMoveOccupancyTypePrintDown', handler_doMoveOccupancyTypePrintDown); router.post('/doMoveOccupancyTypePrintDown', handler_doMoveOccupancyTypePrintDown);
router.post('/doDeleteOccupancyTypePrint', handler_doDeleteOccupancyTypePrint); router.post('/doDeleteOccupancyTypePrint', handler_doDeleteOccupancyTypePrint);
/*
* Lot Type Management
*/
router.get('/lotTypes', handler_lotTypes); router.get('/lotTypes', handler_lotTypes);
router.post('/doAddLotType', handler_doAddLotType); router.post('/doAddLotType', handler_doAddLotType);
router.post('/doUpdateLotType', handler_doUpdateLotType); router.post('/doUpdateLotType', handler_doUpdateLotType);
router.post('/doMoveLotTypeUp', handler_doMoveLotTypeUp); router.post('/doMoveLotTypeUp', handler_doMoveLotTypeUp);
router.post('/doMoveLotTypeDown', handler_doMoveLotTypeDown); router.post('/doMoveLotTypeDown', handler_doMoveLotTypeDown);
router.post('/doDeleteLotType', handler_doDeleteLotType); router.post('/doDeleteLotType', handler_doDeleteLotType);
// Lot Type Fields
router.post('/doAddLotTypeField', handler_doAddLotTypeField); router.post('/doAddLotTypeField', handler_doAddLotTypeField);
router.post('/doUpdateLotTypeField', handler_doUpdateLotTypeField); router.post('/doUpdateLotTypeField', handler_doUpdateLotTypeField);
router.post('/doMoveLotTypeFieldUp', handler_doMoveLotTypeFieldUp); router.post('/doMoveLotTypeFieldUp', handler_doMoveLotTypeFieldUp);
router.post('/doMoveLotTypeFieldDown', handler_doMoveLotTypeFieldDown); router.post('/doMoveLotTypeFieldDown', handler_doMoveLotTypeFieldDown);
router.post('/doDeleteLotTypeField', handler_doDeleteLotTypeField); router.post('/doDeleteLotTypeField', handler_doDeleteLotTypeField);
/*
* Config Tables
*/
router.get('/tables', handler_tables); router.get('/tables', handler_tables);
// Config Tables - Work Order Types
router.post('/doAddWorkOrderType', handler_doAddWorkOrderType); router.post('/doAddWorkOrderType', handler_doAddWorkOrderType);
router.post('/doUpdateWorkOrderType', handler_doUpdateWorkOrderType); router.post('/doUpdateWorkOrderType', handler_doUpdateWorkOrderType);
router.post('/doMoveWorkOrderTypeUp', handler_doMoveWorkOrderTypeUp); router.post('/doMoveWorkOrderTypeUp', handler_doMoveWorkOrderTypeUp);
router.post('/doMoveWorkOrderTypeDown', handler_doMoveWorkOrderTypeDown); router.post('/doMoveWorkOrderTypeDown', handler_doMoveWorkOrderTypeDown);
router.post('/doDeleteWorkOrderType', handler_doDeleteWorkOrderType); router.post('/doDeleteWorkOrderType', handler_doDeleteWorkOrderType);
// Config Tables - Work Order Milestone Types
router.post('/doAddWorkOrderMilestoneType', handler_doAddWorkOrderMilestoneType); router.post('/doAddWorkOrderMilestoneType', handler_doAddWorkOrderMilestoneType);
router.post('/doUpdateWorkOrderMilestoneType', handler_doUpdateWorkOrderMilestoneType); router.post('/doUpdateWorkOrderMilestoneType', handler_doUpdateWorkOrderMilestoneType);
router.post('/doMoveWorkOrderMilestoneTypeUp', handler_doMoveWorkOrderMilestoneTypeUp); router.post('/doMoveWorkOrderMilestoneTypeUp', handler_doMoveWorkOrderMilestoneTypeUp);
router.post('/doMoveWorkOrderMilestoneTypeDown', handler_doMoveWorkOrderMilestoneTypeDown); router.post('/doMoveWorkOrderMilestoneTypeDown', handler_doMoveWorkOrderMilestoneTypeDown);
router.post('/doDeleteWorkOrderMilestoneType', handler_doDeleteWorkOrderMilestoneType); router.post('/doDeleteWorkOrderMilestoneType', handler_doDeleteWorkOrderMilestoneType);
// Config Tables - Lot Statuses
router.post('/doAddLotStatus', handler_doAddLotStatus); router.post('/doAddLotStatus', handler_doAddLotStatus);
router.post('/doUpdateLotStatus', handler_doUpdateLotStatus); router.post('/doUpdateLotStatus', handler_doUpdateLotStatus);
router.post('/doMoveLotStatusUp', handler_doMoveLotStatusUp); router.post('/doMoveLotStatusUp', handler_doMoveLotStatusUp);
router.post('/doMoveLotStatusDown', handler_doMoveLotStatusDown); router.post('/doMoveLotStatusDown', handler_doMoveLotStatusDown);
router.post('/doDeleteLotStatus', handler_doDeleteLotStatus); router.post('/doDeleteLotStatus', handler_doDeleteLotStatus);
// Config Tables - Lot Occupant Types
router.post('/doAddLotOccupantType', handler_doAddLotOccupantType); router.post('/doAddLotOccupantType', handler_doAddLotOccupantType);
router.post('/doUpdateLotOccupantType', handler_doUpdateLotOccupantType); router.post('/doUpdateLotOccupantType', handler_doUpdateLotOccupantType);
router.post('/doMoveLotOccupantTypeUp', handler_doMoveLotOccupantTypeUp); router.post('/doMoveLotOccupantTypeUp', handler_doMoveLotOccupantTypeUp);
router.post('/doMoveLotOccupantTypeDown', handler_doMoveLotOccupantTypeDown); router.post('/doMoveLotOccupantTypeDown', handler_doMoveLotOccupantTypeDown);
router.post('/doDeleteLotOccupantType', handler_doDeleteLotOccupantType); router.post('/doDeleteLotOccupantType', handler_doDeleteLotOccupantType);
// Database Maintenance
router.get('/database', handler_database); router.get('/database', handler_database);
router.post('/doBackupDatabase', handler_doBackupDatabase); router.post('/doBackupDatabase', handler_doBackupDatabase);
router.post('/doCleanupDatabase', handler_doCleanupDatabase); router.post('/doCleanupDatabase', handler_doCleanupDatabase);
// Ntfy Startup
router.get('/ntfyStartup', handler_ntfyStartup); router.get('/ntfyStartup', handler_ntfyStartup);
export default router; export default router;

View File

@ -27,27 +27,35 @@ import handler_doUpdateLotOccupancyTransaction from '../handlers/lotOccupancies-
import { updateGetHandler, updatePostHandler } from '../handlers/permissions.js'; import { updateGetHandler, updatePostHandler } from '../handlers/permissions.js';
import { getConfigProperty } from '../helpers/functions.config.js'; import { getConfigProperty } from '../helpers/functions.config.js';
export const router = Router(); export const router = Router();
// Search
router.get('/', handler_search); router.get('/', handler_search);
router.post('/doSearchLotOccupancies', handler_doSearchLotOccupancies); router.post('/doSearchLotOccupancies', handler_doSearchLotOccupancies);
// Create
router.get('/new', updateGetHandler, handler_new); router.get('/new', updateGetHandler, handler_new);
router.post('/doGetOccupancyTypeFields', updatePostHandler, handler_doGetOccupancyTypeFields); router.post('/doGetOccupancyTypeFields', updatePostHandler, handler_doGetOccupancyTypeFields);
router.post('/doCreateLotOccupancy', updatePostHandler, handler_doCreateLotOccupancy); router.post('/doCreateLotOccupancy', updatePostHandler, handler_doCreateLotOccupancy);
// View
router.get('/:lotOccupancyId', handler_view); router.get('/:lotOccupancyId', handler_view);
// Edit
router.get('/:lotOccupancyId/edit', updateGetHandler, handler_edit); router.get('/:lotOccupancyId/edit', updateGetHandler, handler_edit);
router.post('/doUpdateLotOccupancy', updatePostHandler, handler_doUpdateLotOccupancy); router.post('/doUpdateLotOccupancy', updatePostHandler, handler_doUpdateLotOccupancy);
router.post('/doCopyLotOccupancy', updatePostHandler, handler_doCopyLotOccupancy); router.post('/doCopyLotOccupancy', updatePostHandler, handler_doCopyLotOccupancy);
router.post('/doDeleteLotOccupancy', updatePostHandler, handler_doDeleteLotOccupancy); router.post('/doDeleteLotOccupancy', updatePostHandler, handler_doDeleteLotOccupancy);
// Occupants
router.post('/doSearchPastOccupants', updatePostHandler, handler_doSearchPastOccupants); router.post('/doSearchPastOccupants', updatePostHandler, handler_doSearchPastOccupants);
router.post('/doAddLotOccupancyOccupant', updatePostHandler, handler_doAddLotOccupancyOccupant); router.post('/doAddLotOccupancyOccupant', updatePostHandler, handler_doAddLotOccupancyOccupant);
router.post('/doUpdateLotOccupancyOccupant', updatePostHandler, handler_doUpdateLotOccupancyOccupant); router.post('/doUpdateLotOccupancyOccupant', updatePostHandler, handler_doUpdateLotOccupancyOccupant);
router.post('/doDeleteLotOccupancyOccupant', updatePostHandler, handler_doDeleteLotOccupancyOccupant); router.post('/doDeleteLotOccupancyOccupant', updatePostHandler, handler_doDeleteLotOccupancyOccupant);
// Comments
router.post('/doAddLotOccupancyComment', updatePostHandler, handler_doAddLotOccupancyComment); router.post('/doAddLotOccupancyComment', updatePostHandler, handler_doAddLotOccupancyComment);
router.post('/doUpdateLotOccupancyComment', updatePostHandler, handler_doUpdateLotOccupancyComment); router.post('/doUpdateLotOccupancyComment', updatePostHandler, handler_doUpdateLotOccupancyComment);
router.post('/doDeleteLotOccupancyComment', updatePostHandler, handler_doDeleteLotOccupancyComment); router.post('/doDeleteLotOccupancyComment', updatePostHandler, handler_doDeleteLotOccupancyComment);
// Fees
router.post('/doGetFees', updatePostHandler, handler_doGetFees); router.post('/doGetFees', updatePostHandler, handler_doGetFees);
router.post('/doAddLotOccupancyFee', updatePostHandler, handler_doAddLotOccupancyFee); router.post('/doAddLotOccupancyFee', updatePostHandler, handler_doAddLotOccupancyFee);
router.post('/doUpdateLotOccupancyFeeQuantity', updatePostHandler, handler_doUpdateLotOccupancyFeeQuantity); router.post('/doUpdateLotOccupancyFeeQuantity', updatePostHandler, handler_doUpdateLotOccupancyFeeQuantity);
router.post('/doDeleteLotOccupancyFee', updatePostHandler, handler_doDeleteLotOccupancyFee); router.post('/doDeleteLotOccupancyFee', updatePostHandler, handler_doDeleteLotOccupancyFee);
// Transactions
if (getConfigProperty('settings.dynamicsGP.integrationIsEnabled')) { if (getConfigProperty('settings.dynamicsGP.integrationIsEnabled')) {
router.post('/doGetDynamicsGPDocument', updatePostHandler, handler_doGetDynamicsGPDocument); router.post('/doGetDynamicsGPDocument', updatePostHandler, handler_doGetDynamicsGPDocument);
} }

View File

@ -15,8 +15,14 @@ import handler_doUpdateLot from '../handlers/lots-post/doUpdateLot.js';
import handler_doUpdateLotComment from '../handlers/lots-post/doUpdateLotComment.js'; import handler_doUpdateLotComment from '../handlers/lots-post/doUpdateLotComment.js';
import * as permissionHandlers from '../handlers/permissions.js'; import * as permissionHandlers from '../handlers/permissions.js';
export const router = Router(); export const router = Router();
/*
* Lot Search
*/
router.get('/', handler_search); router.get('/', handler_search);
router.post('/doSearchLots', handler_doSearchLots); router.post('/doSearchLots', handler_doSearchLots);
/*
* Lot View / Edit
*/
router.get('/new', permissionHandlers.updateGetHandler, handler_new); router.get('/new', permissionHandlers.updateGetHandler, handler_new);
router.get('/:lotId', handler_view); router.get('/:lotId', handler_view);
router.get('/:lotId/next', handler_next); router.get('/:lotId/next', handler_next);

View File

@ -27,27 +27,36 @@ import handler_doUpdateWorkOrder from '../handlers/workOrders-post/doUpdateWorkO
import handler_doUpdateWorkOrderComment from '../handlers/workOrders-post/doUpdateWorkOrderComment.js'; import handler_doUpdateWorkOrderComment from '../handlers/workOrders-post/doUpdateWorkOrderComment.js';
import handler_doUpdateWorkOrderMilestone from '../handlers/workOrders-post/doUpdateWorkOrderMilestone.js'; import handler_doUpdateWorkOrderMilestone from '../handlers/workOrders-post/doUpdateWorkOrderMilestone.js';
export const router = Router(); export const router = Router();
// Search
router.get('/', handler_search); router.get('/', handler_search);
router.post('/doSearchWorkOrders', handler_doSearchWorkOrders); router.post('/doSearchWorkOrders', handler_doSearchWorkOrders);
// Milestone Calendar
router.get('/milestoneCalendar', handler_milestoneCalendar); router.get('/milestoneCalendar', handler_milestoneCalendar);
router.post('/doGetWorkOrderMilestones', handler_doGetWorkOrderMilestones); router.post('/doGetWorkOrderMilestones', handler_doGetWorkOrderMilestones);
// Outlook Integration
router.get('/outlook', handler_outlook); router.get('/outlook', handler_outlook);
// New
router.get('/new', permissionHandlers.updateGetHandler, handler_new); router.get('/new', permissionHandlers.updateGetHandler, handler_new);
router.post('/doCreateWorkOrder', permissionHandlers.updatePostHandler, handler_doCreateWorkOrder); router.post('/doCreateWorkOrder', permissionHandlers.updatePostHandler, handler_doCreateWorkOrder);
// View
router.get('/:workOrderId', handler_view); router.get('/:workOrderId', handler_view);
router.post('/doReopenWorkOrder', permissionHandlers.updatePostHandler, handler_doReopenWorkOrder); router.post('/doReopenWorkOrder', permissionHandlers.updatePostHandler, handler_doReopenWorkOrder);
// Edit
router.get('/:workOrderId/edit', permissionHandlers.updateGetHandler, handler_edit); router.get('/:workOrderId/edit', permissionHandlers.updateGetHandler, handler_edit);
router.post('/doUpdateWorkOrder', permissionHandlers.updatePostHandler, handler_doUpdateWorkOrder); router.post('/doUpdateWorkOrder', permissionHandlers.updatePostHandler, handler_doUpdateWorkOrder);
router.post('/doCloseWorkOrder', permissionHandlers.updatePostHandler, handler_doCloseWorkOrder); router.post('/doCloseWorkOrder', permissionHandlers.updatePostHandler, handler_doCloseWorkOrder);
router.post('/doDeleteWorkOrder', permissionHandlers.updatePostHandler, handler_doDeleteWorkOrder); router.post('/doDeleteWorkOrder', permissionHandlers.updatePostHandler, handler_doDeleteWorkOrder);
// Lot Occupancy
router.post('/doAddWorkOrderLotOccupancy', permissionHandlers.updatePostHandler, handler_doAddWorkOrderLotOccupancy); router.post('/doAddWorkOrderLotOccupancy', permissionHandlers.updatePostHandler, handler_doAddWorkOrderLotOccupancy);
router.post('/doDeleteWorkOrderLotOccupancy', permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLotOccupancy); router.post('/doDeleteWorkOrderLotOccupancy', permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLotOccupancy);
router.post('/doAddWorkOrderLot', permissionHandlers.updatePostHandler, handler_doAddWorkOrderLot); router.post('/doAddWorkOrderLot', permissionHandlers.updatePostHandler, handler_doAddWorkOrderLot);
router.post('/doUpdateLotStatus', permissionHandlers.updatePostHandler, handler_doUpdateLotStatus); router.post('/doUpdateLotStatus', permissionHandlers.updatePostHandler, handler_doUpdateLotStatus);
router.post('/doDeleteWorkOrderLot', permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLot); router.post('/doDeleteWorkOrderLot', permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLot);
// Comments
router.post('/doAddWorkOrderComment', permissionHandlers.updatePostHandler, handler_doAddWorkOrderComment); router.post('/doAddWorkOrderComment', permissionHandlers.updatePostHandler, handler_doAddWorkOrderComment);
router.post('/doUpdateWorkOrderComment', permissionHandlers.updatePostHandler, handler_doUpdateWorkOrderComment); router.post('/doUpdateWorkOrderComment', permissionHandlers.updatePostHandler, handler_doUpdateWorkOrderComment);
router.post('/doDeleteWorkOrderComment', permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderComment); router.post('/doDeleteWorkOrderComment', permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderComment);
// Milestones
router.post('/doAddWorkOrderMilestone', permissionHandlers.updatePostHandler, handler_doAddWorkOrderMilestone); router.post('/doAddWorkOrderMilestone', permissionHandlers.updatePostHandler, handler_doAddWorkOrderMilestone);
router.post('/doUpdateWorkOrderMilestone', permissionHandlers.updatePostHandler, handler_doUpdateWorkOrderMilestone); router.post('/doUpdateWorkOrderMilestone', permissionHandlers.updatePostHandler, handler_doUpdateWorkOrderMilestone);
router.post('/doCompleteWorkOrderMilestone', permissionHandlers.updatePostHandler, handler_doCompleteWorkOrderMilestone); router.post('/doCompleteWorkOrderMilestone', permissionHandlers.updatePostHandler, handler_doCompleteWorkOrderMilestone);

View File

@ -105,6 +105,12 @@ const cemeteryToMapName = {
const mapCache = new Map(); const mapCache = new Map();
async function getMap(dataRow) { async function getMap(dataRow) {
const mapCacheKey = dataRow.cemetery; const mapCacheKey = dataRow.cemetery;
/*
if (masterRow.CM_CEMETERY === "HS" &&
(masterRow.CM_BLOCK === "F" || masterRow.CM_BLOCK === "G" || masterRow.CM_BLOCK === "H" || masterRow.CM_BLOCK === "J")) {
mapCacheKey += "-" + masterRow.CM_BLOCK;
}
*/
if (mapCache.has(mapCacheKey)) { if (mapCache.has(mapCacheKey)) {
return mapCache.get(mapCacheKey); return mapCache.get(mapCacheKey);
} }
@ -181,14 +187,17 @@ async function importFromMasterCSV() {
masterRow.CM_INTERMENT_YR !== '0') { masterRow.CM_INTERMENT_YR !== '0') {
occupancyEndDateString = formatDateString(masterRow.CM_INTERMENT_YR, masterRow.CM_INTERMENT_MON, masterRow.CM_INTERMENT_DAY); occupancyEndDateString = formatDateString(masterRow.CM_INTERMENT_YR, masterRow.CM_INTERMENT_MON, masterRow.CM_INTERMENT_DAY);
} }
// if purchase date unavailable
if (preneedOccupancyStartDateString === '0000-00-00' && if (preneedOccupancyStartDateString === '0000-00-00' &&
occupancyEndDateString !== '') { occupancyEndDateString !== '') {
preneedOccupancyStartDateString = occupancyEndDateString; preneedOccupancyStartDateString = occupancyEndDateString;
} }
// if end date unavailable
if (preneedOccupancyStartDateString === '0000-00-00' && if (preneedOccupancyStartDateString === '0000-00-00' &&
masterRow.CM_DEATH_YR !== '' && masterRow.CM_DEATH_YR !== '' &&
masterRow.CM_DEATH_YR !== '0') { masterRow.CM_DEATH_YR !== '0') {
preneedOccupancyStartDateString = formatDateString(masterRow.CM_DEATH_YR, masterRow.CM_DEATH_MON, masterRow.CM_DEATH_DAY); preneedOccupancyStartDateString = formatDateString(masterRow.CM_DEATH_YR, masterRow.CM_DEATH_MON, masterRow.CM_DEATH_DAY);
// if death took place, and there's no preneed end date
if (occupancyEndDateString === '0000-00-00' || if (occupancyEndDateString === '0000-00-00' ||
occupancyEndDateString === '') { occupancyEndDateString === '') {
occupancyEndDateString = preneedOccupancyStartDateString; occupancyEndDateString = preneedOccupancyStartDateString;
@ -251,6 +260,7 @@ async function importFromMasterCSV() {
let deceasedLotOccupancyId; let deceasedLotOccupancyId;
if (masterRow.CM_DECEASED_NAME !== '') { if (masterRow.CM_DECEASED_NAME !== '') {
deceasedOccupancyStartDateString = formatDateString(masterRow.CM_INTERMENT_YR, masterRow.CM_INTERMENT_MON, masterRow.CM_INTERMENT_DAY); deceasedOccupancyStartDateString = formatDateString(masterRow.CM_INTERMENT_YR, masterRow.CM_INTERMENT_MON, masterRow.CM_INTERMENT_DAY);
// if interment date unavailable
if (deceasedOccupancyStartDateString === '0000-00-00' && if (deceasedOccupancyStartDateString === '0000-00-00' &&
masterRow.CM_DEATH_YR !== '' && masterRow.CM_DEATH_YR !== '' &&
masterRow.CM_DEATH_YR !== '0') { masterRow.CM_DEATH_YR !== '0') {
@ -331,6 +341,20 @@ async function importFromMasterCSV() {
occupantPhoneNumber: funeralHomeOccupant.occupantPhoneNumber, occupantPhoneNumber: funeralHomeOccupant.occupantPhoneNumber,
occupantEmailAddress: funeralHomeOccupant.occupantEmailAddress occupantEmailAddress: funeralHomeOccupant.occupantEmailAddress
}, user); }, user);
/*
addOrUpdateLotOccupancyField(
{
lotOccupancyId: deceasedLotOccupancyId,
occupancyTypeFieldId: allOccupancyTypeFields.find(
(occupancyTypeField) => {
return occupancyTypeField.occupancyTypeField === "Funeral Home";
}
).occupancyTypeFieldId,
lotOccupancyFieldValue: masterRow.CM_FUNERAL_HOME
},
user
);
*/
} }
if (masterRow.CM_FUNERAL_YR !== '') { if (masterRow.CM_FUNERAL_YR !== '') {
const lotOccupancyFieldValue = formatDateString(masterRow.CM_FUNERAL_YR, masterRow.CM_FUNERAL_MON, masterRow.CM_FUNERAL_DAY); const lotOccupancyFieldValue = formatDateString(masterRow.CM_FUNERAL_YR, masterRow.CM_FUNERAL_MON, masterRow.CM_FUNERAL_DAY);
@ -811,6 +835,18 @@ async function importFromWorkOrderCSV() {
occupantPhoneNumber: funeralHomeOccupant.occupantPhoneNumber, occupantPhoneNumber: funeralHomeOccupant.occupantPhoneNumber,
occupantEmailAddress: funeralHomeOccupant.occupantEmailAddress occupantEmailAddress: funeralHomeOccupant.occupantEmailAddress
}, user); }, user);
/*
addOrUpdateLotOccupancyField(
{
lotOccupancyId: lotOccupancyId,
occupancyTypeFieldId: allOccupancyTypeFields.find((occupancyTypeField) => {
return occupancyTypeField.occupancyTypeField === "Funeral Home";
}).occupancyTypeFieldId,
lotOccupancyFieldValue: workOrderRow.WO_FUNERAL_HOME
},
user
);
*/
} }
if (workOrderRow.WO_FUNERAL_YR !== '') { if (workOrderRow.WO_FUNERAL_YR !== '') {
const lotOccupancyFieldValue = formatDateString(workOrderRow.WO_FUNERAL_YR, workOrderRow.WO_FUNERAL_MON, workOrderRow.WO_FUNERAL_DAY); const lotOccupancyFieldValue = formatDateString(workOrderRow.WO_FUNERAL_YR, workOrderRow.WO_FUNERAL_MON, workOrderRow.WO_FUNERAL_DAY);
@ -850,6 +886,7 @@ async function importFromWorkOrderCSV() {
workOrderId: workOrder.workOrderId, workOrderId: workOrder.workOrderId,
lotOccupancyId lotOccupancyId
}, user); }, user);
// Milestones
let hasIncompleteMilestones = !workOrderRow.WO_CONFIRMATION_IN; let hasIncompleteMilestones = !workOrderRow.WO_CONFIRMATION_IN;
let maxMilestoneCompletionDateString = workOrderOpenDateString; let maxMilestoneCompletionDateString = workOrderOpenDateString;
if (importIds.acknowledgedWorkOrderMilestoneTypeId) { if (importIds.acknowledgedWorkOrderMilestoneTypeId) {
@ -971,6 +1008,7 @@ async function importFromWorkOrderCSV() {
console.log(`Started ${new Date().toLocaleString()}`); console.log(`Started ${new Date().toLocaleString()}`);
console.time('importFromCsv'); console.time('importFromCsv');
purgeTables(); purgeTables();
// purgeConfigTables();
await importFromMasterCSV(); await importFromMasterCSV();
await importFromPrepaidCSV(); await importFromPrepaidCSV();
await importFromWorkOrderCSV(); await importFromWorkOrderCSV();

View File

@ -1,6 +1,11 @@
// eslint-disable-next-line @eslint-community/eslint-comments/disable-enable-pair
/* eslint-disable unicorn/no-await-expression-member */
import sqlite from 'better-sqlite3'; import sqlite from 'better-sqlite3';
import { lotOccupancyDB as databasePath } from '../data/databasePaths.js'; import { lotOccupancyDB as databasePath } from '../data/databasePaths.js';
import * as cacheFunctions from '../helpers/functions.cache.js'; import * as cacheFunctions from '../helpers/functions.cache.js';
/*
* Fee IDs
*/
const feeCache = new Map(); const feeCache = new Map();
export function getFeeIdByFeeDescription(feeDescription) { export function getFeeIdByFeeDescription(feeDescription) {
if (feeCache.keys.length === 0) { if (feeCache.keys.length === 0) {
@ -17,6 +22,9 @@ export function getFeeIdByFeeDescription(feeDescription) {
} }
return feeCache.get(feeDescription); return feeCache.get(feeDescription);
} }
/*
* Lot Occupant Type IDs
*/
export const preneedOwnerLotOccupantTypeId = (await cacheFunctions.getLotOccupantTypeByLotOccupantType('Preneed Owner')) export const preneedOwnerLotOccupantTypeId = (await cacheFunctions.getLotOccupantTypeByLotOccupantType('Preneed Owner'))
.lotOccupantTypeId; .lotOccupantTypeId;
export const funeralDirectorLotOccupantTypeId = (await cacheFunctions.getLotOccupantTypeByLotOccupantType('Funeral Director')).lotOccupantTypeId; export const funeralDirectorLotOccupantTypeId = (await cacheFunctions.getLotOccupantTypeByLotOccupantType('Funeral Director')).lotOccupantTypeId;
@ -24,9 +32,15 @@ export const deceasedLotOccupantTypeId = (await cacheFunctions.getLotOccupantTyp
.lotOccupantTypeId; .lotOccupantTypeId;
export const purchaserLotOccupantTypeId = (await cacheFunctions.getLotOccupantTypeByLotOccupantType('Purchaser')) export const purchaserLotOccupantTypeId = (await cacheFunctions.getLotOccupantTypeByLotOccupantType('Purchaser'))
.lotOccupantTypeId; .lotOccupantTypeId;
/*
* Lot Status IDs
*/
export const availableLotStatusId = (await cacheFunctions.getLotStatusByLotStatus('Available')).lotStatusId; export const availableLotStatusId = (await cacheFunctions.getLotStatusByLotStatus('Available')).lotStatusId;
export const reservedLotStatusId = (await cacheFunctions.getLotStatusByLotStatus('Reserved')).lotStatusId; export const reservedLotStatusId = (await cacheFunctions.getLotStatusByLotStatus('Reserved')).lotStatusId;
export const takenLotStatusId = (await cacheFunctions.getLotStatusByLotStatus('Taken')).lotStatusId; export const takenLotStatusId = (await cacheFunctions.getLotStatusByLotStatus('Taken')).lotStatusId;
/*
* Lot Type IDs
*/
const casketLotTypeId = (await cacheFunctions.getLotTypesByLotType('Casket Grave')).lotTypeId; const casketLotTypeId = (await cacheFunctions.getLotTypesByLotType('Casket Grave')).lotTypeId;
const columbariumLotTypeId = (await cacheFunctions.getLotTypesByLotType('Columbarium')).lotTypeId; const columbariumLotTypeId = (await cacheFunctions.getLotTypesByLotType('Columbarium')).lotTypeId;
const crematoriumLotTypeId = (await cacheFunctions.getLotTypesByLotType('Crematorium')).lotTypeId; const crematoriumLotTypeId = (await cacheFunctions.getLotTypesByLotType('Crematorium')).lotTypeId;
@ -55,12 +69,21 @@ export function getLotTypeId(dataRow) {
} }
return casketLotTypeId; return casketLotTypeId;
} }
/*
* Occupancy Type IDs
*/
export const preneedOccupancyType = (await cacheFunctions.getOccupancyTypeByOccupancyType('Preneed')); export const preneedOccupancyType = (await cacheFunctions.getOccupancyTypeByOccupancyType('Preneed'));
export const deceasedOccupancyType = (await cacheFunctions.getOccupancyTypeByOccupancyType('Interment')); export const deceasedOccupancyType = (await cacheFunctions.getOccupancyTypeByOccupancyType('Interment'));
export const cremationOccupancyType = (await cacheFunctions.getOccupancyTypeByOccupancyType('Cremation')); export const cremationOccupancyType = (await cacheFunctions.getOccupancyTypeByOccupancyType('Cremation'));
/*
* Work Order Milestone Type IDs
*/
export const acknowledgedWorkOrderMilestoneTypeId = (await cacheFunctions.getWorkOrderMilestoneTypeByWorkOrderMilestoneType('Acknowledged'))?.workOrderMilestoneTypeId; export const acknowledgedWorkOrderMilestoneTypeId = (await cacheFunctions.getWorkOrderMilestoneTypeByWorkOrderMilestoneType('Acknowledged'))?.workOrderMilestoneTypeId;
export const deathWorkOrderMilestoneTypeId = (await cacheFunctions.getWorkOrderMilestoneTypeByWorkOrderMilestoneType('Death'))?.workOrderMilestoneTypeId; export const deathWorkOrderMilestoneTypeId = (await cacheFunctions.getWorkOrderMilestoneTypeByWorkOrderMilestoneType('Death'))?.workOrderMilestoneTypeId;
export const funeralWorkOrderMilestoneTypeId = (await cacheFunctions.getWorkOrderMilestoneTypeByWorkOrderMilestoneType('Funeral'))?.workOrderMilestoneTypeId; export const funeralWorkOrderMilestoneTypeId = (await cacheFunctions.getWorkOrderMilestoneTypeByWorkOrderMilestoneType('Funeral'))?.workOrderMilestoneTypeId;
export const cremationWorkOrderMilestoneTypeId = (await cacheFunctions.getWorkOrderMilestoneTypeByWorkOrderMilestoneType('Cremation'))?.workOrderMilestoneTypeId; export const cremationWorkOrderMilestoneTypeId = (await cacheFunctions.getWorkOrderMilestoneTypeByWorkOrderMilestoneType('Cremation'))?.workOrderMilestoneTypeId;
export const intermentWorkOrderMilestoneTypeId = (await cacheFunctions.getWorkOrderMilestoneTypeByWorkOrderMilestoneType('Interment'))?.workOrderMilestoneTypeId; export const intermentWorkOrderMilestoneTypeId = (await cacheFunctions.getWorkOrderMilestoneTypeByWorkOrderMilestoneType('Interment'))?.workOrderMilestoneTypeId;
/*
* Work Order Type IDs
*/
export const workOrderTypeId = 1; export const workOrderTypeId = 1;

View File

@ -20,6 +20,7 @@ async function importMaps() {
} }
} }
catch { catch {
// ignore
} }
finally { finally {
try { try {
@ -28,6 +29,7 @@ async function importMaps() {
} }
} }
catch { catch {
// ignore
} }
} }
} }

View File

@ -1,3 +1,4 @@
/* eslint-disable unicorn/filename-case, @eslint-community/eslint-comments/disable-enable-pair */
import assert from 'node:assert'; import assert from 'node:assert';
import fs from 'node:fs/promises'; import fs from 'node:fs/promises';
import { lotOccupancyDB as databasePath, useTestDatabases } from '../data/databasePaths.js'; import { lotOccupancyDB as databasePath, useTestDatabases } from '../data/databasePaths.js';

View File

@ -1,3 +1,4 @@
/* eslint-disable unicorn/filename-case, @eslint-community/eslint-comments/disable-enable-pair */
import assert from 'node:assert'; import assert from 'node:assert';
import { exec } from 'node:child_process'; import { exec } from 'node:child_process';
import * as http from 'node:http'; import * as http from 'node:http';
@ -34,6 +35,7 @@ describe('lot-occupancy-system', () => {
httpServer.close(); httpServer.close();
} }
catch { catch {
// ignore
} }
}); });
it(`Ensure server starts on port ${portNumber.toString()}`, () => { it(`Ensure server starts on port ${portNumber.toString()}`, () => {

View File

@ -1,8 +1,11 @@
import assert from 'node:assert'; import assert from 'node:assert';
import fs from 'node:fs'; import fs from 'node:fs';
import { lotNameSortNameFunction } from '../data/config.cemetery.ssm.js'; import { lotNameSortNameFunction } from '../data/config.cemetery.ssm.js';
// skipcq: JS-C1003 - Testing functions
import * as cacheFunctions from '../helpers/functions.cache.js'; import * as cacheFunctions from '../helpers/functions.cache.js';
// skipcq: JS-C1003 - Testing functions
import * as sqlFilterFunctions from '../helpers/functions.sqlFilters.js'; import * as sqlFilterFunctions from '../helpers/functions.sqlFilters.js';
// skipcq: JS-C1003 - Testing functions
import * as userFunctions from '../helpers/functions.user.js'; import * as userFunctions from '../helpers/functions.user.js';
describe('config.cemetery.ssm', () => { describe('config.cemetery.ssm', () => {
it('Sorts burial site names', () => { it('Sorts burial site names', () => {
@ -13,6 +16,7 @@ describe('config.cemetery.ssm', () => {
}); });
describe('functions.cache', () => { describe('functions.cache', () => {
const badId = -3; const badId = -3;
// eslint-disable-next-line no-secrets/no-secrets
const badName = 'qwertyuiopasdfghjklzxcvbnm'; const badName = 'qwertyuiopasdfghjklzxcvbnm';
before(() => { before(() => {
cacheFunctions.clearCaches(); cacheFunctions.clearCaches();

View File

@ -6,7 +6,6 @@
"isolatedModules": false, "isolatedModules": false,
"declaration": true, "declaration": true,
"noImplicitAny": false, "noImplicitAny": false,
"removeComments": true,
"allowUnreachableCode": false, "allowUnreachableCode": false,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"resolveJsonModule": true, "resolveJsonModule": true,

View File

@ -1,6 +1,10 @@
/* eslint-disable unicorn/filename-case, @eslint-community/eslint-comments/disable-enable-pair */
import { Service } from 'node-windows'; import { Service } from 'node-windows';
import { serviceConfig } from './windowsService.js'; import { serviceConfig } from './windowsService.js';
// Create a new service object
const svc = new Service(serviceConfig); const svc = new Service(serviceConfig);
// Listen for the "install" event, which indicates the
// process is available as a service.
svc.on('install', () => { svc.on('install', () => {
svc.start(); svc.start();
}); });

View File

@ -1,8 +1,12 @@
/* eslint-disable unicorn/filename-case, @eslint-community/eslint-comments/disable-enable-pair */
import { Service } from 'node-windows'; import { Service } from 'node-windows';
import { serviceConfig } from './windowsService.js'; import { serviceConfig } from './windowsService.js';
// Create a new service object
const svc = new Service(serviceConfig); const svc = new Service(serviceConfig);
// Listen for the "uninstall" event so we know when it's done.
svc.on('uninstall', () => { svc.on('uninstall', () => {
console.log('Uninstall complete.'); console.log('Uninstall complete.');
console.log('The service exists:', svc.exists); console.log('The service exists:', svc.exists);
}); });
// Uninstall the service.
svc.uninstall(); svc.uninstall();