diff --git a/data/config.cemetery.ssm.js b/data/config.cemetery.ssm.js index b3f12112..5d8bb2fe 100644 --- a/data/config.cemetery.ssm.js +++ b/data/config.cemetery.ssm.js @@ -1,4 +1,5 @@ import { config as cemeteryConfig } from "./config.cemetery.ontario.js"; export const config = Object.assign({}, cemeteryConfig); config.settings.lotOccupancy.occupantCityDefault = "Sault Ste. Marie"; +config.aliases.externalReceiptNumber = "GP Receipt Number"; export default config; diff --git a/data/config.cemetery.ssm.ts b/data/config.cemetery.ssm.ts index 637acfa5..66377bcd 100644 --- a/data/config.cemetery.ssm.ts +++ b/data/config.cemetery.ssm.ts @@ -4,4 +4,6 @@ export const config = Object.assign({}, cemeteryConfig); config.settings.lotOccupancy.occupantCityDefault = "Sault Ste. Marie"; +config.aliases.externalReceiptNumber = "GP Receipt Number"; + export default config; \ No newline at end of file diff --git a/handlers/lotOccupancies-post/doAddLotOccupancyTransaction.d.ts b/handlers/lotOccupancies-post/doAddLotOccupancyTransaction.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/lotOccupancies-post/doAddLotOccupancyTransaction.d.ts @@ -0,0 +1,3 @@ +import type { RequestHandler } from "express"; +export declare const handler: RequestHandler; +export default handler; diff --git a/handlers/lotOccupancies-post/doAddLotOccupancyTransaction.js b/handlers/lotOccupancies-post/doAddLotOccupancyTransaction.js new file mode 100644 index 00000000..8960245c --- /dev/null +++ b/handlers/lotOccupancies-post/doAddLotOccupancyTransaction.js @@ -0,0 +1,11 @@ +import { addLotOccupancyTransaction } from "../../helpers/lotOccupancyDB/addLotOccupancyTransaction.js"; +import { getLotOccupancyTransactions } from "../../helpers/lotOccupancyDB/getLotOccupancyTransactions.js"; +export const handler = async (request, response) => { + addLotOccupancyTransaction(request.body, request.session); + const lotOccupancyTransactions = getLotOccupancyTransactions(request.body.lotOccupancyId); + response.json({ + success: true, + lotOccupancyTransactions + }); +}; +export default handler; diff --git a/handlers/lotOccupancies-post/doAddLotOccupancyTransaction.ts b/handlers/lotOccupancies-post/doAddLotOccupancyTransaction.ts new file mode 100644 index 00000000..759cceab --- /dev/null +++ b/handlers/lotOccupancies-post/doAddLotOccupancyTransaction.ts @@ -0,0 +1,27 @@ +import type { + RequestHandler +} from "express"; + +import { + addLotOccupancyTransaction +} from "../../helpers/lotOccupancyDB/addLotOccupancyTransaction.js"; + +import { + getLotOccupancyTransactions +} from "../../helpers/lotOccupancyDB/getLotOccupancyTransactions.js"; + + +export const handler: RequestHandler = async (request, response) => { + + addLotOccupancyTransaction(request.body, request.session); + + const lotOccupancyTransactions = getLotOccupancyTransactions(request.body.lotOccupancyId); + + response.json({ + success: true, + lotOccupancyTransactions + }); +}; + + +export default handler; \ No newline at end of file diff --git a/handlers/lotOccupancies-post/doDeleteLotOccupancyOccupant.js b/handlers/lotOccupancies-post/doDeleteLotOccupancyOccupant.js index 6a0907b0..d9c4cc6c 100644 --- a/handlers/lotOccupancies-post/doDeleteLotOccupancyOccupant.js +++ b/handlers/lotOccupancies-post/doDeleteLotOccupancyOccupant.js @@ -1,10 +1,10 @@ import { deleteLotOccupancyOccupant } from "../../helpers/lotOccupancyDB/deleteLotOccupancyOccupant.js"; import { getLotOccupancyOccupants } from "../../helpers/lotOccupancyDB/getLotOccupancyOccupants.js"; export const handler = async (request, response) => { - deleteLotOccupancyOccupant(request.body.lotOccupancyId, request.body.lotOccupantIndex, request.session); + const success = deleteLotOccupancyOccupant(request.body.lotOccupancyId, request.body.lotOccupantIndex, request.session); const lotOccupancyOccupants = getLotOccupancyOccupants(request.body.lotOccupancyId); response.json({ - success: true, + success, lotOccupancyOccupants }); }; diff --git a/handlers/lotOccupancies-post/doDeleteLotOccupancyOccupant.ts b/handlers/lotOccupancies-post/doDeleteLotOccupancyOccupant.ts index adea17fc..19d8c535 100644 --- a/handlers/lotOccupancies-post/doDeleteLotOccupancyOccupant.ts +++ b/handlers/lotOccupancies-post/doDeleteLotOccupancyOccupant.ts @@ -13,12 +13,12 @@ import { export const handler: RequestHandler = async (request, response) => { - deleteLotOccupancyOccupant(request.body.lotOccupancyId, request.body.lotOccupantIndex, request.session); + const success = deleteLotOccupancyOccupant(request.body.lotOccupancyId, request.body.lotOccupantIndex, request.session); const lotOccupancyOccupants = getLotOccupancyOccupants(request.body.lotOccupancyId); response.json({ - success: true, + success, lotOccupancyOccupants }); }; diff --git a/handlers/lotOccupancies-post/doDeleteLotOccupancyTransaction.d.ts b/handlers/lotOccupancies-post/doDeleteLotOccupancyTransaction.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/lotOccupancies-post/doDeleteLotOccupancyTransaction.d.ts @@ -0,0 +1,3 @@ +import type { RequestHandler } from "express"; +export declare const handler: RequestHandler; +export default handler; diff --git a/handlers/lotOccupancies-post/doDeleteLotOccupancyTransaction.js b/handlers/lotOccupancies-post/doDeleteLotOccupancyTransaction.js new file mode 100644 index 00000000..2c974b43 --- /dev/null +++ b/handlers/lotOccupancies-post/doDeleteLotOccupancyTransaction.js @@ -0,0 +1,11 @@ +import { deleteLotOccupancyTransaction } from "../../helpers/lotOccupancyDB/deleteLotOccupancyTransaction.js"; +import { getLotOccupancyTransactions } from "../../helpers/lotOccupancyDB/getLotOccupancyTransactions.js"; +export const handler = async (request, response) => { + const success = deleteLotOccupancyTransaction(request.body.lotOccupancyId, request.body.transactionIndex, request.session); + const lotOccupancyTransactions = getLotOccupancyTransactions(request.body.lotOccupancyId); + response.json({ + success, + lotOccupancyTransactions + }); +}; +export default handler; diff --git a/handlers/lotOccupancies-post/doDeleteLotOccupancyTransaction.ts b/handlers/lotOccupancies-post/doDeleteLotOccupancyTransaction.ts new file mode 100644 index 00000000..e9a738b5 --- /dev/null +++ b/handlers/lotOccupancies-post/doDeleteLotOccupancyTransaction.ts @@ -0,0 +1,27 @@ +import type { + RequestHandler +} from "express"; + +import { + deleteLotOccupancyTransaction +} from "../../helpers/lotOccupancyDB/deleteLotOccupancyTransaction.js"; + +import { + getLotOccupancyTransactions +} from "../../helpers/lotOccupancyDB/getLotOccupancyTransactions.js"; + + +export const handler: RequestHandler = async (request, response) => { + + const success = deleteLotOccupancyTransaction(request.body.lotOccupancyId, request.body.transactionIndex, request.session); + + const lotOccupancyTransactions = getLotOccupancyTransactions(request.body.lotOccupancyId); + + response.json({ + success, + lotOccupancyTransactions + }); +}; + + +export default handler; \ No newline at end of file diff --git a/handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.js b/handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.js index b7ed4331..0d0ab199 100644 --- a/handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.js +++ b/handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.js @@ -1,10 +1,10 @@ import { updateLotOccupancyOccupant } from "../../helpers/lotOccupancyDB/updateLotOccupancyOccupant.js"; import { getLotOccupancyOccupants } from "../../helpers/lotOccupancyDB/getLotOccupancyOccupants.js"; export const handler = async (request, response) => { - updateLotOccupancyOccupant(request.body, request.session); + const success = updateLotOccupancyOccupant(request.body, request.session); const lotOccupancyOccupants = getLotOccupancyOccupants(request.body.lotOccupancyId); response.json({ - success: true, + success, lotOccupancyOccupants }); }; diff --git a/handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.ts b/handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.ts index 7cd98440..73437ba8 100644 --- a/handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.ts +++ b/handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.ts @@ -13,12 +13,12 @@ import { export const handler: RequestHandler = async (request, response) => { - updateLotOccupancyOccupant(request.body, request.session); + const success = updateLotOccupancyOccupant(request.body, request.session); const lotOccupancyOccupants = getLotOccupancyOccupants(request.body.lotOccupancyId); response.json({ - success: true, + success, lotOccupancyOccupants }); }; diff --git a/helpers/functions.config.d.ts b/helpers/functions.config.d.ts index 4dcb78f7..d863d3b7 100644 --- a/helpers/functions.config.d.ts +++ b/helpers/functions.config.d.ts @@ -23,6 +23,7 @@ export declare function getProperty(propertyName: "aliases.occupancy"): string; export declare function getProperty(propertyName: "aliases.occupancies"): string; export declare function getProperty(propertyName: "aliases.occupant"): string; export declare function getProperty(propertyName: "aliases.occupants"): string; +export declare function getProperty(propertyName: "aliases.externalReceiptNumber"): string; export declare function getProperty(propertyName: "settings.lotOccupancy.occupancyEndDateIsRequired"): boolean; export declare function getProperty(propertyName: "settings.lotOccupancy.occupantCityDefault"): string; export declare function getProperty(propertyName: "settings.lotOccupancy.occupantProvinceDefault"): string; diff --git a/helpers/functions.config.js b/helpers/functions.config.js index b1ba8513..36b4989b 100644 --- a/helpers/functions.config.js +++ b/helpers/functions.config.js @@ -23,6 +23,7 @@ configFallbackValues.set("aliases.occupancy", "Occupancy"); configFallbackValues.set("aliases.occupancies", "Occupancies"); configFallbackValues.set("aliases.occupant", "Occupant"); configFallbackValues.set("aliases.occupants", "Occupants"); +configFallbackValues.set("aliases.externalReceiptNumber", "External Receipt Number"); configFallbackValues.set("settings.lotOccupancy.occupancyEndDateIsRequired", true); configFallbackValues.set("settings.lotOccupancy.occupantCityDefault", ""); configFallbackValues.set("settings.lotOccupancy.occupantProvinceDefault", ""); diff --git a/helpers/functions.config.ts b/helpers/functions.config.ts index c608cb0c..c972b61e 100644 --- a/helpers/functions.config.ts +++ b/helpers/functions.config.ts @@ -41,6 +41,7 @@ configFallbackValues.set("aliases.occupancy", "Occupancy"); configFallbackValues.set("aliases.occupancies", "Occupancies"); configFallbackValues.set("aliases.occupant", "Occupant"); configFallbackValues.set("aliases.occupants", "Occupants"); +configFallbackValues.set("aliases.externalReceiptNumber", "External Receipt Number"); configFallbackValues.set("settings.lotOccupancy.occupancyEndDateIsRequired", true); configFallbackValues.set("settings.lotOccupancy.occupantCityDefault", ""); @@ -82,6 +83,7 @@ export function getProperty(propertyName: "aliases.occupancy"): string; export function getProperty(propertyName: "aliases.occupancies"): string; export function getProperty(propertyName: "aliases.occupant"): string; export function getProperty(propertyName: "aliases.occupants"): string; +export function getProperty(propertyName: "aliases.externalReceiptNumber"): string; export function getProperty(propertyName: "settings.lotOccupancy.occupancyEndDateIsRequired"): boolean; export function getProperty(propertyName: "settings.lotOccupancy.occupantCityDefault"): string; diff --git a/helpers/lotOccupancyDB/addLotOccupancyOccupant.js b/helpers/lotOccupancyDB/addLotOccupancyOccupant.js index 9b9bfd1b..fe609d74 100644 --- a/helpers/lotOccupancyDB/addLotOccupancyOccupant.js +++ b/helpers/lotOccupancyDB/addLotOccupancyOccupant.js @@ -16,7 +16,7 @@ export const addLotOccupancyOccupant = (lotOccupancyOccupantForm, requestSession database .prepare("insert into LotOccupancyOccupants (" + "lotOccupancyId, lotOccupantIndex," + - "occupantName," + + " occupantName," + " occupantAddress1, occupantAddress2," + " occupantCity, occupantProvince, occupantPostalCode," + " occupantPhoneNumber," + diff --git a/helpers/lotOccupancyDB/addLotOccupancyOccupant.ts b/helpers/lotOccupancyDB/addLotOccupancyOccupant.ts index 496c26b2..27e53450 100644 --- a/helpers/lotOccupancyDB/addLotOccupancyOccupant.ts +++ b/helpers/lotOccupancyDB/addLotOccupancyOccupant.ts @@ -43,7 +43,7 @@ export const addLotOccupancyOccupant = database .prepare("insert into LotOccupancyOccupants (" + "lotOccupancyId, lotOccupantIndex," + - "occupantName," + + " occupantName," + " occupantAddress1, occupantAddress2," + " occupantCity, occupantProvince, occupantPostalCode," + " occupantPhoneNumber," + diff --git a/helpers/lotOccupancyDB/addLotOccupancyTransaction.d.ts b/helpers/lotOccupancyDB/addLotOccupancyTransaction.d.ts new file mode 100644 index 00000000..ebb864ba --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancyTransaction.d.ts @@ -0,0 +1,11 @@ +import type * as recordTypes from "../../types/recordTypes"; +interface AddLotOccupancyTransactionForm { + lotOccupancyId: string | number; + transactionDateString?: string; + transactionTimeString?: string; + transactionAmount: string | number; + externalReceiptNumber: string; + transactionNote: string; +} +export declare const addLotOccupancyTransaction: (lotOccupancyTransactionForm: AddLotOccupancyTransactionForm, requestSession: recordTypes.PartialSession) => number; +export default addLotOccupancyTransaction; diff --git a/helpers/lotOccupancyDB/addLotOccupancyTransaction.js b/helpers/lotOccupancyDB/addLotOccupancyTransaction.js new file mode 100644 index 00000000..cf2eb4b0 --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancyTransaction.js @@ -0,0 +1,36 @@ +import { dateStringToInteger, dateToInteger, dateToTimeInteger, timeStringToInteger } from "@cityssm/expressjs-server-js/dateTimeFns.js"; +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const addLotOccupancyTransaction = (lotOccupancyTransactionForm, requestSession) => { + const database = sqlite(databasePath); + let transactionIndex = 0; + const maxIndexResult = database.prepare("select transactionIndex" + + " from LotOccupancyTransactions" + + " where lotOccupancyId = ?" + + " order by transactionIndex" + + " limit 1") + .get(lotOccupancyTransactionForm.lotOccupancyId); + if (maxIndexResult) { + transactionIndex = maxIndexResult.transactionIndex + 1; + } + const rightNow = new Date(); + const transactionDate = lotOccupancyTransactionForm.transactionDateString ? + dateStringToInteger(lotOccupancyTransactionForm.transactionDateString) : + dateToInteger(rightNow); + const transactionTime = lotOccupancyTransactionForm.transactionTimeString ? + timeStringToInteger(lotOccupancyTransactionForm.transactionTimeString) : + dateToTimeInteger(rightNow); + database + .prepare("insert into LotOccupancyTransactions (" + + "lotOccupancyId, transactionIndex," + + " transactionDate, transactionTime," + + " transactionAmount, externalReceiptNumber," + + " transactionNote," + + " recordCreate_userName, recordCreate_timeMillis," + + " recordUpdate_userName, recordUpdate_timeMillis)" + + " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + .run(lotOccupancyTransactionForm.lotOccupancyId, transactionIndex, transactionDate, transactionTime, lotOccupancyTransactionForm.transactionAmount, lotOccupancyTransactionForm.externalReceiptNumber, lotOccupancyTransactionForm.transactionNote, requestSession.user.userName, rightNow.getTime(), requestSession.user.userName, rightNow.getTime()); + database.close(); + return transactionIndex; +}; +export default addLotOccupancyTransaction; diff --git a/helpers/lotOccupancyDB/addLotOccupancyTransaction.ts b/helpers/lotOccupancyDB/addLotOccupancyTransaction.ts new file mode 100644 index 00000000..ca2b6a52 --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancyTransaction.ts @@ -0,0 +1,81 @@ +import { + dateStringToInteger, + dateToInteger, + dateToTimeInteger, + timeStringToInteger +} from "@cityssm/expressjs-server-js/dateTimeFns.js"; +import sqlite from "better-sqlite3"; + +import { + lotOccupancyDB as databasePath +} from "../../data/databasePaths.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +interface AddLotOccupancyTransactionForm { + lotOccupancyId: string | number; + transactionDateString ? : string; + transactionTimeString ? : string; + transactionAmount: string | number; + externalReceiptNumber: string; + transactionNote: string; +} + + +export const addLotOccupancyTransaction = + (lotOccupancyTransactionForm: AddLotOccupancyTransactionForm, requestSession: recordTypes.PartialSession): number => { + + const database = sqlite(databasePath); + + let transactionIndex = 0; + + const maxIndexResult = database.prepare("select transactionIndex" + + " from LotOccupancyTransactions" + + " where lotOccupancyId = ?" + + " order by transactionIndex" + + " limit 1") + .get(lotOccupancyTransactionForm.lotOccupancyId); + + if (maxIndexResult) { + transactionIndex = maxIndexResult.transactionIndex + 1; + } + + const rightNow = new Date(); + + const transactionDate = lotOccupancyTransactionForm.transactionDateString ? + dateStringToInteger(lotOccupancyTransactionForm.transactionDateString) : + dateToInteger(rightNow); + + const transactionTime = lotOccupancyTransactionForm.transactionTimeString ? + timeStringToInteger(lotOccupancyTransactionForm.transactionTimeString) : + dateToTimeInteger(rightNow); + + database + .prepare("insert into LotOccupancyTransactions (" + + "lotOccupancyId, transactionIndex," + + " transactionDate, transactionTime," + + " transactionAmount, externalReceiptNumber," + + " transactionNote," + + " recordCreate_userName, recordCreate_timeMillis," + + " recordUpdate_userName, recordUpdate_timeMillis)" + + " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + .run(lotOccupancyTransactionForm.lotOccupancyId, + transactionIndex, + transactionDate, + transactionTime, + lotOccupancyTransactionForm.transactionAmount, + lotOccupancyTransactionForm.externalReceiptNumber, + lotOccupancyTransactionForm.transactionNote, + requestSession.user.userName, + rightNow.getTime(), + requestSession.user.userName, + rightNow.getTime()); + + database.close(); + + return transactionIndex; + }; + + +export default addLotOccupancyTransaction; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/deleteLotOccupancyTransaction.d.ts b/helpers/lotOccupancyDB/deleteLotOccupancyTransaction.d.ts new file mode 100644 index 00000000..84481caa --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotOccupancyTransaction.d.ts @@ -0,0 +1,3 @@ +import type * as recordTypes from "../../types/recordTypes"; +export declare const deleteLotOccupancyTransaction: (lotOccupancyId: number | string, transactionIndex: number | string, requestSession: recordTypes.PartialSession) => boolean; +export default deleteLotOccupancyTransaction; diff --git a/helpers/lotOccupancyDB/deleteLotOccupancyTransaction.js b/helpers/lotOccupancyDB/deleteLotOccupancyTransaction.js new file mode 100644 index 00000000..85d6f863 --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotOccupancyTransaction.js @@ -0,0 +1,16 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const deleteLotOccupancyTransaction = (lotOccupancyId, transactionIndex, requestSession) => { + const database = sqlite(databasePath); + const rightNowMillis = Date.now(); + const result = database + .prepare("update LotOccupancyTransactions" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?" + + " and transactionIndex = ?") + .run(requestSession.user.userName, rightNowMillis, lotOccupancyId, transactionIndex); + database.close(); + return (result.changes > 0); +}; +export default deleteLotOccupancyTransaction; diff --git a/helpers/lotOccupancyDB/deleteLotOccupancyTransaction.ts b/helpers/lotOccupancyDB/deleteLotOccupancyTransaction.ts new file mode 100644 index 00000000..863d7638 --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotOccupancyTransaction.ts @@ -0,0 +1,36 @@ +import sqlite from "better-sqlite3"; + +import { + lotOccupancyDB as databasePath +} from "../../data/databasePaths.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +export const deleteLotOccupancyTransaction = + (lotOccupancyId: number | string, + transactionIndex: number | string, + requestSession: recordTypes.PartialSession): boolean => { + + const database = sqlite(databasePath); + + const rightNowMillis = Date.now(); + + const result = database + .prepare("update LotOccupancyTransactions" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?" + + " and transactionIndex = ?") + .run(requestSession.user.userName, + rightNowMillis, + lotOccupancyId, + transactionIndex); + + database.close(); + + return (result.changes > 0); + }; + + +export default deleteLotOccupancyTransaction; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8a6b9d61..654a967c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,6 @@ "@types/http-errors": "^1.8.2", "@types/leaflet": "^1.7.11", "@types/mocha": "^9.1.1", - "@types/mssql": "^8.0.3", "@types/node-windows": "^0.1.2", "@types/papaparse": "^5.3.3", "@types/session-file-store": "^1.2.2", @@ -984,17 +983,6 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", "dev": true }, - "node_modules/@types/mssql": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@types/mssql/-/mssql-8.0.3.tgz", - "integrity": "sha512-jh1owWcsq/fdkVFrgNEgDOy4H82PCMF3OmTydfPabC8W4MgCPMTzBSbW7SMNcxz6mizGBkI8h72SwgQz7To64Q==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/tedious": "*", - "tarn": "^3.0.1" - } - }, "node_modules/@types/node": { "version": "18.0.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.3.tgz", @@ -1090,15 +1078,6 @@ "@types/express-session": "*" } }, - "node_modules/@types/tedious": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.8.tgz", - "integrity": "sha512-CWCNlKiX2/fqFb1uiEXTRQ33yqqa0wKP/SDQbHKrPaIfKji4lWAx1Y2jNNjcBFLMh/8MWpQCGseM25M8GTyHvg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/tunnel": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", @@ -11470,17 +11449,6 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", "dev": true }, - "@types/mssql": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@types/mssql/-/mssql-8.0.3.tgz", - "integrity": "sha512-jh1owWcsq/fdkVFrgNEgDOy4H82PCMF3OmTydfPabC8W4MgCPMTzBSbW7SMNcxz6mizGBkI8h72SwgQz7To64Q==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/tedious": "*", - "tarn": "^3.0.1" - } - }, "@types/node": { "version": "18.0.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.3.tgz", @@ -11575,15 +11543,6 @@ "@types/express-session": "*" } }, - "@types/tedious": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.8.tgz", - "integrity": "sha512-CWCNlKiX2/fqFb1uiEXTRQ33yqqa0wKP/SDQbHKrPaIfKji4lWAx1Y2jNNjcBFLMh/8MWpQCGseM25M8GTyHvg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/tunnel": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", diff --git a/package.json b/package.json index 25924190..c3281cee 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,6 @@ "@types/http-errors": "^1.8.2", "@types/leaflet": "^1.7.11", "@types/mocha": "^9.1.1", - "@types/mssql": "^8.0.3", "@types/node-windows": "^0.1.2", "@types/papaparse": "^5.3.3", "@types/session-file-store": "^1.2.2", diff --git a/public-typescript/lotOccupancyEdit.js b/public-typescript/lotOccupancyEdit.js index 264218bc..34f00c6e 100644 --- a/public-typescript/lotOccupancyEdit.js +++ b/public-typescript/lotOccupancyEdit.js @@ -590,6 +590,13 @@ Object.defineProperty(exports, "__esModule", { value: true }); if (!isCreate) { let lotOccupancyFees = exports.lotOccupancyFees; const lotOccupancyFeesContainerElement = document.querySelector("#container--lotOccupancyFees"); + const getFeeGrandTotal = () => { + let feeGrandTotal = 0; + for (const lotOccupancyFee of lotOccupancyFees) { + feeGrandTotal += (lotOccupancyFee.feeAmount + lotOccupancyFee.taxAmount) * lotOccupancyFee.quantity; + } + return feeGrandTotal; + }; const deleteLotOccupancyFee = (clickEvent) => { const feeId = clickEvent.currentTarget.closest(".container--lotOccupancyFee").dataset.feeId; const doDelete = () => { @@ -673,6 +680,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); lotOccupancyFeesContainerElement.querySelector("#lotOccupancyFees--feeAmountTotal").textContent = "$" + feeAmountTotal.toFixed(2); lotOccupancyFeesContainerElement.querySelector("#lotOccupancyFees--taxAmountTotal").textContent = "$" + taxAmountTotal.toFixed(2); lotOccupancyFeesContainerElement.querySelector("#lotOccupancyFees--grandTotal").textContent = "$" + (feeAmountTotal + taxAmountTotal).toFixed(2); + renderLotOccupancyTransactions(); }; document.querySelector("#button--addFee").addEventListener("click", () => { if (hasUnsavedChanges) { @@ -805,6 +813,131 @@ Object.defineProperty(exports, "__esModule", { value: true }); } }); }); + let lotOccupancyTransactions = exports.lotOccupancyTransactions; + const lotOccupancyTransactionsContainerElement = document.querySelector("#container--lotOccupancyTransactions"); + const getTransactionGrandTotal = () => { + let transactionGrandTotal = 0; + for (const lotOccupancyTransaction of lotOccupancyTransactions) { + transactionGrandTotal += lotOccupancyTransaction.transactionAmount; + } + return transactionGrandTotal; + }; + const deleteLotOccupancyTransaction = (clickEvent) => { + const transactionIndex = clickEvent.currentTarget.closest(".container--lotOccupancyTransaction").dataset.transactionIndex; + const doDelete = () => { + cityssm.postJSON(urlPrefix + "/lotOccupancies/doDeleteLotOccupancyTransaction", { + lotOccupancyId, + transactionIndex + }, (responseJSON) => { + if (responseJSON.success) { + lotOccupancyTransactions = responseJSON.lotOccupancyTransactions; + renderLotOccupancyTransactions(); + } + else { + bulmaJS.alert({ + title: "Error Deleting Transaction", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); + }; + bulmaJS.confirm({ + title: "Delete Trasnaction", + message: "Are you sure you want to delete this transaction?", + contextualColorName: "warning", + okButton: { + text: "Yes, Delete Transaction", + callbackFunction: doDelete, + } + }); + }; + const renderLotOccupancyTransactions = () => { + if (lotOccupancyTransactions.length === 0) { + lotOccupancyTransactionsContainerElement.innerHTML = "
" + + "

There are no transactions associated with this record.

" + + "
"; + return; + } + lotOccupancyTransactionsContainerElement.innerHTML = "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ("" + + "" + + "" + + "" + + "") + + "
Date" + cityssm.escapeHTML(exports.aliases.externalReceiptNumber) + "AmountOptions
Transaction Total
"; + let transactionGrandTotal = 0; + for (const lotOccupancyTransaction of lotOccupancyTransactions) { + transactionGrandTotal += lotOccupancyTransaction.transactionAmount; + const tableRowElement = document.createElement("tr"); + tableRowElement.className = "container--lotOccupancyTransaction"; + tableRowElement.dataset.transactionIndex = lotOccupancyTransaction.transactionIndex.toString(); + tableRowElement.innerHTML = "" + lotOccupancyTransaction.transactionDateString + "" + + ("" + + cityssm.escapeHTML(lotOccupancyTransaction.externalReceiptNumber) + "
" + + "" + cityssm.escapeHTML(lotOccupancyTransaction.transactionNote) + "" + + "") + + ("$" + lotOccupancyTransaction.transactionAmount.toFixed(2) + "") + + ("" + + "" + + ""); + tableRowElement.querySelector("button").addEventListener("click", deleteLotOccupancyTransaction); + lotOccupancyTransactionsContainerElement.querySelector("tbody").append(tableRowElement); + } + lotOccupancyTransactionsContainerElement.querySelector("#lotOccupancyTransactions--grandTotal").textContent = "$" + transactionGrandTotal.toFixed(2); + const feeGrandTotal = getFeeGrandTotal(); + if (feeGrandTotal > transactionGrandTotal) { + } + }; + document.querySelector("#button--addTransaction").addEventListener("click", () => { + let addCloseModalFunction; + const doAddTransaction = (submitEvent) => { + submitEvent.preventDefault(); + cityssm.postJSON(urlPrefix + "/lotOccupancies/doAddLotOccupancyTransaction", submitEvent.currentTarget, (responseJSON) => { + if (responseJSON.success) { + lotOccupancyTransactions = responseJSON.lotOccupancyTransactions; + addCloseModalFunction(); + renderLotOccupancyTransactions(); + } + else { + bulmaJS.confirm({ + title: "Error Adding Transaction", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); + }; + cityssm.openHtmlModal("lotOccupancy-addTransaction", { + onshow: (modalElement) => { + los.populateAliases(modalElement); + modalElement.querySelector("#lotOccupancyTransactionAdd--lotOccupancyId").value = lotOccupancyId.toString(); + const feeGrandTotal = getFeeGrandTotal(); + const transactionGrandTotal = getTransactionGrandTotal(); + const transactionAmountElement = modalElement.querySelector("#lotOccupancyTransactionAdd--transactionAmount"); + transactionAmountElement.min = (-1 * transactionGrandTotal).toFixed(2); + transactionAmountElement.max = Math.max(feeGrandTotal - transactionGrandTotal, 0).toFixed(2); + transactionAmountElement.value = Math.max(feeGrandTotal - transactionGrandTotal, 0).toFixed(2); + }, + onshown: (modalElement, closeModalFunction) => { + bulmaJS.toggleHtmlClipped(); + addCloseModalFunction = closeModalFunction; + modalElement.querySelector("form").addEventListener("submit", doAddTransaction); + }, + onremoved: () => { + bulmaJS.toggleHtmlClipped(); + } + }); + }); renderLotOccupancyFees(); } })(); diff --git a/public-typescript/lotOccupancyEdit.ts b/public-typescript/lotOccupancyEdit.ts index e0fd696f..d77ec86c 100644 --- a/public-typescript/lotOccupancyEdit.ts +++ b/public-typescript/lotOccupancyEdit.ts @@ -90,20 +90,22 @@ declare const bulmaJS: BulmaJS; const doDelete = () => { cityssm.postJSON(urlPrefix + "/lotOccupancies/doDeleteLotOccupancy", { - lotOccupancyId - }, - (responseJSON: {success: boolean; errorMessage?: string;}) => { + lotOccupancyId + }, + (responseJSON: { + success: boolean;errorMessage ? : string; + }) => { - if (responseJSON.success) { - window.location.href = urlPrefix + "/lotOccupancies?t=" + Date.now(); - } else { - bulmaJS.alert({ - title: "Error Deleting Record", - message: responseJSON.errorMessage, - contextualColorName: "danger" - }); - } - }); + if (responseJSON.success) { + window.location.href = urlPrefix + "/lotOccupancies?t=" + Date.now(); + } else { + bulmaJS.alert({ + title: "Error Deleting Record", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); } bulmaJS.confirm({ @@ -832,7 +834,7 @@ declare const bulmaJS: BulmaJS; } /* - * Fees + * Fees / Transactions */ if (!isCreate) { @@ -840,6 +842,17 @@ declare const bulmaJS: BulmaJS; let lotOccupancyFees: recordTypes.LotOccupancyFee[] = exports.lotOccupancyFees; const lotOccupancyFeesContainerElement = document.querySelector("#container--lotOccupancyFees") as HTMLElement; + const getFeeGrandTotal = (): number => { + + let feeGrandTotal = 0; + + for (const lotOccupancyFee of lotOccupancyFees) { + feeGrandTotal += (lotOccupancyFee.feeAmount + lotOccupancyFee.taxAmount) * lotOccupancyFee.quantity; + } + + return feeGrandTotal; + }; + const deleteLotOccupancyFee = (clickEvent: Event) => { const feeId = ((clickEvent.currentTarget as HTMLElement).closest(".container--lotOccupancyFee") as HTMLElement).dataset.feeId; @@ -874,7 +887,7 @@ declare const bulmaJS: BulmaJS; text: "Yes, Delete Fee", callbackFunction: doDelete, } - }) + }); }; const renderLotOccupancyFees = () => { @@ -941,6 +954,8 @@ declare const bulmaJS: BulmaJS; (lotOccupancyFeesContainerElement.querySelector("#lotOccupancyFees--feeAmountTotal") as HTMLElement).textContent = "$" + feeAmountTotal.toFixed(2); (lotOccupancyFeesContainerElement.querySelector("#lotOccupancyFees--taxAmountTotal") as HTMLElement).textContent = "$" + taxAmountTotal.toFixed(2); (lotOccupancyFeesContainerElement.querySelector("#lotOccupancyFees--grandTotal") as HTMLElement).textContent = "$" + (feeAmountTotal + taxAmountTotal).toFixed(2); + + renderLotOccupancyTransactions(); }; document.querySelector("#button--addFee").addEventListener("click", () => { @@ -1120,10 +1135,183 @@ declare const bulmaJS: BulmaJS; }); }); + let lotOccupancyTransactions: recordTypes.LotOccupancyTransaction[] = exports.lotOccupancyTransactions; + const lotOccupancyTransactionsContainerElement = document.querySelector("#container--lotOccupancyTransactions") as HTMLElement; + + const getTransactionGrandTotal = (): number => { + + let transactionGrandTotal = 0; + + for (const lotOccupancyTransaction of lotOccupancyTransactions) { + transactionGrandTotal += lotOccupancyTransaction.transactionAmount; + } + + return transactionGrandTotal; + }; + + const deleteLotOccupancyTransaction = (clickEvent: Event) => { + + const transactionIndex = ((clickEvent.currentTarget as HTMLElement).closest(".container--lotOccupancyTransaction") as HTMLElement).dataset.transactionIndex; + + const doDelete = () => { + cityssm.postJSON(urlPrefix + "/lotOccupancies/doDeleteLotOccupancyTransaction", { + lotOccupancyId, + transactionIndex + }, + (responseJSON: { + success: boolean; + errorMessage ? : string; + lotOccupancyTransactions ? : recordTypes.LotOccupancyTransaction[]; + }) => { + + if (responseJSON.success) { + lotOccupancyTransactions = responseJSON.lotOccupancyTransactions; + renderLotOccupancyTransactions(); + } else { + bulmaJS.alert({ + title: "Error Deleting Transaction", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); + }; + + bulmaJS.confirm({ + title: "Delete Trasnaction", + message: "Are you sure you want to delete this transaction?", + contextualColorName: "warning", + okButton: { + text: "Yes, Delete Transaction", + callbackFunction: doDelete, + } + }); + }; + + const renderLotOccupancyTransactions = () => { + + if (lotOccupancyTransactions.length === 0) { + + lotOccupancyTransactionsContainerElement.innerHTML = "
" + + "

There are no transactions associated with this record.

" + + "
"; + + return; + } + + lotOccupancyTransactionsContainerElement.innerHTML = "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ("" + + "" + + "" + + "" + + "") + + "
Date" + cityssm.escapeHTML(exports.aliases.externalReceiptNumber) + "AmountOptions
Transaction Total
"; + + let transactionGrandTotal = 0; + + for (const lotOccupancyTransaction of lotOccupancyTransactions) { + + transactionGrandTotal += lotOccupancyTransaction.transactionAmount; + + const tableRowElement = document.createElement("tr"); + tableRowElement.className = "container--lotOccupancyTransaction"; + tableRowElement.dataset.transactionIndex = lotOccupancyTransaction.transactionIndex.toString(); + + tableRowElement.innerHTML = "" + lotOccupancyTransaction.transactionDateString + "" + + ("" + + cityssm.escapeHTML(lotOccupancyTransaction.externalReceiptNumber) + "
" + + "" + cityssm.escapeHTML(lotOccupancyTransaction.transactionNote) + "" + + "") + + ("$" + lotOccupancyTransaction.transactionAmount.toFixed(2) + "") + + ("" + + "" + + ""); + + tableRowElement.querySelector("button").addEventListener("click", deleteLotOccupancyTransaction); + + lotOccupancyTransactionsContainerElement.querySelector("tbody").append(tableRowElement); + } + + (lotOccupancyTransactionsContainerElement.querySelector("#lotOccupancyTransactions--grandTotal") as HTMLElement).textContent = "$" + transactionGrandTotal.toFixed(2); + + const feeGrandTotal = getFeeGrandTotal(); + + if (feeGrandTotal > transactionGrandTotal) { + + /* + lotOccupancyTransactionsContainerElement.insertAdjacentHTML("afterbegin", + "
{ + + let addCloseModalFunction: () => void; + + const doAddTransaction = (submitEvent: SubmitEvent) => { + + submitEvent.preventDefault(); + + cityssm.postJSON(urlPrefix + "/lotOccupancies/doAddLotOccupancyTransaction", + submitEvent.currentTarget, + (responseJSON: { + success: boolean; + errorMessage ? : string; + lotOccupancyTransactions ? : recordTypes.LotOccupancyTransaction[]; + }) => { + + if (responseJSON.success) { + lotOccupancyTransactions = responseJSON.lotOccupancyTransactions; + addCloseModalFunction(); + renderLotOccupancyTransactions(); + } else { + bulmaJS.confirm({ + title: "Error Adding Transaction", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); + }; + + cityssm.openHtmlModal("lotOccupancy-addTransaction", { + onshow: (modalElement) => { + los.populateAliases(modalElement); + + (modalElement.querySelector("#lotOccupancyTransactionAdd--lotOccupancyId") as HTMLInputElement).value = lotOccupancyId.toString(); + + const feeGrandTotal = getFeeGrandTotal(); + const transactionGrandTotal = getTransactionGrandTotal(); + + const transactionAmountElement = modalElement.querySelector("#lotOccupancyTransactionAdd--transactionAmount") as HTMLInputElement; + + transactionAmountElement.min = (-1 * transactionGrandTotal).toFixed(2); + transactionAmountElement.max = Math.max(feeGrandTotal - transactionGrandTotal, 0).toFixed(2); + transactionAmountElement.value = Math.max(feeGrandTotal - transactionGrandTotal, 0).toFixed(2); + }, + onshown: (modalElement, closeModalFunction) => { + bulmaJS.toggleHtmlClipped(); + + addCloseModalFunction = closeModalFunction; + + modalElement.querySelector("form").addEventListener("submit", doAddTransaction); + }, + onremoved: () => { + bulmaJS.toggleHtmlClipped(); + } + }); + }); + renderLotOccupancyFees(); } - - /* - * Transactions - */ })(); \ No newline at end of file diff --git a/public-typescript/main.js b/public-typescript/main.js index 37fb81bb..74f0be8e 100644 --- a/public-typescript/main.js +++ b/public-typescript/main.js @@ -63,6 +63,9 @@ Object.defineProperty(exports, "__esModule", { value: true }); case "occupant": aliasElement.textContent = exports.aliases.occupant.toLowerCase(); break; + case "ExternalReceiptNumber": + aliasElement.textContent = exports.aliases.externalReceiptNumber; + break; } } }; diff --git a/public-typescript/main.ts b/public-typescript/main.ts index 63064fd9..c81bea34 100644 --- a/public-typescript/main.ts +++ b/public-typescript/main.ts @@ -99,6 +99,10 @@ import type * as globalTypes from "../types/globalTypes"; case "occupant": aliasElement.textContent = exports.aliases.occupant.toLowerCase(); break; + + case "ExternalReceiptNumber": + aliasElement.textContent = exports.aliases.externalReceiptNumber; + break; } } }; diff --git a/public/html/lotOccupancy-addTransaction.html b/public/html/lotOccupancy-addTransaction.html new file mode 100644 index 00000000..9cc6b239 --- /dev/null +++ b/public/html/lotOccupancy-addTransaction.html @@ -0,0 +1,47 @@ + \ No newline at end of file diff --git a/public/javascripts/lotOccupancyEdit.min.js b/public/javascripts/lotOccupancyEdit.min.js index 7984dd5f..a287ace1 100644 --- a/public/javascripts/lotOccupancyEdit.min.js +++ b/public/javascripts/lotOccupancyEdit.min.js @@ -1 +1 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=exports.los,t=document.querySelector("main").dataset.urlPrefix,c=document.querySelector("#lotOccupancy--lotOccupancyId").value,a=""===c;let o=!1,n=a;const s=()=>{o||(o=!0,cityssm.enableNavBlocker())},l=document.querySelector("#form--lotOccupancy");l.addEventListener("submit",e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/"+(a?"doCreateLotOccupancy":"doUpdateLotOccupancy"),l,e=>{e.success?(o=!1,cityssm.disableNavBlocker(),a||n?window.location.href=t+"/lotOccupancies/"+e.lotOccupancyId+"/edit?t="+Date.now():bulmaJS.alert({message:exports.aliases.occupancy+" Updated Successfully",contextualColorName:"success"})):bulmaJS.alert({title:"Error Saving "+exports.aliases.occupancy,message:e.errorMessage,contextualColorName:"danger"})})});const r=l.querySelectorAll("input, select");for(const e of r)e.addEventListener("change",s);a||document.querySelector("#button--deleteLotOccupancy").addEventListener("click",e=>{e.preventDefault();bulmaJS.confirm({title:"Delete "+exports.aliases.occupancy+" Record",message:"Are you sure you want to delete this record?",contextualColorName:"warning",okButton:{text:"Yes, Delete",callbackFunction:()=>{cityssm.postJSON(t+"/lotOccupancies/doDeleteLotOccupancy",{lotOccupancyId:c},e=>{e.success?window.location.href=t+"/lotOccupancies?t="+Date.now():bulmaJS.alert({title:"Error Deleting Record",message:e.errorMessage,contextualColorName:"danger"})})}}})});const u=document.querySelector("#lotOccupancy--occupancyTypeId");if(a){const e=document.querySelector("#container--lotOccupancyFields");u.addEventListener("change",()=>{""!==u.value?cityssm.postJSON(t+"/lotOccupancies/doGetOccupancyTypeFields",{occupancyTypeId:u.value},t=>{if(0===t.occupancyTypeFields.length)return void(e.innerHTML='

There are no additional fields for this '+exports.aliases.occupancy.toLowerCase()+" type.

");e.innerHTML="";let c="";for(const a of t.occupancyTypeFields){c+=","+a.occupancyTypeFieldId;const t=document.createElement("div");t.className="field",t.innerHTML='
',t.querySelector("label").textContent=a.occupancyTypeField;const o=document.createElement("input");o.className="input",o.id="lotOccupancy--lotOccupancyFieldValue_"+a.occupancyTypeFieldId,o.name="lotOccupancyFieldValue_"+a.occupancyTypeFieldId,o.type="text",o.required=a.isRequired,o.minLength=a.minimumLength,o.maxLength=a.maximumLength,a.pattern&&""!==a.pattern&&(o.pattern=a.pattern),t.querySelector(".control").append(o),e.append(t)}e.insertAdjacentHTML("beforeend",'')}):e.innerHTML='

Select the '+exports.aliases.occupancy.toLowerCase()+" type to load the available fields.

"})}else{const e=u.value;u.addEventListener("change",()=>{u.value!==e&&bulmaJS.confirm({title:"Confirm Change",message:"Are you sure you want to change the "+exports.aliases.occupancy.toLowerCase()+" type?\nThis change affects the additional fields associated with this record, and may also affect the available fees.",contextualColorName:"warning",okButton:{text:"Yes, Keep the Change",callbackFunction:()=>{n=!0}},cancelButton:{text:"Revert the Change",callbackFunction:()=>{u.value=e}}})})}if(document.querySelector("#lotOccupancy--lotName").addEventListener("click",c=>{const a=c.currentTarget.value;let o,n,l;const r=e=>{e.preventDefault();const t=e.currentTarget;document.querySelector("#lotOccupancy--lotId").value=t.dataset.lotId,document.querySelector("#lotOccupancy--lotName").value=t.dataset.lotName,s(),o()},u=()=>{l.innerHTML='


Searching...

',cityssm.postJSON(t+"/lots/doSearchLots",n,e=>{if(0===e.count)return void(l.innerHTML='

No results.

');const t=document.createElement("div");t.className="panel";for(const c of e.lots){const e=document.createElement("a");e.className="panel-block is-block",e.href="#",e.dataset.lotId=c.lotId.toString(),e.dataset.lotName=c.lotName,e.innerHTML='
'+cityssm.escapeHTML(c.lotName)+'
'+cityssm.escapeHTML(c.mapName)+'
'+cityssm.escapeHTML(c.lotStatus)+'
'+(c.lotOccupancyCount>0?"Currently Occupied":"")+"
",e.addEventListener("click",r),t.append(e)}l.innerHTML="",l.append(t)})};cityssm.openHtmlModal("lotOccupancy-selectLot",{onshow:t=>{e.populateAliases(t)},onshown:(e,t)=>{bulmaJS.toggleHtmlClipped(),o=t;const c=e.querySelector("#lotSelect--lotName");c.value=a,c.focus(),c.addEventListener("change",u),e.querySelector("#lotSelect--occupancyStatus").addEventListener("change",u),n=e.querySelector("#form--lotSelect"),l=e.querySelector("#resultsContainer--lotSelect"),n.addEventListener("submit",e=>{e.preventDefault()}),u()},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),document.querySelector("#lotOccupancy--occupancyStartDateString").addEventListener("change",()=>{document.querySelector("#lotOccupancy--occupancyEndDateString").min=document.querySelector("#lotOccupancy--occupancyStartDateString").value}),e.initializeUnlockFieldButtons(l),!a){let a=exports.lotOccupancyOccupants;const o=o=>{const n=Number.parseInt(o.currentTarget.closest("tr").dataset.lotOccupantIndex,10),l=a.find(e=>e.lotOccupantIndex===n);let r,u;const i=e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/doUpdateLotOccupancyOccupant",r,e=>{e.success?(a=e.lotOccupancyOccupants,u(),s()):bulmaJS.alert({title:"Error Updating "+exports.aliases.occupant,message:e.errorMessage,contextualColorName:"danger"})})};cityssm.openHtmlModal("lotOccupancy-editOccupant",{onshow:t=>{e.populateAliases(t),t.querySelector("#lotOccupancyOccupantEdit--lotOccupancyId").value=c,t.querySelector("#lotOccupancyOccupantEdit--lotOccupantIndex").value=n.toString();const a=t.querySelector("#lotOccupancyOccupantEdit--lotOccupantTypeId");let o=!1;for(const e of exports.lotOccupantTypes){const t=document.createElement("option");t.value=e.lotOccupantTypeId.toString(),t.textContent=e.lotOccupantType,e.lotOccupantTypeId===l.lotOccupantTypeId&&(t.selected=!0,o=!0),a.append(t)}if(!o){const e=document.createElement("option");e.value=l.lotOccupantTypeId.toString(),e.textContent=l.lotOccupantType,e.selected=!0,a.append(e)}t.querySelector("#lotOccupancyOccupantEdit--occupantName").value=l.occupantName,t.querySelector("#lotOccupancyOccupantEdit--occupantAddress1").value=l.occupantAddress1,t.querySelector("#lotOccupancyOccupantEdit--occupantAddress2").value=l.occupantAddress2,t.querySelector("#lotOccupancyOccupantEdit--occupantCity").value=l.occupantCity,t.querySelector("#lotOccupancyOccupantEdit--occupantProvince").value=l.occupantProvince,t.querySelector("#lotOccupancyOccupantEdit--occupantPostalCode").value=l.occupantPostalCode,t.querySelector("#lotOccupancyOccupantEdit--occupantPhoneNumber").value=l.occupantPhoneNumber},onshown:(e,t)=>{bulmaJS.toggleHtmlClipped(),e.querySelector("#lotOccupancyOccupantEdit--lotOccupantTypeId").focus(),(r=e.querySelector("form")).addEventListener("submit",i),u=t},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})},n=e=>{const o=e.currentTarget.closest("tr").dataset.lotOccupantIndex;bulmaJS.confirm({title:"Remove "+exports.aliases.occupant+"?",message:"Are you sure you want to remove this "+exports.aliases.occupant.toLowerCase()+"?",okButton:{text:"Yes, Remove "+exports.aliases.occupant,callbackFunction:()=>{cityssm.postJSON(t+"/lotOccupancies/doDeleteLotOccupancyOccupant",{lotOccupancyId:c,lotOccupantIndex:o},e=>{e.success?(a=e.lotOccupancyOccupants,s()):bulmaJS.alert({title:"Error Removing "+exports.aliases.occupant,message:e.errorMessage,contextualColorName:"danger"})})}},contextualColorName:"warning"})},s=()=>{const e=document.querySelector("#container--lotOccupancyOccupants");if(cityssm.clearElement(e),0===a.length)return void(e.innerHTML='

There are no '+exports.aliases.occupants.toLowerCase()+" associated with this record.

");const t=document.createElement("table");t.className="table is-fullwidth is-striped is-hoverable",t.innerHTML=""+exports.aliases.occupant+" Type"+exports.aliases.occupant+"AddressPhone Number";for(const e of a){const c=document.createElement("tr");c.dataset.lotOccupantIndex=e.lotOccupantIndex.toString(),c.innerHTML=""+cityssm.escapeHTML(e.lotOccupantType)+""+cityssm.escapeHTML(e.occupantName)+""+cityssm.escapeHTML(e.occupantAddress1)+"
"+(e.occupantAddress2?cityssm.escapeHTML(e.occupantAddress2)+"
":"")+cityssm.escapeHTML(e.occupantCity)+", "+cityssm.escapeHTML(e.occupantProvince)+"
"+cityssm.escapeHTML(e.occupantPostalCode)+""+cityssm.escapeHTML(e.occupantPhoneNumber)+'
',c.querySelector(".button--edit").addEventListener("click",o),c.querySelector(".button--delete").addEventListener("click",n),t.querySelector("tbody").append(c)}e.append(t)};document.querySelector("#button--addOccupant").addEventListener("click",()=>{let o,n;const l=e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/doAddLotOccupancyOccupant",o,e=>{e.success?(a=e.lotOccupancyOccupants,n(),s()):bulmaJS.alert({title:"Error Adding "+exports.aliases.occupant,message:e.errorMessage,contextualColorName:"danger"})})};cityssm.openHtmlModal("lotOccupancy-addOccupant",{onshow:t=>{e.populateAliases(t),t.querySelector("#lotOccupancyOccupantAdd--lotOccupancyId").value=c;const a=t.querySelector("#lotOccupancyOccupantAdd--lotOccupantTypeId");for(const e of exports.lotOccupantTypes){const t=document.createElement("option");t.value=e.lotOccupantTypeId.toString(),t.textContent=e.lotOccupantType,a.append(t)}t.querySelector("#lotOccupancyOccupantAdd--occupantCity").value=exports.occupantCityDefault,t.querySelector("#lotOccupancyOccupantAdd--occupantProvince").value=exports.occupantProvinceDefault},onshown:(e,t)=>{bulmaJS.toggleHtmlClipped(),e.querySelector("#lotOccupancyOccupantAdd--lotOccupantTypeId").focus(),(o=e.querySelector("form")).addEventListener("submit",l),n=t},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),s()}if(!a){let a=exports.lotOccupancyComments;const o=o=>{const n=Number.parseInt(o.currentTarget.closest("tr").dataset.lotOccupancyCommentId,10),l=a.find(e=>e.lotOccupancyCommentId===n);let r,u;const i=e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/doUpdateLotOccupancyComment",r,e=>{e.success?(a=e.lotOccupancyComments,u(),s()):bulmaJS.alert({title:"Error Updating Comment",message:e.errorMessage,contextualColorName:"danger"})})};cityssm.openHtmlModal("lotOccupancy-editComment",{onshow:t=>{e.populateAliases(t),t.querySelector("#lotOccupancyCommentEdit--lotOccupancyId").value=c,t.querySelector("#lotOccupancyCommentEdit--lotOccupancyCommentId").value=n.toString(),t.querySelector("#lotOccupancyCommentEdit--lotOccupancyComment").value=l.lotOccupancyComment,t.querySelector("#lotOccupancyCommentEdit--lotOccupancyCommentDateString").value=l.lotOccupancyCommentDateString,t.querySelector("#lotOccupancyCommentEdit--lotOccupancyCommentTimeString").value=l.lotOccupancyCommentTimeString},onshown:(e,t)=>{bulmaJS.toggleHtmlClipped(),e.querySelector("#lotOccupancyCommentEdit--lotOccupancyComment").focus(),(r=e.querySelector("form")).addEventListener("submit",i),u=t},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})},n=e=>{const o=Number.parseInt(e.currentTarget.closest("tr").dataset.lotOccupancyCommentId,10);bulmaJS.confirm({title:"Remove Comment?",message:"Are you sure you want to remove this comment?",okButton:{text:"Yes, Remove Comment",callbackFunction:()=>{cityssm.postJSON(t+"/lotOccupancies/doDeleteLotOccupancyComment",{lotOccupancyId:c,lotOccupancyCommentId:o},e=>{e.success?(a=e.lotOccupancyComments,s()):bulmaJS.alert({title:"Error Removing Comment",message:e.errorMessage,contextualColorName:"danger"})})}},contextualColorName:"warning"})},s=()=>{const e=document.querySelector("#container--lotOccupancyComments");if(0===a.length)return void(e.innerHTML='

There are no comments associated with this record.

');const t=document.createElement("table");t.className="table is-fullwidth is-striped is-hoverable",t.innerHTML='CommentorComment DateCommentOptions';for(const e of a){const c=document.createElement("tr");c.dataset.lotOccupancyCommentId=e.lotOccupancyCommentId.toString(),c.innerHTML=""+cityssm.escapeHTML(e.recordCreate_userName)+""+e.lotOccupancyCommentDateString+(0===e.lotOccupancyCommentTime?"":" "+e.lotOccupancyCommentTimeString)+""+cityssm.escapeHTML(e.lotOccupancyComment)+'
',c.querySelector(".button--edit").addEventListener("click",o),c.querySelector(".button--delete").addEventListener("click",n),t.querySelector("tbody").append(c)}e.innerHTML="",e.append(t)};document.querySelector("#button--addComment").addEventListener("click",()=>{let o,n;const l=e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/doAddLotOccupancyComment",o,e=>{e.success?(a=e.lotOccupancyComments,n(),s()):bulmaJS.alert({title:"Error Adding Comment",message:e.errorMessage,contextualColorName:"danger"})})};cityssm.openHtmlModal("lotOccupancy-addComment",{onshow:t=>{e.populateAliases(t),t.querySelector("#lotOccupancyCommentAdd--lotOccupancyId").value=c},onshown:(e,t)=>{bulmaJS.toggleHtmlClipped(),e.querySelector("#lotOccupancyCommentAdd--lotOccupancyComment").focus(),(o=e.querySelector("form")).addEventListener("submit",l),n=t},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),s()}if(!a){let e=exports.lotOccupancyFees;const a=document.querySelector("#container--lotOccupancyFees"),n=a=>{const o=a.currentTarget.closest(".container--lotOccupancyFee").dataset.feeId;bulmaJS.confirm({title:"Delete Fee",message:"Are you sure you want to delete this fee?",contextualColorName:"warning",okButton:{text:"Yes, Delete Fee",callbackFunction:()=>{cityssm.postJSON(t+"/lotOccupancies/doDeleteLotOccupancyFee",{lotOccupancyId:c,feeId:o},t=>{t.success?(e=t.lotOccupancyFees,s()):bulmaJS.alert({title:"Error Deleting Fee",message:t.errorMessage,contextualColorName:"danger"})})}}})},s=()=>{if(0===e.length)return void(a.innerHTML='

There are no fees associated with this record.

');a.innerHTML='
FeeUnit Cost×QuantityequalsTotalOptions
Subtotal
Tax
Grand Total
';let t=0,c=0;for(const o of e){const e=document.createElement("tr");e.className="container--lotOccupancyFee",e.dataset.feeId=o.feeId.toString(),e.innerHTML=''+cityssm.escapeHTML(o.feeName)+""+(1===o.quantity?"":'$'+o.feeAmount.toFixed(2)+'×'+o.quantity+"=")+'$'+(o.feeAmount*o.quantity).toFixed(2)+'',e.querySelector("button").addEventListener("click",n),a.querySelector("tbody").append(e),t+=o.feeAmount*o.quantity,c+=o.taxAmount*o.quantity}a.querySelector("#lotOccupancyFees--feeAmountTotal").textContent="$"+t.toFixed(2),a.querySelector("#lotOccupancyFees--taxAmountTotal").textContent="$"+c.toFixed(2),a.querySelector("#lotOccupancyFees--grandTotal").textContent="$"+(t+c).toFixed(2)};document.querySelector("#button--addFee").addEventListener("click",()=>{if(o)return void bulmaJS.alert({message:"Please save all unsaved changes before adding fees.",contextualColorName:"warning"});let n,l,r;const u=(a,o=1)=>{cityssm.postJSON(t+"/lotOccupancies/doAddLotOccupancyFee",{lotOccupancyId:c,feeId:a,quantity:o},t=>{t.success?(e=t.lotOccupancyFees,s(),d()):bulmaJS.alert({title:"Error Adding Fee",message:t.errorMessage,contextualColorName:"danger"})})},i=e=>{e.preventDefault();const t=Number.parseInt(e.currentTarget.dataset.feeId,10),c=Number.parseInt(e.currentTarget.closest(".container--feeCategory").dataset.feeCategoryId,10),a=n.find(e=>e.feeCategoryId===c).fees.find(e=>e.feeId===t);a.includeQuantity?(e=>{let t,c;const a=a=>{a.preventDefault(),u(e.feeId,t.value),c()};cityssm.openHtmlModal("lotOccupancy-setFeeQuantity",{onshow:t=>{t.querySelector("#lotOccupancyFeeQuantity--quantityUnit").textContent=e.quantityUnit},onshown:(e,o)=>{c=o,t=e.querySelector("#lotOccupancyFeeQuantity--quantity"),e.querySelector("form").addEventListener("submit",a)}})})(a):u(t)},d=()=>{const e=l.value.trim().toLowerCase().split(" ");r.innerHTML="";for(const t of n){const c=document.createElement("div");c.className="container--feeCategory",c.dataset.feeCategoryId=t.feeCategoryId.toString(),c.innerHTML='

'+cityssm.escapeHTML(t.feeCategory)+'

';let o=!1;for(const n of t.fees){if(a.querySelector(".container--lotOccupancyFee[data-fee-id='"+n.feeId+"']"))continue;let t=!0;for(const c of e)if(!n.feeName.toLowerCase().includes(c)){t=!1;break}if(!t)continue;o=!0;const s=document.createElement("a");s.className="panel-block is-block container--fee",s.dataset.feeId=n.feeId.toString(),s.href="#",s.innerHTML=""+cityssm.escapeHTML(n.feeName)+"
"+cityssm.escapeHTML(n.feeDescription).replace(/\n/g,"
")+"
",s.addEventListener("click",i),c.querySelector(".panel").append(s)}o&&r.append(c)}};cityssm.openHtmlModal("lotOccupancy-addFee",{onshow:e=>{l=e.querySelector("#feeSelect--feeName"),r=e.querySelector("#resultsContainer--feeSelect"),cityssm.postJSON(t+"/lotOccupancies/doGetFees",{lotOccupancyId:c},e=>{n=e.feeCategories,l.disabled=!1,l.addEventListener("keyup",d),l.focus(),d()})},onshown:()=>{bulmaJS.toggleHtmlClipped()},onhidden:()=>{s()},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),s()}})(); \ No newline at end of file +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const t=exports.los,e=document.querySelector("main").dataset.urlPrefix,c=document.querySelector("#lotOccupancy--lotOccupancyId").value,a=""===c;let o=!1,n=a;const s=()=>{o||(o=!0,cityssm.enableNavBlocker())},l=document.querySelector("#form--lotOccupancy");l.addEventListener("submit",t=>{t.preventDefault(),cityssm.postJSON(e+"/lotOccupancies/"+(a?"doCreateLotOccupancy":"doUpdateLotOccupancy"),l,t=>{t.success?(o=!1,cityssm.disableNavBlocker(),a||n?window.location.href=e+"/lotOccupancies/"+t.lotOccupancyId+"/edit?t="+Date.now():bulmaJS.alert({message:exports.aliases.occupancy+" Updated Successfully",contextualColorName:"success"})):bulmaJS.alert({title:"Error Saving "+exports.aliases.occupancy,message:t.errorMessage,contextualColorName:"danger"})})});const r=l.querySelectorAll("input, select");for(const t of r)t.addEventListener("change",s);a||document.querySelector("#button--deleteLotOccupancy").addEventListener("click",t=>{t.preventDefault();bulmaJS.confirm({title:"Delete "+exports.aliases.occupancy+" Record",message:"Are you sure you want to delete this record?",contextualColorName:"warning",okButton:{text:"Yes, Delete",callbackFunction:()=>{cityssm.postJSON(e+"/lotOccupancies/doDeleteLotOccupancy",{lotOccupancyId:c},t=>{t.success?window.location.href=e+"/lotOccupancies?t="+Date.now():bulmaJS.alert({title:"Error Deleting Record",message:t.errorMessage,contextualColorName:"danger"})})}}})});const u=document.querySelector("#lotOccupancy--occupancyTypeId");if(a){const t=document.querySelector("#container--lotOccupancyFields");u.addEventListener("change",()=>{""!==u.value?cityssm.postJSON(e+"/lotOccupancies/doGetOccupancyTypeFields",{occupancyTypeId:u.value},e=>{if(0===e.occupancyTypeFields.length)return void(t.innerHTML='

There are no additional fields for this '+exports.aliases.occupancy.toLowerCase()+" type.

");t.innerHTML="";let c="";for(const a of e.occupancyTypeFields){c+=","+a.occupancyTypeFieldId;const e=document.createElement("div");e.className="field",e.innerHTML='
',e.querySelector("label").textContent=a.occupancyTypeField;const o=document.createElement("input");o.className="input",o.id="lotOccupancy--lotOccupancyFieldValue_"+a.occupancyTypeFieldId,o.name="lotOccupancyFieldValue_"+a.occupancyTypeFieldId,o.type="text",o.required=a.isRequired,o.minLength=a.minimumLength,o.maxLength=a.maximumLength,a.pattern&&""!==a.pattern&&(o.pattern=a.pattern),e.querySelector(".control").append(o),t.append(e)}t.insertAdjacentHTML("beforeend",'')}):t.innerHTML='

Select the '+exports.aliases.occupancy.toLowerCase()+" type to load the available fields.

"})}else{const t=u.value;u.addEventListener("change",()=>{u.value!==t&&bulmaJS.confirm({title:"Confirm Change",message:"Are you sure you want to change the "+exports.aliases.occupancy.toLowerCase()+" type?\nThis change affects the additional fields associated with this record, and may also affect the available fees.",contextualColorName:"warning",okButton:{text:"Yes, Keep the Change",callbackFunction:()=>{n=!0}},cancelButton:{text:"Revert the Change",callbackFunction:()=>{u.value=t}}})})}if(document.querySelector("#lotOccupancy--lotName").addEventListener("click",c=>{const a=c.currentTarget.value;let o,n,l;const r=t=>{t.preventDefault();const e=t.currentTarget;document.querySelector("#lotOccupancy--lotId").value=e.dataset.lotId,document.querySelector("#lotOccupancy--lotName").value=e.dataset.lotName,s(),o()},u=()=>{l.innerHTML='


Searching...

',cityssm.postJSON(e+"/lots/doSearchLots",n,t=>{if(0===t.count)return void(l.innerHTML='

No results.

');const e=document.createElement("div");e.className="panel";for(const c of t.lots){const t=document.createElement("a");t.className="panel-block is-block",t.href="#",t.dataset.lotId=c.lotId.toString(),t.dataset.lotName=c.lotName,t.innerHTML='
'+cityssm.escapeHTML(c.lotName)+'
'+cityssm.escapeHTML(c.mapName)+'
'+cityssm.escapeHTML(c.lotStatus)+'
'+(c.lotOccupancyCount>0?"Currently Occupied":"")+"
",t.addEventListener("click",r),e.append(t)}l.innerHTML="",l.append(e)})};cityssm.openHtmlModal("lotOccupancy-selectLot",{onshow:e=>{t.populateAliases(e)},onshown:(t,e)=>{bulmaJS.toggleHtmlClipped(),o=e;const c=t.querySelector("#lotSelect--lotName");c.value=a,c.focus(),c.addEventListener("change",u),t.querySelector("#lotSelect--occupancyStatus").addEventListener("change",u),n=t.querySelector("#form--lotSelect"),l=t.querySelector("#resultsContainer--lotSelect"),n.addEventListener("submit",t=>{t.preventDefault()}),u()},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),document.querySelector("#lotOccupancy--occupancyStartDateString").addEventListener("change",()=>{document.querySelector("#lotOccupancy--occupancyEndDateString").min=document.querySelector("#lotOccupancy--occupancyStartDateString").value}),t.initializeUnlockFieldButtons(l),!a){let a=exports.lotOccupancyOccupants;const o=o=>{const n=Number.parseInt(o.currentTarget.closest("tr").dataset.lotOccupantIndex,10),l=a.find(t=>t.lotOccupantIndex===n);let r,u;const i=t=>{t.preventDefault(),cityssm.postJSON(e+"/lotOccupancies/doUpdateLotOccupancyOccupant",r,t=>{t.success?(a=t.lotOccupancyOccupants,u(),s()):bulmaJS.alert({title:"Error Updating "+exports.aliases.occupant,message:t.errorMessage,contextualColorName:"danger"})})};cityssm.openHtmlModal("lotOccupancy-editOccupant",{onshow:e=>{t.populateAliases(e),e.querySelector("#lotOccupancyOccupantEdit--lotOccupancyId").value=c,e.querySelector("#lotOccupancyOccupantEdit--lotOccupantIndex").value=n.toString();const a=e.querySelector("#lotOccupancyOccupantEdit--lotOccupantTypeId");let o=!1;for(const t of exports.lotOccupantTypes){const e=document.createElement("option");e.value=t.lotOccupantTypeId.toString(),e.textContent=t.lotOccupantType,t.lotOccupantTypeId===l.lotOccupantTypeId&&(e.selected=!0,o=!0),a.append(e)}if(!o){const t=document.createElement("option");t.value=l.lotOccupantTypeId.toString(),t.textContent=l.lotOccupantType,t.selected=!0,a.append(t)}e.querySelector("#lotOccupancyOccupantEdit--occupantName").value=l.occupantName,e.querySelector("#lotOccupancyOccupantEdit--occupantAddress1").value=l.occupantAddress1,e.querySelector("#lotOccupancyOccupantEdit--occupantAddress2").value=l.occupantAddress2,e.querySelector("#lotOccupancyOccupantEdit--occupantCity").value=l.occupantCity,e.querySelector("#lotOccupancyOccupantEdit--occupantProvince").value=l.occupantProvince,e.querySelector("#lotOccupancyOccupantEdit--occupantPostalCode").value=l.occupantPostalCode,e.querySelector("#lotOccupancyOccupantEdit--occupantPhoneNumber").value=l.occupantPhoneNumber},onshown:(t,e)=>{bulmaJS.toggleHtmlClipped(),t.querySelector("#lotOccupancyOccupantEdit--lotOccupantTypeId").focus(),(r=t.querySelector("form")).addEventListener("submit",i),u=e},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})},n=t=>{const o=t.currentTarget.closest("tr").dataset.lotOccupantIndex;bulmaJS.confirm({title:"Remove "+exports.aliases.occupant+"?",message:"Are you sure you want to remove this "+exports.aliases.occupant.toLowerCase()+"?",okButton:{text:"Yes, Remove "+exports.aliases.occupant,callbackFunction:()=>{cityssm.postJSON(e+"/lotOccupancies/doDeleteLotOccupancyOccupant",{lotOccupancyId:c,lotOccupantIndex:o},t=>{t.success?(a=t.lotOccupancyOccupants,s()):bulmaJS.alert({title:"Error Removing "+exports.aliases.occupant,message:t.errorMessage,contextualColorName:"danger"})})}},contextualColorName:"warning"})},s=()=>{const t=document.querySelector("#container--lotOccupancyOccupants");if(cityssm.clearElement(t),0===a.length)return void(t.innerHTML='

There are no '+exports.aliases.occupants.toLowerCase()+" associated with this record.

");const e=document.createElement("table");e.className="table is-fullwidth is-striped is-hoverable",e.innerHTML=""+exports.aliases.occupant+" Type"+exports.aliases.occupant+"AddressPhone Number";for(const t of a){const c=document.createElement("tr");c.dataset.lotOccupantIndex=t.lotOccupantIndex.toString(),c.innerHTML=""+cityssm.escapeHTML(t.lotOccupantType)+""+cityssm.escapeHTML(t.occupantName)+""+cityssm.escapeHTML(t.occupantAddress1)+"
"+(t.occupantAddress2?cityssm.escapeHTML(t.occupantAddress2)+"
":"")+cityssm.escapeHTML(t.occupantCity)+", "+cityssm.escapeHTML(t.occupantProvince)+"
"+cityssm.escapeHTML(t.occupantPostalCode)+""+cityssm.escapeHTML(t.occupantPhoneNumber)+'
',c.querySelector(".button--edit").addEventListener("click",o),c.querySelector(".button--delete").addEventListener("click",n),e.querySelector("tbody").append(c)}t.append(e)};document.querySelector("#button--addOccupant").addEventListener("click",()=>{let o,n;const l=t=>{t.preventDefault(),cityssm.postJSON(e+"/lotOccupancies/doAddLotOccupancyOccupant",o,t=>{t.success?(a=t.lotOccupancyOccupants,n(),s()):bulmaJS.alert({title:"Error Adding "+exports.aliases.occupant,message:t.errorMessage,contextualColorName:"danger"})})};cityssm.openHtmlModal("lotOccupancy-addOccupant",{onshow:e=>{t.populateAliases(e),e.querySelector("#lotOccupancyOccupantAdd--lotOccupancyId").value=c;const a=e.querySelector("#lotOccupancyOccupantAdd--lotOccupantTypeId");for(const t of exports.lotOccupantTypes){const e=document.createElement("option");e.value=t.lotOccupantTypeId.toString(),e.textContent=t.lotOccupantType,a.append(e)}e.querySelector("#lotOccupancyOccupantAdd--occupantCity").value=exports.occupantCityDefault,e.querySelector("#lotOccupancyOccupantAdd--occupantProvince").value=exports.occupantProvinceDefault},onshown:(t,e)=>{bulmaJS.toggleHtmlClipped(),t.querySelector("#lotOccupancyOccupantAdd--lotOccupantTypeId").focus(),(o=t.querySelector("form")).addEventListener("submit",l),n=e},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),s()}if(!a){let a=exports.lotOccupancyComments;const o=o=>{const n=Number.parseInt(o.currentTarget.closest("tr").dataset.lotOccupancyCommentId,10),l=a.find(t=>t.lotOccupancyCommentId===n);let r,u;const i=t=>{t.preventDefault(),cityssm.postJSON(e+"/lotOccupancies/doUpdateLotOccupancyComment",r,t=>{t.success?(a=t.lotOccupancyComments,u(),s()):bulmaJS.alert({title:"Error Updating Comment",message:t.errorMessage,contextualColorName:"danger"})})};cityssm.openHtmlModal("lotOccupancy-editComment",{onshow:e=>{t.populateAliases(e),e.querySelector("#lotOccupancyCommentEdit--lotOccupancyId").value=c,e.querySelector("#lotOccupancyCommentEdit--lotOccupancyCommentId").value=n.toString(),e.querySelector("#lotOccupancyCommentEdit--lotOccupancyComment").value=l.lotOccupancyComment,e.querySelector("#lotOccupancyCommentEdit--lotOccupancyCommentDateString").value=l.lotOccupancyCommentDateString,e.querySelector("#lotOccupancyCommentEdit--lotOccupancyCommentTimeString").value=l.lotOccupancyCommentTimeString},onshown:(t,e)=>{bulmaJS.toggleHtmlClipped(),t.querySelector("#lotOccupancyCommentEdit--lotOccupancyComment").focus(),(r=t.querySelector("form")).addEventListener("submit",i),u=e},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})},n=t=>{const o=Number.parseInt(t.currentTarget.closest("tr").dataset.lotOccupancyCommentId,10);bulmaJS.confirm({title:"Remove Comment?",message:"Are you sure you want to remove this comment?",okButton:{text:"Yes, Remove Comment",callbackFunction:()=>{cityssm.postJSON(e+"/lotOccupancies/doDeleteLotOccupancyComment",{lotOccupancyId:c,lotOccupancyCommentId:o},t=>{t.success?(a=t.lotOccupancyComments,s()):bulmaJS.alert({title:"Error Removing Comment",message:t.errorMessage,contextualColorName:"danger"})})}},contextualColorName:"warning"})},s=()=>{const t=document.querySelector("#container--lotOccupancyComments");if(0===a.length)return void(t.innerHTML='

There are no comments associated with this record.

');const e=document.createElement("table");e.className="table is-fullwidth is-striped is-hoverable",e.innerHTML='CommentorComment DateCommentOptions';for(const t of a){const c=document.createElement("tr");c.dataset.lotOccupancyCommentId=t.lotOccupancyCommentId.toString(),c.innerHTML=""+cityssm.escapeHTML(t.recordCreate_userName)+""+t.lotOccupancyCommentDateString+(0===t.lotOccupancyCommentTime?"":" "+t.lotOccupancyCommentTimeString)+""+cityssm.escapeHTML(t.lotOccupancyComment)+'
',c.querySelector(".button--edit").addEventListener("click",o),c.querySelector(".button--delete").addEventListener("click",n),e.querySelector("tbody").append(c)}t.innerHTML="",t.append(e)};document.querySelector("#button--addComment").addEventListener("click",()=>{let o,n;const l=t=>{t.preventDefault(),cityssm.postJSON(e+"/lotOccupancies/doAddLotOccupancyComment",o,t=>{t.success?(a=t.lotOccupancyComments,n(),s()):bulmaJS.alert({title:"Error Adding Comment",message:t.errorMessage,contextualColorName:"danger"})})};cityssm.openHtmlModal("lotOccupancy-addComment",{onshow:e=>{t.populateAliases(e),e.querySelector("#lotOccupancyCommentAdd--lotOccupancyId").value=c},onshown:(t,e)=>{bulmaJS.toggleHtmlClipped(),t.querySelector("#lotOccupancyCommentAdd--lotOccupancyComment").focus(),(o=t.querySelector("form")).addEventListener("submit",l),n=e},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),s()}if(!a){let a=exports.lotOccupancyFees;const n=document.querySelector("#container--lotOccupancyFees"),s=()=>{let t=0;for(const e of a)t+=(e.feeAmount+e.taxAmount)*e.quantity;return t},l=t=>{const o=t.currentTarget.closest(".container--lotOccupancyFee").dataset.feeId;bulmaJS.confirm({title:"Delete Fee",message:"Are you sure you want to delete this fee?",contextualColorName:"warning",okButton:{text:"Yes, Delete Fee",callbackFunction:()=>{cityssm.postJSON(e+"/lotOccupancies/doDeleteLotOccupancyFee",{lotOccupancyId:c,feeId:o},t=>{t.success?(a=t.lotOccupancyFees,r()):bulmaJS.alert({title:"Error Deleting Fee",message:t.errorMessage,contextualColorName:"danger"})})}}})},r=()=>{if(0===a.length)return void(n.innerHTML='

There are no fees associated with this record.

');n.innerHTML='
FeeUnit Cost×QuantityequalsTotalOptions
Subtotal
Tax
Grand Total
';let t=0,e=0;for(const c of a){const a=document.createElement("tr");a.className="container--lotOccupancyFee",a.dataset.feeId=c.feeId.toString(),a.innerHTML=''+cityssm.escapeHTML(c.feeName)+""+(1===c.quantity?"":'$'+c.feeAmount.toFixed(2)+'×'+c.quantity+"=")+'$'+(c.feeAmount*c.quantity).toFixed(2)+'',a.querySelector("button").addEventListener("click",l),n.querySelector("tbody").append(a),t+=c.feeAmount*c.quantity,e+=c.taxAmount*c.quantity}n.querySelector("#lotOccupancyFees--feeAmountTotal").textContent="$"+t.toFixed(2),n.querySelector("#lotOccupancyFees--taxAmountTotal").textContent="$"+e.toFixed(2),n.querySelector("#lotOccupancyFees--grandTotal").textContent="$"+(t+e).toFixed(2),m()};document.querySelector("#button--addFee").addEventListener("click",()=>{if(o)return void bulmaJS.alert({message:"Please save all unsaved changes before adding fees.",contextualColorName:"warning"});let t,s,l;const u=(t,o=1)=>{cityssm.postJSON(e+"/lotOccupancies/doAddLotOccupancyFee",{lotOccupancyId:c,feeId:t,quantity:o},t=>{t.success?(a=t.lotOccupancyFees,r(),d()):bulmaJS.alert({title:"Error Adding Fee",message:t.errorMessage,contextualColorName:"danger"})})},i=e=>{e.preventDefault();const c=Number.parseInt(e.currentTarget.dataset.feeId,10),a=Number.parseInt(e.currentTarget.closest(".container--feeCategory").dataset.feeCategoryId,10),o=t.find(t=>t.feeCategoryId===a).fees.find(t=>t.feeId===c);o.includeQuantity?(t=>{let e,c;const a=a=>{a.preventDefault(),u(t.feeId,e.value),c()};cityssm.openHtmlModal("lotOccupancy-setFeeQuantity",{onshow:e=>{e.querySelector("#lotOccupancyFeeQuantity--quantityUnit").textContent=t.quantityUnit},onshown:(t,o)=>{c=o,e=t.querySelector("#lotOccupancyFeeQuantity--quantity"),t.querySelector("form").addEventListener("submit",a)}})})(o):u(c)},d=()=>{const e=s.value.trim().toLowerCase().split(" ");l.innerHTML="";for(const c of t){const t=document.createElement("div");t.className="container--feeCategory",t.dataset.feeCategoryId=c.feeCategoryId.toString(),t.innerHTML='

'+cityssm.escapeHTML(c.feeCategory)+'

';let a=!1;for(const o of c.fees){if(n.querySelector(".container--lotOccupancyFee[data-fee-id='"+o.feeId+"']"))continue;let c=!0;for(const t of e)if(!o.feeName.toLowerCase().includes(t)){c=!1;break}if(!c)continue;a=!0;const s=document.createElement("a");s.className="panel-block is-block container--fee",s.dataset.feeId=o.feeId.toString(),s.href="#",s.innerHTML=""+cityssm.escapeHTML(o.feeName)+"
"+cityssm.escapeHTML(o.feeDescription).replace(/\n/g,"
")+"
",s.addEventListener("click",i),t.querySelector(".panel").append(s)}a&&l.append(t)}};cityssm.openHtmlModal("lotOccupancy-addFee",{onshow:a=>{s=a.querySelector("#feeSelect--feeName"),l=a.querySelector("#resultsContainer--feeSelect"),cityssm.postJSON(e+"/lotOccupancies/doGetFees",{lotOccupancyId:c},e=>{t=e.feeCategories,s.disabled=!1,s.addEventListener("keyup",d),s.focus(),d()})},onshown:()=>{bulmaJS.toggleHtmlClipped()},onhidden:()=>{r()},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})});let u=exports.lotOccupancyTransactions;const i=document.querySelector("#container--lotOccupancyTransactions"),d=()=>{let t=0;for(const e of u)t+=e.transactionAmount;return t},p=t=>{const a=t.currentTarget.closest(".container--lotOccupancyTransaction").dataset.transactionIndex;bulmaJS.confirm({title:"Delete Trasnaction",message:"Are you sure you want to delete this transaction?",contextualColorName:"warning",okButton:{text:"Yes, Delete Transaction",callbackFunction:()=>{cityssm.postJSON(e+"/lotOccupancies/doDeleteLotOccupancyTransaction",{lotOccupancyId:c,transactionIndex:a},t=>{t.success?(u=t.lotOccupancyTransactions,m()):bulmaJS.alert({title:"Error Deleting Transaction",message:t.errorMessage,contextualColorName:"danger"})})}}})},m=()=>{if(0===u.length)return void(i.innerHTML='

There are no transactions associated with this record.

');i.innerHTML='
Date'+cityssm.escapeHTML(exports.aliases.externalReceiptNumber)+'AmountOptions
Transaction Total
';let t=0;for(const e of u){t+=e.transactionAmount;const c=document.createElement("tr");c.className="container--lotOccupancyTransaction",c.dataset.transactionIndex=e.transactionIndex.toString(),c.innerHTML=""+e.transactionDateString+""+cityssm.escapeHTML(e.externalReceiptNumber)+"
"+cityssm.escapeHTML(e.transactionNote)+'$'+e.transactionAmount.toFixed(2)+'',c.querySelector("button").addEventListener("click",p),i.querySelector("tbody").append(c)}i.querySelector("#lotOccupancyTransactions--grandTotal").textContent="$"+t.toFixed(2);s()};document.querySelector("#button--addTransaction").addEventListener("click",()=>{let a;const o=t=>{t.preventDefault(),cityssm.postJSON(e+"/lotOccupancies/doAddLotOccupancyTransaction",t.currentTarget,t=>{t.success?(u=t.lotOccupancyTransactions,a(),m()):bulmaJS.confirm({title:"Error Adding Transaction",message:t.errorMessage,contextualColorName:"danger"})})};cityssm.openHtmlModal("lotOccupancy-addTransaction",{onshow:e=>{t.populateAliases(e),e.querySelector("#lotOccupancyTransactionAdd--lotOccupancyId").value=c.toString();const a=s(),o=d(),n=e.querySelector("#lotOccupancyTransactionAdd--transactionAmount");n.min=(-1*o).toFixed(2),n.max=Math.max(a-o,0).toFixed(2),n.value=Math.max(a-o,0).toFixed(2)},onshown:(t,e)=>{bulmaJS.toggleHtmlClipped(),a=e,t.querySelector("form").addEventListener("submit",o)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),r()}})(); \ No newline at end of file diff --git a/public/javascripts/main.min.js b/public/javascripts/main.min.js index 218d8bc3..aa2f217d 100644 --- a/public/javascripts/main.min.js +++ b/public/javascripts/main.min.js @@ -1 +1 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=e=>{const t=e.currentTarget.closest(".field").querySelector("input, select");if("INPUT"===t.tagName)t.disabled=!1;else{const e=t.querySelectorAll("option");for(const t of e)t.disabled=!1}t.focus()},t={highlightMap:(e,t,o)=>{let s,l=t;for(;!(s=e.querySelector("#"+l))&&l.includes("-");)l=l.slice(0,Math.max(0,l.lastIndexOf("-"))),console.log(l);if(s){s.style.fill=null,s.classList.add("highlight","is-"+o);const e=s.querySelectorAll("path");for(const t of e)t.style.fill=null}},initializeUnlockFieldButtons:t=>{const o=t.querySelectorAll(".is-unlock-field-button");for(const t of o)t.addEventListener("click",e)},populateAliases:e=>{const t=e.querySelectorAll(".alias");for(const e of t)switch(e.dataset.alias){case"Lot":e.textContent=exports.aliases.lot;break;case"lot":e.textContent=exports.aliases.lot.toLowerCase();break;case"Occupancy":e.textContent=exports.aliases.occupancy;break;case"occupancy":e.textContent=exports.aliases.occupancy.toLowerCase();break;case"Occupant":e.textContent=exports.aliases.occupant;break;case"occupant":e.textContent=exports.aliases.occupant.toLowerCase()}}};exports.los=t})(); \ No newline at end of file +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=e=>{const t=e.currentTarget.closest(".field").querySelector("input, select");if("INPUT"===t.tagName)t.disabled=!1;else{const e=t.querySelectorAll("option");for(const t of e)t.disabled=!1}t.focus()},t={highlightMap:(e,t,s)=>{let o,l=t;for(;!(o=e.querySelector("#"+l))&&l.includes("-");)l=l.slice(0,Math.max(0,l.lastIndexOf("-"))),console.log(l);if(o){o.style.fill=null,o.classList.add("highlight","is-"+s);const e=o.querySelectorAll("path");for(const t of e)t.style.fill=null}},initializeUnlockFieldButtons:t=>{const s=t.querySelectorAll(".is-unlock-field-button");for(const t of s)t.addEventListener("click",e)},populateAliases:e=>{const t=e.querySelectorAll(".alias");for(const e of t)switch(e.dataset.alias){case"Lot":e.textContent=exports.aliases.lot;break;case"lot":e.textContent=exports.aliases.lot.toLowerCase();break;case"Occupancy":e.textContent=exports.aliases.occupancy;break;case"occupancy":e.textContent=exports.aliases.occupancy.toLowerCase();break;case"Occupant":e.textContent=exports.aliases.occupant;break;case"occupant":e.textContent=exports.aliases.occupant.toLowerCase();break;case"ExternalReceiptNumber":e.textContent=exports.aliases.externalReceiptNumber}}};exports.los=t})(); \ No newline at end of file diff --git a/routes/lotOccupancies.js b/routes/lotOccupancies.js index 0846dcfe..3704d4aa 100644 --- a/routes/lotOccupancies.js +++ b/routes/lotOccupancies.js @@ -17,6 +17,8 @@ import handler_doDeleteLotOccupancyComment from "../handlers/lotOccupancies-post import handler_doGetFees from "../handlers/lotOccupancies-post/doGetFees.js"; import handler_doAddLotOccupancyFee from "../handlers/lotOccupancies-post/doAddLotOccupancyFee.js"; import handler_doDeleteLotOccupancyFee from "../handlers/lotOccupancies-post/doDeleteLotOccupancyFee.js"; +import handler_doAddLotOccupancyTransaction from "../handlers/lotOccupancies-post/doAddLotOccupancyTransaction.js"; +import handler_doDeleteLotOccupancyTransaction from "../handlers/lotOccupancies-post/doDeleteLotOccupancyTransaction.js"; import * as permissionHandlers from "../handlers/permissions.js"; export const router = Router(); router.get("/", handler_search); @@ -37,4 +39,6 @@ router.post("/doDeleteLotOccupancyComment", permissionHandlers.updatePostHandler router.post("/doGetFees", permissionHandlers.updatePostHandler, handler_doGetFees); router.post("/doAddLotOccupancyFee", permissionHandlers.updatePostHandler, handler_doAddLotOccupancyFee); router.post("/doDeleteLotOccupancyFee", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancyFee); +router.post("/doAddLotOccupancyTransaction", permissionHandlers.updatePostHandler, handler_doAddLotOccupancyTransaction); +router.post("/doDeleteLotOccupancyTransaction", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancyTransaction); export default router; diff --git a/routes/lotOccupancies.ts b/routes/lotOccupancies.ts index 2a94aa89..1bd079c0 100644 --- a/routes/lotOccupancies.ts +++ b/routes/lotOccupancies.ts @@ -27,6 +27,9 @@ import handler_doGetFees from "../handlers/lotOccupancies-post/doGetFees.js"; import handler_doAddLotOccupancyFee from "../handlers/lotOccupancies-post/doAddLotOccupancyFee.js"; import handler_doDeleteLotOccupancyFee from "../handlers/lotOccupancies-post/doDeleteLotOccupancyFee.js"; +import handler_doAddLotOccupancyTransaction from "../handlers/lotOccupancies-post/doAddLotOccupancyTransaction.js"; +import handler_doDeleteLotOccupancyTransaction from "../handlers/lotOccupancies-post/doDeleteLotOccupancyTransaction.js"; + import * as permissionHandlers from "../handlers/permissions.js"; @@ -115,5 +118,14 @@ router.post("/doDeleteLotOccupancyFee", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancyFee); +// Transactions + +router.post("/doAddLotOccupancyTransaction", + permissionHandlers.updatePostHandler, + handler_doAddLotOccupancyTransaction); + +router.post("/doDeleteLotOccupancyTransaction", + permissionHandlers.updatePostHandler, + handler_doDeleteLotOccupancyTransaction); export default router; \ No newline at end of file diff --git a/types/configTypes.d.ts b/types/configTypes.d.ts index 937b1d0b..c8c8aa29 100644 --- a/types/configTypes.d.ts +++ b/types/configTypes.d.ts @@ -22,6 +22,7 @@ export interface Config { occupancies?: string; occupant?: string; occupants?: string; + externalReceiptNumber?: string; }; settings?: { fees?: { diff --git a/types/configTypes.ts b/types/configTypes.ts index 60ab1a02..17620aff 100644 --- a/types/configTypes.ts +++ b/types/configTypes.ts @@ -22,6 +22,7 @@ export interface Config { occupancies ? : string; occupant ? : string; occupants ? : string; + externalReceiptNumber ?: string; }; settings ? : { fees ? : { diff --git a/types/recordTypes.d.ts b/types/recordTypes.d.ts index 03db39e7..d17b920a 100644 --- a/types/recordTypes.d.ts +++ b/types/recordTypes.d.ts @@ -135,7 +135,7 @@ export interface LotOccupancyTransaction extends Record { transactionDateString?: string; transactionTime?: number; transactionTimeString?: string; - tranactionAmount?: number; + transactionAmount?: number; externalReceiptNumber?: string; transactionNote?: string; } diff --git a/types/recordTypes.ts b/types/recordTypes.ts index c773d68d..49e3a3e8 100644 --- a/types/recordTypes.ts +++ b/types/recordTypes.ts @@ -193,7 +193,7 @@ export interface LotOccupancyTransaction extends Record { transactionDateString ? : string; transactionTime ? : number; transactionTimeString ? : string; - tranactionAmount ? : number; + transactionAmount ? : number; externalReceiptNumber ? : string; transactionNote ? : string; } diff --git a/views/_footerA.ejs b/views/_footerA.ejs index 35c6bdf2..fa0ced3f 100644 --- a/views/_footerA.ejs +++ b/views/_footerA.ejs @@ -21,7 +21,8 @@ occupancy: "<%= configFunctions.getProperty("aliases.occupancy") %>", occupancies: "<%= configFunctions.getProperty("aliases.occupancies") %>", occupant: "<%= configFunctions.getProperty("aliases.occupant") %>", - occupants: "<%= configFunctions.getProperty("aliases.occupants") %>" + occupants: "<%= configFunctions.getProperty("aliases.occupants") %>", + externalReceiptNumber: "<%= configFunctions.getProperty("aliases.externalReceiptNumber") %>" }; diff --git a/views/lotOccupancy-edit.ejs b/views/lotOccupancy-edit.ejs index 29585ca5..2b5bf6d8 100644 --- a/views/lotOccupancy-edit.ejs +++ b/views/lotOccupancy-edit.ejs @@ -287,6 +287,7 @@
+