diff --git a/database/getContracts.d.ts b/database/getContracts.d.ts index 8b6e80c4..773d4de1 100644 --- a/database/getContracts.d.ts +++ b/database/getContracts.d.ts @@ -13,6 +13,7 @@ export interface GetContractsFilters { burialSiteNameSearchType?: '' | 'endsWith' | 'startsWith'; burialSiteTypeId?: number | string; funeralHomeId?: number | string; + funeralTime?: '' | 'upcoming'; notWorkOrderId?: number | string; workOrderId?: number | string; } @@ -20,6 +21,7 @@ export interface GetContractsOptions { /** -1 for no limit */ limit: number | string; offset: number | string; + orderBy?: string; includeFees: boolean; includeInterments: boolean; includeTransactions: boolean; diff --git a/database/getContracts.js b/database/getContracts.js index b23ba0ee..743f0950 100644 --- a/database/getContracts.js +++ b/database/getContracts.js @@ -1,4 +1,4 @@ -import { dateIntegerToString, dateStringToInteger, timeIntegerToPeriodString, timeIntegerToString } from '@cityssm/utils-datetime'; +import { dateIntegerToString, dateStringToInteger, dateToInteger, timeIntegerToPeriodString, timeIntegerToString } from '@cityssm/utils-datetime'; import sqlite from 'better-sqlite3'; import { getConfigProperty } from '../helpers/config.helpers.js'; import { sunriseDB } from '../helpers/database.helpers.js'; @@ -20,49 +20,51 @@ export default async function getContracts(filters, options, connectedDatabase) if (isLimited) { count = database .prepare(`select count(*) as recordCount - from Contracts o - left join BurialSites l on o.burialSiteId = l.burialSiteId - left join Cemeteries m on l.cemeteryId = m.cemeteryId - ${sqlWhereClause}`) + from Contracts c + left join BurialSites l on c.burialSiteId = l.burialSiteId + left join Cemeteries m on l.cemeteryId = m.cemeteryId + ${sqlWhereClause}`) .get(sqlParameters).recordCount; } let contracts = []; if (count !== 0) { contracts = database - .prepare(`select o.contractId, - o.contractTypeId, t.contractType, t.isPreneed, - o.burialSiteId, lt.burialSiteType, l.burialSiteName, - case when l.recordDelete_timeMillis is null then 1 else 0 end as burialSiteIsActive, - l.cemeteryId, m.cemeteryName, - o.contractStartDate, userFn_dateIntegerToString(o.contractStartDate) as contractStartDateString, - o.contractEndDate, userFn_dateIntegerToString(o.contractEndDate) as contractEndDateString, - o.purchaserName, o.purchaserAddress1, o.purchaserAddress2, - o.purchaserCity, o.purchaserProvince, o.purchaserPostalCode, - o.purchaserPhoneNumber, o.purchaserEmail, o.purchaserRelationship, - o.funeralHomeId, o.funeralDirectorName, f.funeralHomeName, + .prepare(`select c.contractId, + c.contractTypeId, t.contractType, t.isPreneed, + c.burialSiteId, lt.burialSiteType, l.burialSiteName, + case when l.recordDelete_timeMillis is null then 1 else 0 end as burialSiteIsActive, + l.cemeteryId, m.cemeteryName, + c.contractStartDate, userFn_dateIntegerToString(c.contractStartDate) as contractStartDateString, + c.contractEndDate, userFn_dateIntegerToString(c.contractEndDate) as contractEndDateString, + c.purchaserName, c.purchaserAddress1, c.purchaserAddress2, + c.purchaserCity, c.purchaserProvince, c.purchaserPostalCode, + c.purchaserPhoneNumber, c.purchaserEmail, c.purchaserRelationship, + c.funeralHomeId, c.funeralDirectorName, f.funeralHomeName, - o.funeralDate, userFn_dateIntegerToString(o.funeralDate) as funeralDateString, - o.funeralTime, - userFn_timeIntegerToString(o.funeralTime) as funeralTimeString, - userFn_timeIntegerToPeriodString(o.funeralTime) as funeralTimePeriodString, - o.directionOfArrival, - o.committalTypeId, c.committalType - from Contracts o - left join ContractTypes t on o.contractTypeId = t.contractTypeId - left join CommittalTypes c on o.committalTypeId = c.committalTypeId - left join BurialSites l on o.burialSiteId = l.burialSiteId - left join BurialSiteTypes lt on l.burialSiteTypeId = lt.burialSiteTypeId - left join Cemeteries m on l.cemeteryId = m.cemeteryId - left join FuneralHomes f on o.funeralHomeId = f.funeralHomeId - ${sqlWhereClause} - order by o.contractStartDate desc, ifnull(o.contractEndDate, 99999999) desc, - l.burialSiteNameSegment1, - l.burialSiteNameSegment2, - l.burialSiteNameSegment3, - l.burialSiteNameSegment4, - l.burialSiteNameSegment5, - o.burialSiteId, o.contractId desc - ${isLimited ? ` limit ${options.limit} offset ${options.offset}` : ''}`) + c.funeralDate, userFn_dateIntegerToString(c.funeralDate) as funeralDateString, + c.funeralTime, + userFn_timeIntegerToString(c.funeralTime) as funeralTimeString, + userFn_timeIntegerToPeriodString(c.funeralTime) as funeralTimePeriodString, + c.directionOfArrival, + c.committalTypeId, cm.committalType + from Contracts c + left join ContractTypes t on c.contractTypeId = t.contractTypeId + left join CommittalTypes cm on c.committalTypeId = cm.committalTypeId + left join BurialSites l on c.burialSiteId = l.burialSiteId + left join BurialSiteTypes lt on l.burialSiteTypeId = lt.burialSiteTypeId + left join Cemeteries m on l.cemeteryId = m.cemeteryId + left join FuneralHomes f on c.funeralHomeId = f.funeralHomeId + ${sqlWhereClause} + ${options.orderBy !== undefined && options.orderBy !== '' + ? ` order by ${options.orderBy}` + : `order by c.contractStartDate desc, ifnull(c.contractEndDate, 99999999) desc, + l.burialSiteNameSegment1, + l.burialSiteNameSegment2, + l.burialSiteNameSegment3, + l.burialSiteNameSegment4, + l.burialSiteNameSegment5, + c.burialSiteId, c.contractId desc`} + ${isLimited ? ` limit ${options.limit} offset ${options.offset}` : ''}`) .all(sqlParameters); if (!isLimited) { count = contracts.length; @@ -99,38 +101,38 @@ async function addInclusions(contract, options, database) { } // eslint-disable-next-line complexity function buildWhereClause(filters) { - let sqlWhereClause = ' where o.recordDelete_timeMillis is null'; + let sqlWhereClause = ' where c.recordDelete_timeMillis is null'; const sqlParameters = []; if ((filters.burialSiteId ?? '') !== '') { - sqlWhereClause += ' and o.burialSiteId = ?'; + sqlWhereClause += ' and c.burialSiteId = ?'; sqlParameters.push(filters.burialSiteId); } const burialSiteNameFilters = getBurialSiteNameWhereClause(filters.burialSiteName, filters.burialSiteNameSearchType ?? '', 'l'); sqlWhereClause += burialSiteNameFilters.sqlWhereClause; sqlParameters.push(...burialSiteNameFilters.sqlParameters); - const deceasedNameFilters = getDeceasedNameWhereClause(filters.deceasedName, 'o'); + const deceasedNameFilters = getDeceasedNameWhereClause(filters.deceasedName, 'c'); if (deceasedNameFilters.sqlParameters.length > 0) { - sqlWhereClause += ` and o.contractId in ( - select contractId from ContractInterments o + sqlWhereClause += ` and c.contractId in ( + select contractId from ContractInterments c where recordDelete_timeMillis is null ${deceasedNameFilters.sqlWhereClause})`; sqlParameters.push(...deceasedNameFilters.sqlParameters); } if ((filters.contractTypeId ?? '') !== '') { - sqlWhereClause += ' and o.contractTypeId = ?'; + sqlWhereClause += ' and c.contractTypeId = ?'; sqlParameters.push(filters.contractTypeId); } - const contractTimeFilters = getContractTimeWhereClause(filters.contractTime ?? '', 'o'); + const contractTimeFilters = getContractTimeWhereClause(filters.contractTime ?? '', 'c'); sqlWhereClause += contractTimeFilters.sqlWhereClause; sqlParameters.push(...contractTimeFilters.sqlParameters); if ((filters.contractStartDateString ?? '') !== '') { - sqlWhereClause += ' and o.contractStartDate = ?'; + sqlWhereClause += ' and c.contractStartDate = ?'; sqlParameters.push(dateStringToInteger(filters.contractStartDateString)); } if ((filters.contractEffectiveDateString ?? '') !== '') { sqlWhereClause += ` and ( - o.contractEndDate is null - or (o.contractStartDate <= ? and o.contractEndDate >= ?) + c.contractEndDate is null + or (c.contractStartDate <= ? and c.contractEndDate >= ?) )`; sqlParameters.push(dateStringToInteger(filters.contractEffectiveDateString), dateStringToInteger(filters.contractEffectiveDateString)); } @@ -143,17 +145,21 @@ function buildWhereClause(filters) { sqlParameters.push(filters.burialSiteTypeId); } if ((filters.funeralHomeId ?? '') !== '') { - sqlWhereClause += ' and o.funeralHomeId = ?'; + sqlWhereClause += ' and c.funeralHomeId = ?'; sqlParameters.push(filters.funeralHomeId); } + if ((filters.funeralTime ?? '') === 'upcoming') { + sqlWhereClause += ' and c.funeralDate >= ?'; + sqlParameters.push(dateToInteger(new Date())); + } if ((filters.workOrderId ?? '') !== '') { sqlWhereClause += - ' and o.contractId in (select contractId from WorkOrderContracts where recordDelete_timeMillis is null and workOrderId = ?)'; + ' and c.contractId in (select contractId from WorkOrderContracts where recordDelete_timeMillis is null and workOrderId = ?)'; sqlParameters.push(filters.workOrderId); } if ((filters.notWorkOrderId ?? '') !== '') { sqlWhereClause += - ' and o.contractId not in (select contractId from WorkOrderContracts where recordDelete_timeMillis is null and workOrderId = ?)'; + ' and c.contractId not in (select contractId from WorkOrderContracts where recordDelete_timeMillis is null and workOrderId = ?)'; sqlParameters.push(filters.notWorkOrderId); } return { diff --git a/database/getContracts.ts b/database/getContracts.ts index 692c398e..5e49f72c 100644 --- a/database/getContracts.ts +++ b/database/getContracts.ts @@ -2,6 +2,7 @@ import { type DateString, dateIntegerToString, dateStringToInteger, + dateToInteger, timeIntegerToPeriodString, timeIntegerToString } from '@cityssm/utils-datetime' @@ -36,7 +37,9 @@ export interface GetContractsFilters { burialSiteNameSearchType?: '' | 'endsWith' | 'startsWith' burialSiteTypeId?: number | string + funeralHomeId?: number | string + funeralTime?: '' | 'upcoming' notWorkOrderId?: number | string workOrderId?: number | string @@ -47,6 +50,8 @@ export interface GetContractsOptions { limit: number | string offset: number | string + orderBy?: string + includeFees: boolean includeInterments: boolean includeTransactions: boolean @@ -62,7 +67,9 @@ export default async function getContracts( database.function('userFn_dateIntegerToString', dateIntegerToString) database.function('userFn_timeIntegerToString', timeIntegerToString) database.function( - 'userFn_timeIntegerToPeriodString', timeIntegerToPeriodString) + 'userFn_timeIntegerToPeriodString', + timeIntegerToPeriodString + ) const { sqlParameters, sqlWhereClause } = buildWhereClause(filters) @@ -78,10 +85,10 @@ export default async function getContracts( database .prepare( `select count(*) as recordCount - from Contracts o - left join BurialSites l on o.burialSiteId = l.burialSiteId - left join Cemeteries m on l.cemeteryId = m.cemeteryId - ${sqlWhereClause}` + from Contracts c + left join BurialSites l on c.burialSiteId = l.burialSiteId + left join Cemeteries m on l.cemeteryId = m.cemeteryId + ${sqlWhereClause}` ) .get(sqlParameters) as { recordCount: number } ).recordCount @@ -92,42 +99,44 @@ export default async function getContracts( if (count !== 0) { contracts = database .prepare( - `select o.contractId, - o.contractTypeId, t.contractType, t.isPreneed, - o.burialSiteId, lt.burialSiteType, l.burialSiteName, - case when l.recordDelete_timeMillis is null then 1 else 0 end as burialSiteIsActive, - l.cemeteryId, m.cemeteryName, - o.contractStartDate, userFn_dateIntegerToString(o.contractStartDate) as contractStartDateString, - o.contractEndDate, userFn_dateIntegerToString(o.contractEndDate) as contractEndDateString, - o.purchaserName, o.purchaserAddress1, o.purchaserAddress2, - o.purchaserCity, o.purchaserProvince, o.purchaserPostalCode, - o.purchaserPhoneNumber, o.purchaserEmail, o.purchaserRelationship, - o.funeralHomeId, o.funeralDirectorName, f.funeralHomeName, + `select c.contractId, + c.contractTypeId, t.contractType, t.isPreneed, + c.burialSiteId, lt.burialSiteType, l.burialSiteName, + case when l.recordDelete_timeMillis is null then 1 else 0 end as burialSiteIsActive, + l.cemeteryId, m.cemeteryName, + c.contractStartDate, userFn_dateIntegerToString(c.contractStartDate) as contractStartDateString, + c.contractEndDate, userFn_dateIntegerToString(c.contractEndDate) as contractEndDateString, + c.purchaserName, c.purchaserAddress1, c.purchaserAddress2, + c.purchaserCity, c.purchaserProvince, c.purchaserPostalCode, + c.purchaserPhoneNumber, c.purchaserEmail, c.purchaserRelationship, + c.funeralHomeId, c.funeralDirectorName, f.funeralHomeName, - o.funeralDate, userFn_dateIntegerToString(o.funeralDate) as funeralDateString, - o.funeralTime, - userFn_timeIntegerToString(o.funeralTime) as funeralTimeString, - userFn_timeIntegerToPeriodString(o.funeralTime) as funeralTimePeriodString, - o.directionOfArrival, - o.committalTypeId, c.committalType - from Contracts o - left join ContractTypes t on o.contractTypeId = t.contractTypeId - left join CommittalTypes c on o.committalTypeId = c.committalTypeId - left join BurialSites l on o.burialSiteId = l.burialSiteId - left join BurialSiteTypes lt on l.burialSiteTypeId = lt.burialSiteTypeId - left join Cemeteries m on l.cemeteryId = m.cemeteryId - left join FuneralHomes f on o.funeralHomeId = f.funeralHomeId - ${sqlWhereClause} - order by o.contractStartDate desc, ifnull(o.contractEndDate, 99999999) desc, - l.burialSiteNameSegment1, - l.burialSiteNameSegment2, - l.burialSiteNameSegment3, - l.burialSiteNameSegment4, - l.burialSiteNameSegment5, - o.burialSiteId, o.contractId desc - ${ - isLimited ? ` limit ${options.limit} offset ${options.offset}` : '' - }` + c.funeralDate, userFn_dateIntegerToString(c.funeralDate) as funeralDateString, + c.funeralTime, + userFn_timeIntegerToString(c.funeralTime) as funeralTimeString, + userFn_timeIntegerToPeriodString(c.funeralTime) as funeralTimePeriodString, + c.directionOfArrival, + c.committalTypeId, cm.committalType + from Contracts c + left join ContractTypes t on c.contractTypeId = t.contractTypeId + left join CommittalTypes cm on c.committalTypeId = cm.committalTypeId + left join BurialSites l on c.burialSiteId = l.burialSiteId + left join BurialSiteTypes lt on l.burialSiteTypeId = lt.burialSiteTypeId + left join Cemeteries m on l.cemeteryId = m.cemeteryId + left join FuneralHomes f on c.funeralHomeId = f.funeralHomeId + ${sqlWhereClause} + ${ + options.orderBy !== undefined && options.orderBy !== '' + ? ` order by ${options.orderBy}` + : `order by c.contractStartDate desc, ifnull(c.contractEndDate, 99999999) desc, + l.burialSiteNameSegment1, + l.burialSiteNameSegment2, + l.burialSiteNameSegment3, + l.burialSiteNameSegment4, + l.burialSiteNameSegment5, + c.burialSiteId, c.contractId desc` + } + ${isLimited ? ` limit ${options.limit} offset ${options.offset}` : ''}` ) .all(sqlParameters) as Contract[] @@ -192,11 +201,11 @@ function buildWhereClause(filters: GetContractsFilters): { sqlParameters: unknown[] sqlWhereClause: string } { - let sqlWhereClause = ' where o.recordDelete_timeMillis is null' + let sqlWhereClause = ' where c.recordDelete_timeMillis is null' const sqlParameters: unknown[] = [] if ((filters.burialSiteId ?? '') !== '') { - sqlWhereClause += ' and o.burialSiteId = ?' + sqlWhereClause += ' and c.burialSiteId = ?' sqlParameters.push(filters.burialSiteId) } @@ -210,30 +219,30 @@ function buildWhereClause(filters: GetContractsFilters): { const deceasedNameFilters = getDeceasedNameWhereClause( filters.deceasedName, - 'o' + 'c' ) if (deceasedNameFilters.sqlParameters.length > 0) { - sqlWhereClause += ` and o.contractId in ( - select contractId from ContractInterments o + sqlWhereClause += ` and c.contractId in ( + select contractId from ContractInterments c where recordDelete_timeMillis is null ${deceasedNameFilters.sqlWhereClause})` sqlParameters.push(...deceasedNameFilters.sqlParameters) } if ((filters.contractTypeId ?? '') !== '') { - sqlWhereClause += ' and o.contractTypeId = ?' + sqlWhereClause += ' and c.contractTypeId = ?' sqlParameters.push(filters.contractTypeId) } const contractTimeFilters = getContractTimeWhereClause( filters.contractTime ?? '', - 'o' + 'c' ) sqlWhereClause += contractTimeFilters.sqlWhereClause sqlParameters.push(...contractTimeFilters.sqlParameters) if ((filters.contractStartDateString ?? '') !== '') { - sqlWhereClause += ' and o.contractStartDate = ?' + sqlWhereClause += ' and c.contractStartDate = ?' sqlParameters.push( dateStringToInteger(filters.contractStartDateString as DateString) ) @@ -241,8 +250,8 @@ function buildWhereClause(filters: GetContractsFilters): { if ((filters.contractEffectiveDateString ?? '') !== '') { sqlWhereClause += ` and ( - o.contractEndDate is null - or (o.contractStartDate <= ? and o.contractEndDate >= ?) + c.contractEndDate is null + or (c.contractStartDate <= ? and c.contractEndDate >= ?) )` sqlParameters.push( dateStringToInteger(filters.contractEffectiveDateString as DateString), @@ -261,19 +270,24 @@ function buildWhereClause(filters: GetContractsFilters): { } if ((filters.funeralHomeId ?? '') !== '') { - sqlWhereClause += ' and o.funeralHomeId = ?' + sqlWhereClause += ' and c.funeralHomeId = ?' sqlParameters.push(filters.funeralHomeId) } + if ((filters.funeralTime ?? '') === 'upcoming') { + sqlWhereClause += ' and c.funeralDate >= ?' + sqlParameters.push(dateToInteger(new Date())) + } + if ((filters.workOrderId ?? '') !== '') { sqlWhereClause += - ' and o.contractId in (select contractId from WorkOrderContracts where recordDelete_timeMillis is null and workOrderId = ?)' + ' and c.contractId in (select contractId from WorkOrderContracts where recordDelete_timeMillis is null and workOrderId = ?)' sqlParameters.push(filters.workOrderId) } if ((filters.notWorkOrderId ?? '') !== '') { sqlWhereClause += - ' and o.contractId not in (select contractId from WorkOrderContracts where recordDelete_timeMillis is null and workOrderId = ?)' + ' and c.contractId not in (select contractId from WorkOrderContracts where recordDelete_timeMillis is null and workOrderId = ?)' sqlParameters.push(filters.notWorkOrderId) } diff --git a/handlers/funeralHomes-get/view.d.ts b/handlers/funeralHomes-get/view.d.ts index d5e48cba..19d32e4d 100644 --- a/handlers/funeralHomes-get/view.d.ts +++ b/handlers/funeralHomes-get/view.d.ts @@ -1,2 +1,2 @@ import type { Request, Response } from 'express'; -export default function handler(request: Request, response: Response): void; +export default function handler(request: Request, response: Response): Promise; diff --git a/handlers/funeralHomes-get/view.js b/handlers/funeralHomes-get/view.js index cba61531..d2452fec 100644 --- a/handlers/funeralHomes-get/view.js +++ b/handlers/funeralHomes-get/view.js @@ -1,13 +1,26 @@ +import getContracts from '../../database/getContracts.js'; import getFuneralHome from '../../database/getFuneralHome.js'; import { getConfigProperty } from '../../helpers/config.helpers.js'; -export default function handler(request, response) { +export default async function handler(request, response) { const funeralHome = getFuneralHome(request.params.funeralHomeId); if (funeralHome === undefined) { response.redirect(`${getConfigProperty('reverseProxy.urlPrefix')}/funeralHomes/?error=funeralHomeIdNotFound`); return; } + const contracts = await getContracts({ + funeralHomeId: funeralHome.funeralHomeId, + funeralTime: 'upcoming' + }, { + limit: -1, + offset: 0, + orderBy: 'c.funeralDate, c.funeralTime, c.contractId', + includeFees: false, + includeInterments: true, + includeTransactions: false + }); response.render('funeralHome-view', { headTitle: funeralHome.funeralHomeName, - funeralHome + funeralHome, + contracts: contracts.contracts, }); } diff --git a/handlers/funeralHomes-get/view.ts b/handlers/funeralHomes-get/view.ts index d2449750..30a173e8 100644 --- a/handlers/funeralHomes-get/view.ts +++ b/handlers/funeralHomes-get/view.ts @@ -1,9 +1,10 @@ import type { Request, Response } from 'express' +import getContracts from '../../database/getContracts.js' import getFuneralHome from '../../database/getFuneralHome.js' import { getConfigProperty } from '../../helpers/config.helpers.js' -export default function handler(request: Request, response: Response): void { +export default async function handler(request: Request, response: Response): Promise { const funeralHome = getFuneralHome(request.params.funeralHomeId) if (funeralHome === undefined) { @@ -13,9 +14,28 @@ export default function handler(request: Request, response: Response): void { return } + const contracts = await getContracts( + { + funeralHomeId: funeralHome.funeralHomeId, + funeralTime: 'upcoming' + }, + { + limit: -1, + offset: 0, + + orderBy: 'c.funeralDate, c.funeralTime, c.contractId', + + includeFees: false, + includeInterments: true, + includeTransactions: false + } + ) + response.render('funeralHome-view', { headTitle: funeralHome.funeralHomeName, - funeralHome + funeralHome, + + contracts: contracts.contracts, }) } diff --git a/views/funeralHome-view.ejs b/views/funeralHome-view.ejs index 437f07a0..f7584e9e 100644 --- a/views/funeralHome-view.ejs +++ b/views/funeralHome-view.ejs @@ -66,6 +66,73 @@ +
+
+
+
+

Contracts with Upcoming Funerals

+
+
+ +
+
+
+
+ <% if (contracts.length === 0) { %> +
+
+

No contracts with upcoming funerals.

+
+
+ <% } else { %> + + + + + + + + + + + <% for (const contract of contracts) { %> + + + + + + + <% } %> + +
Contract TypeFuneral DateIntermentsFuneral Director
+ + <%= contract.contractType %> +
+ #<%= contract.contractId %> +
+ <%= contract.funeralDateString %> + <%= contract.funeralTime === null || contract.funeralTime === 0 ? '' : contract.funeralTimeString %> + + <% if (contract.contractInterments.length === 0) { %> + (No Interments) + <% } else { %> +
    + <% for (const interment of contract.contractInterments) { %> +
  • + + <%= interment.deceasedName %> +
  • + <% } %> +
+ <% } %> +
+ <%= contract.funeralDirectorName %>
+
+ <% } %> +
+
+ <%- include('_footerA'); -%> <%- include('_footerB'); -%> \ No newline at end of file