From 14f996233d9a48d9b8a81cbbfa815bc717dc9871 Mon Sep 17 00:00:00 2001 From: Dan Gowans Date: Thu, 18 Aug 2022 16:16:33 -0400 Subject: [PATCH] development primarily lot occupancy fees --- .../doAddLotOccupancyFee.d.ts | 3 + .../doAddLotOccupancyFee.js | 11 + .../doAddLotOccupancyFee.ts | 27 ++ .../doDeleteLotOccupancy.d.ts | 3 + .../doDeleteLotOccupancy.js | 8 + .../doDeleteLotOccupancy.ts | 20 + .../doDeleteLotOccupancyFee.d.ts | 3 + .../doDeleteLotOccupancyFee.js | 11 + .../doDeleteLotOccupancyFee.ts | 27 ++ helpers/functions.fee.d.ts | 3 + helpers/functions.fee.js | 10 + helpers/functions.fee.ts | 18 + .../lotOccupancyDB/addLotOccupancyFee.d.ts | 8 + helpers/lotOccupancyDB/addLotOccupancyFee.js | 42 ++ helpers/lotOccupancyDB/addLotOccupancyFee.ts | 92 +++++ .../lotOccupancyDB/deleteLotOccupancy.d.ts | 3 + helpers/lotOccupancyDB/deleteLotOccupancy.js | 15 + helpers/lotOccupancyDB/deleteLotOccupancy.ts | 33 ++ .../lotOccupancyDB/deleteLotOccupancyFee.d.ts | 3 + .../lotOccupancyDB/deleteLotOccupancyFee.js | 16 + .../lotOccupancyDB/deleteLotOccupancyFee.ts | 36 ++ helpers/lotOccupancyDB/getFee.d.ts | 3 + helpers/lotOccupancyDB/getFee.js | 25 ++ helpers/lotOccupancyDB/getFee.ts | 38 ++ helpers/lotOccupancyDB/getLotOccupancyFees.js | 6 +- helpers/lotOccupancyDB/getLotOccupancyFees.ts | 6 +- public-typescript/lotOccupancyEdit.js | 216 +++++++++- public-typescript/lotOccupancyEdit.ts | 295 ++++++++++++- public/html/adminFees-addFee.html | 1 - public/html/lotOccupancy-setFeeQuantity.html | 31 ++ public/javascripts/lotOccupancyEdit.min.js | 2 +- routes/lotOccupancies.js | 6 + routes/lotOccupancies.ts | 15 + views/lotOccupancy-edit.ejs | 391 ++++++++++-------- views/lotOccupancy-view.ejs | 297 ++++++++----- 35 files changed, 1415 insertions(+), 309 deletions(-) create mode 100644 handlers/lotOccupancies-post/doAddLotOccupancyFee.d.ts create mode 100644 handlers/lotOccupancies-post/doAddLotOccupancyFee.js create mode 100644 handlers/lotOccupancies-post/doAddLotOccupancyFee.ts create mode 100644 handlers/lotOccupancies-post/doDeleteLotOccupancy.d.ts create mode 100644 handlers/lotOccupancies-post/doDeleteLotOccupancy.js create mode 100644 handlers/lotOccupancies-post/doDeleteLotOccupancy.ts create mode 100644 handlers/lotOccupancies-post/doDeleteLotOccupancyFee.d.ts create mode 100644 handlers/lotOccupancies-post/doDeleteLotOccupancyFee.js create mode 100644 handlers/lotOccupancies-post/doDeleteLotOccupancyFee.ts create mode 100644 helpers/functions.fee.d.ts create mode 100644 helpers/functions.fee.js create mode 100644 helpers/functions.fee.ts create mode 100644 helpers/lotOccupancyDB/addLotOccupancyFee.d.ts create mode 100644 helpers/lotOccupancyDB/addLotOccupancyFee.js create mode 100644 helpers/lotOccupancyDB/addLotOccupancyFee.ts create mode 100644 helpers/lotOccupancyDB/deleteLotOccupancy.d.ts create mode 100644 helpers/lotOccupancyDB/deleteLotOccupancy.js create mode 100644 helpers/lotOccupancyDB/deleteLotOccupancy.ts create mode 100644 helpers/lotOccupancyDB/deleteLotOccupancyFee.d.ts create mode 100644 helpers/lotOccupancyDB/deleteLotOccupancyFee.js create mode 100644 helpers/lotOccupancyDB/deleteLotOccupancyFee.ts create mode 100644 helpers/lotOccupancyDB/getFee.d.ts create mode 100644 helpers/lotOccupancyDB/getFee.js create mode 100644 helpers/lotOccupancyDB/getFee.ts create mode 100644 public/html/lotOccupancy-setFeeQuantity.html diff --git a/handlers/lotOccupancies-post/doAddLotOccupancyFee.d.ts b/handlers/lotOccupancies-post/doAddLotOccupancyFee.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/lotOccupancies-post/doAddLotOccupancyFee.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/doAddLotOccupancyFee.js b/handlers/lotOccupancies-post/doAddLotOccupancyFee.js new file mode 100644 index 00000000..33e164ae --- /dev/null +++ b/handlers/lotOccupancies-post/doAddLotOccupancyFee.js @@ -0,0 +1,11 @@ +import { addLotOccupancyFee } from "../../helpers/lotOccupancyDB/addLotOccupancyFee.js"; +import { getLotOccupancyFees } from "../../helpers/lotOccupancyDB/getLotOccupancyFees.js"; +export const handler = async (request, response) => { + addLotOccupancyFee(request.body, request.session); + const lotOccupancyFees = getLotOccupancyFees(request.body.lotOccupancyId); + response.json({ + success: true, + lotOccupancyFees + }); +}; +export default handler; diff --git a/handlers/lotOccupancies-post/doAddLotOccupancyFee.ts b/handlers/lotOccupancies-post/doAddLotOccupancyFee.ts new file mode 100644 index 00000000..7b742771 --- /dev/null +++ b/handlers/lotOccupancies-post/doAddLotOccupancyFee.ts @@ -0,0 +1,27 @@ +import type { + RequestHandler +} from "express"; + +import { + addLotOccupancyFee +} from "../../helpers/lotOccupancyDB/addLotOccupancyFee.js"; + +import { + getLotOccupancyFees +} from "../../helpers/lotOccupancyDB/getLotOccupancyFees.js"; + + +export const handler: RequestHandler = async (request, response) => { + + addLotOccupancyFee(request.body, request.session); + + const lotOccupancyFees = getLotOccupancyFees(request.body.lotOccupancyId); + + response.json({ + success: true, + lotOccupancyFees + }); +}; + + +export default handler; \ No newline at end of file diff --git a/handlers/lotOccupancies-post/doDeleteLotOccupancy.d.ts b/handlers/lotOccupancies-post/doDeleteLotOccupancy.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/lotOccupancies-post/doDeleteLotOccupancy.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/doDeleteLotOccupancy.js b/handlers/lotOccupancies-post/doDeleteLotOccupancy.js new file mode 100644 index 00000000..334e3f28 --- /dev/null +++ b/handlers/lotOccupancies-post/doDeleteLotOccupancy.js @@ -0,0 +1,8 @@ +import { deleteLotOccupancy } from "../../helpers/lotOccupancyDB/deleteLotOccupancy.js"; +export const handler = async (request, response) => { + const success = deleteLotOccupancy(request.body.lotOccupancyId, request.session); + response.json({ + success + }); +}; +export default handler; diff --git a/handlers/lotOccupancies-post/doDeleteLotOccupancy.ts b/handlers/lotOccupancies-post/doDeleteLotOccupancy.ts new file mode 100644 index 00000000..ff578ae9 --- /dev/null +++ b/handlers/lotOccupancies-post/doDeleteLotOccupancy.ts @@ -0,0 +1,20 @@ +import type { + RequestHandler +} from "express"; + +import { + deleteLotOccupancy +} from "../../helpers/lotOccupancyDB/deleteLotOccupancy.js"; + + +export const handler: RequestHandler = async (request, response) => { + + const success = deleteLotOccupancy(request.body.lotOccupancyId, request.session); + + response.json({ + success + }); +}; + + +export default handler; \ No newline at end of file diff --git a/handlers/lotOccupancies-post/doDeleteLotOccupancyFee.d.ts b/handlers/lotOccupancies-post/doDeleteLotOccupancyFee.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/lotOccupancies-post/doDeleteLotOccupancyFee.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/doDeleteLotOccupancyFee.js b/handlers/lotOccupancies-post/doDeleteLotOccupancyFee.js new file mode 100644 index 00000000..0fc5c8c5 --- /dev/null +++ b/handlers/lotOccupancies-post/doDeleteLotOccupancyFee.js @@ -0,0 +1,11 @@ +import { deleteLotOccupancyFee } from "../../helpers/lotOccupancyDB/deleteLotOccupancyFee.js"; +import { getLotOccupancyFees } from "../../helpers/lotOccupancyDB/getLotOccupancyFees.js"; +export const handler = async (request, response) => { + const success = deleteLotOccupancyFee(request.body.lotOccupancyId, request.body.feeId, request.session); + const lotOccupancyFees = getLotOccupancyFees(request.body.lotOccupancyId); + response.json({ + success, + lotOccupancyFees + }); +}; +export default handler; diff --git a/handlers/lotOccupancies-post/doDeleteLotOccupancyFee.ts b/handlers/lotOccupancies-post/doDeleteLotOccupancyFee.ts new file mode 100644 index 00000000..7cdbe5a6 --- /dev/null +++ b/handlers/lotOccupancies-post/doDeleteLotOccupancyFee.ts @@ -0,0 +1,27 @@ +import type { + RequestHandler +} from "express"; + +import { + deleteLotOccupancyFee +} from "../../helpers/lotOccupancyDB/deleteLotOccupancyFee.js"; + +import { + getLotOccupancyFees +} from "../../helpers/lotOccupancyDB/getLotOccupancyFees.js"; + + +export const handler: RequestHandler = async (request, response) => { + + const success = deleteLotOccupancyFee(request.body.lotOccupancyId, request.body.feeId, request.session); + + const lotOccupancyFees = getLotOccupancyFees(request.body.lotOccupancyId); + + response.json({ + success, + lotOccupancyFees + }); +}; + + +export default handler; \ No newline at end of file diff --git a/helpers/functions.fee.d.ts b/helpers/functions.fee.d.ts new file mode 100644 index 00000000..26c4e8ab --- /dev/null +++ b/helpers/functions.fee.d.ts @@ -0,0 +1,3 @@ +import * as recordTypes from "../types/recordTypes"; +export declare const calculateFeeAmount: (fee: recordTypes.Fee, lotOccupancy: recordTypes.LotOccupancy) => number; +export declare const calculateTaxAmount: (fee: recordTypes.Fee, feeAmount: number) => number; diff --git a/helpers/functions.fee.js b/helpers/functions.fee.js new file mode 100644 index 00000000..e523838d --- /dev/null +++ b/helpers/functions.fee.js @@ -0,0 +1,10 @@ +export const calculateFeeAmount = (fee, lotOccupancy) => { + return fee.feeFunction ? + 0 : + (fee.feeAmount || 0); +}; +export const calculateTaxAmount = (fee, feeAmount) => { + return fee.taxPercentage ? + feeAmount * (fee.taxPercentage / 100) : + (fee.taxAmount || 0); +}; diff --git a/helpers/functions.fee.ts b/helpers/functions.fee.ts new file mode 100644 index 00000000..c604e662 --- /dev/null +++ b/helpers/functions.fee.ts @@ -0,0 +1,18 @@ +import e from "express"; +import * as recordTypes from "../types/recordTypes"; + + +export const calculateFeeAmount = (fee: recordTypes.Fee, lotOccupancy: recordTypes.LotOccupancy): number => { + + return fee.feeFunction ? + 0 : + (fee.feeAmount || 0); +}; + + +export const calculateTaxAmount = (fee: recordTypes.Fee, feeAmount: number) => { + + return fee.taxPercentage ? + feeAmount * (fee.taxPercentage / 100) : + (fee.taxAmount || 0); +}; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/addLotOccupancyFee.d.ts b/helpers/lotOccupancyDB/addLotOccupancyFee.d.ts new file mode 100644 index 00000000..3c47c5c3 --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancyFee.d.ts @@ -0,0 +1,8 @@ +import type * as recordTypes from "../../types/recordTypes"; +interface AddLotOccupancyFeeForm { + lotOccupancyId: string; + feeId: string; + quantity: number | string; +} +export declare const addLotOccupancyFee: (lotOccupancyFeeForm: AddLotOccupancyFeeForm, requestSession: recordTypes.PartialSession) => boolean; +export default addLotOccupancyFee; diff --git a/helpers/lotOccupancyDB/addLotOccupancyFee.js b/helpers/lotOccupancyDB/addLotOccupancyFee.js new file mode 100644 index 00000000..503e9dc8 --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancyFee.js @@ -0,0 +1,42 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +import { calculateFeeAmount, calculateTaxAmount } from "../functions.fee.js"; +import { getFee } from "./getFee.js"; +import { getLotOccupancy } from "./getLotOccupancy.js"; +export const addLotOccupancyFee = (lotOccupancyFeeForm, requestSession) => { + const database = sqlite(databasePath); + const record = database.prepare("select recordDelete_timeMillis" + + " from LotOccupancyFees" + + " where lotOccupancyId = ?" + + " and feeId = ?") + .get(lotOccupancyFeeForm.lotOccupancyId, lotOccupancyFeeForm.feeId); + if (record) { + if (record.recordDelete_timeMillis) { + database.prepare("delete from LotOccupancyFees" + + " where recordDelete_timeMillis is not null" + + " and lotOccupancyId = ?" + + " and feeId = ?") + .run(lotOccupancyFeeForm.lotOccupancyId, lotOccupancyFeeForm.feeId); + } + else { + database.close(); + return false; + } + } + const lotOccupancy = getLotOccupancy(lotOccupancyFeeForm.lotOccupancyId); + const fee = getFee(lotOccupancyFeeForm.feeId); + const feeAmount = calculateFeeAmount(fee, lotOccupancy); + const taxAmount = calculateTaxAmount(fee, feeAmount); + const rightNowMillis = Date.now(); + const result = database + .prepare("insert into LotOccupancyFees (" + + "lotOccupancyId, feeId," + + " quantity, feeAmount, taxAmount," + + " recordCreate_userName, recordCreate_timeMillis," + + " recordUpdate_userName, recordUpdate_timeMillis)" + + " values (?, ?, ?, ?, ?, ?, ?, ?, ?)") + .run(lotOccupancyFeeForm.lotOccupancyId, lotOccupancyFeeForm.feeId, lotOccupancyFeeForm.quantity, feeAmount, taxAmount, requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis); + database.close(); + return result.changes > 0; +}; +export default addLotOccupancyFee; diff --git a/helpers/lotOccupancyDB/addLotOccupancyFee.ts b/helpers/lotOccupancyDB/addLotOccupancyFee.ts new file mode 100644 index 00000000..b91a4d41 --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancyFee.ts @@ -0,0 +1,92 @@ +import sqlite from "better-sqlite3"; + +import { + lotOccupancyDB as databasePath +} from "../../data/databasePaths.js"; + +import { + calculateFeeAmount, + calculateTaxAmount +} from "../functions.fee.js"; + +import { + getFee +} from "./getFee.js"; + +import { + getLotOccupancy +} from "./getLotOccupancy.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +interface AddLotOccupancyFeeForm { + lotOccupancyId: string; + feeId: string; + quantity: number | string; +} + + +export const addLotOccupancyFee = + (lotOccupancyFeeForm: AddLotOccupancyFeeForm, requestSession: recordTypes.PartialSession): boolean => { + + const database = sqlite(databasePath); + + // Check if record already exists + + const record: { + recordDelete_timeMillis?: number + } = database.prepare("select recordDelete_timeMillis" + + " from LotOccupancyFees" + + " where lotOccupancyId = ?" + + " and feeId = ?") + .get(lotOccupancyFeeForm.lotOccupancyId, lotOccupancyFeeForm.feeId); + + if (record) { + if (record.recordDelete_timeMillis) { + + database.prepare("delete from LotOccupancyFees" + + " where recordDelete_timeMillis is not null" + + " and lotOccupancyId = ?" + + " and feeId = ?") + .run(lotOccupancyFeeForm.lotOccupancyId, lotOccupancyFeeForm.feeId); + } else { + database.close(); + return false; + } + } + + // Create new record + + const lotOccupancy = getLotOccupancy(lotOccupancyFeeForm.lotOccupancyId); + const fee = getFee(lotOccupancyFeeForm.feeId); + + const feeAmount = calculateFeeAmount(fee, lotOccupancy); + const taxAmount = calculateTaxAmount(fee, feeAmount); + + const rightNowMillis = Date.now(); + + const result = database + .prepare("insert into LotOccupancyFees (" + + "lotOccupancyId, feeId," + + " quantity, feeAmount, taxAmount," + + " recordCreate_userName, recordCreate_timeMillis," + + " recordUpdate_userName, recordUpdate_timeMillis)" + + " values (?, ?, ?, ?, ?, ?, ?, ?, ?)") + .run(lotOccupancyFeeForm.lotOccupancyId, + lotOccupancyFeeForm.feeId, + lotOccupancyFeeForm.quantity, + feeAmount, + taxAmount, + requestSession.user.userName, + rightNowMillis, + requestSession.user.userName, + rightNowMillis); + + database.close(); + + return result.changes > 0 + }; + + +export default addLotOccupancyFee; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/deleteLotOccupancy.d.ts b/helpers/lotOccupancyDB/deleteLotOccupancy.d.ts new file mode 100644 index 00000000..32bf63ba --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotOccupancy.d.ts @@ -0,0 +1,3 @@ +import type * as recordTypes from "../../types/recordTypes"; +export declare const deleteLotOccupancy: (lotOccupancyId: number | string, requestSession: recordTypes.PartialSession) => boolean; +export default deleteLotOccupancy; diff --git a/helpers/lotOccupancyDB/deleteLotOccupancy.js b/helpers/lotOccupancyDB/deleteLotOccupancy.js new file mode 100644 index 00000000..4d3bb2f4 --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotOccupancy.js @@ -0,0 +1,15 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const deleteLotOccupancy = (lotOccupancyId, requestSession) => { + const database = sqlite(databasePath); + const rightNowMillis = Date.now(); + const result = database + .prepare("update LotOccupancies" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?") + .run(requestSession.user.userName, rightNowMillis, lotOccupancyId); + database.close(); + return (result.changes > 0); +}; +export default deleteLotOccupancy; diff --git a/helpers/lotOccupancyDB/deleteLotOccupancy.ts b/helpers/lotOccupancyDB/deleteLotOccupancy.ts new file mode 100644 index 00000000..9747e134 --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotOccupancy.ts @@ -0,0 +1,33 @@ +import sqlite from "better-sqlite3"; + +import { + lotOccupancyDB as databasePath +} from "../../data/databasePaths.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +export const deleteLotOccupancy = + (lotOccupancyId: number | string, + requestSession: recordTypes.PartialSession): boolean => { + + const database = sqlite(databasePath); + + const rightNowMillis = Date.now(); + + const result = database + .prepare("update LotOccupancies" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?") + .run(requestSession.user.userName, + rightNowMillis, + lotOccupancyId); + + database.close(); + + return (result.changes > 0); + }; + + +export default deleteLotOccupancy; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/deleteLotOccupancyFee.d.ts b/helpers/lotOccupancyDB/deleteLotOccupancyFee.d.ts new file mode 100644 index 00000000..c3f6a176 --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotOccupancyFee.d.ts @@ -0,0 +1,3 @@ +import type * as recordTypes from "../../types/recordTypes"; +export declare const deleteLotOccupancyFee: (lotOccupancyId: number | string, feeId: number | string, requestSession: recordTypes.PartialSession) => boolean; +export default deleteLotOccupancyFee; diff --git a/helpers/lotOccupancyDB/deleteLotOccupancyFee.js b/helpers/lotOccupancyDB/deleteLotOccupancyFee.js new file mode 100644 index 00000000..234ce486 --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotOccupancyFee.js @@ -0,0 +1,16 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const deleteLotOccupancyFee = (lotOccupancyId, feeId, requestSession) => { + const database = sqlite(databasePath); + const rightNowMillis = Date.now(); + const result = database + .prepare("update LotOccupancyFees" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?" + + " and feeId = ?") + .run(requestSession.user.userName, rightNowMillis, lotOccupancyId, feeId); + database.close(); + return (result.changes > 0); +}; +export default deleteLotOccupancyFee; diff --git a/helpers/lotOccupancyDB/deleteLotOccupancyFee.ts b/helpers/lotOccupancyDB/deleteLotOccupancyFee.ts new file mode 100644 index 00000000..37ca8260 --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotOccupancyFee.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 deleteLotOccupancyFee = + (lotOccupancyId: number | string, + feeId: number | string, + requestSession: recordTypes.PartialSession): boolean => { + + const database = sqlite(databasePath); + + const rightNowMillis = Date.now(); + + const result = database + .prepare("update LotOccupancyFees" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?" + + " and feeId = ?") + .run(requestSession.user.userName, + rightNowMillis, + lotOccupancyId, + feeId); + + database.close(); + + return (result.changes > 0); + }; + + +export default deleteLotOccupancyFee; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/getFee.d.ts b/helpers/lotOccupancyDB/getFee.d.ts new file mode 100644 index 00000000..51bff88e --- /dev/null +++ b/helpers/lotOccupancyDB/getFee.d.ts @@ -0,0 +1,3 @@ +import type * as recordTypes from "../../types/recordTypes"; +export declare const getFee: (feeId: number | string) => recordTypes.Fee; +export default getFee; diff --git a/helpers/lotOccupancyDB/getFee.js b/helpers/lotOccupancyDB/getFee.js new file mode 100644 index 00000000..af4aadab --- /dev/null +++ b/helpers/lotOccupancyDB/getFee.js @@ -0,0 +1,25 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const getFee = (feeId) => { + const database = sqlite(databasePath, { + readonly: true + }); + const fee = database.prepare("select f.feeId," + + " f.feeCategoryId, c.feeCategory," + + " f.feeName, f.feeDescription," + + " f.occupancyTypeId, o.occupancyType," + + " f.lotTypeId, l.lotType," + + " f.feeAmount, f.feeFunction, f.taxAmount, f.taxPercentage," + + " f.includeQuantity, f.quantityUnit," + + " f.isRequired" + + " from Fees f" + + " left join FeeCategories c on f.feeCategoryId = c.feeCategoryId" + + " left join OccupancyTypes o on f.occupancyTypeId = o.occupancyTypeId" + + " left join LotTypes l on f.lotTypeId = l.lotTypeId" + + " where f.recordDelete_timeMillis is null" + + " and f.feeId = ?") + .get(feeId); + database.close(); + return fee; +}; +export default getFee; diff --git a/helpers/lotOccupancyDB/getFee.ts b/helpers/lotOccupancyDB/getFee.ts new file mode 100644 index 00000000..90524139 --- /dev/null +++ b/helpers/lotOccupancyDB/getFee.ts @@ -0,0 +1,38 @@ +import sqlite from "better-sqlite3"; + +import { + lotOccupancyDB as databasePath +} from "../../data/databasePaths.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +export const getFee = (feeId: number | string): recordTypes.Fee => { + + const database = sqlite(databasePath, { + readonly: true + }); + + const fee = database.prepare("select f.feeId," + + " f.feeCategoryId, c.feeCategory," + + " f.feeName, f.feeDescription," + + " f.occupancyTypeId, o.occupancyType," + + " f.lotTypeId, l.lotType," + + " f.feeAmount, f.feeFunction, f.taxAmount, f.taxPercentage," + + " f.includeQuantity, f.quantityUnit," + + " f.isRequired" + + " from Fees f" + + " left join FeeCategories c on f.feeCategoryId = c.feeCategoryId" + + " left join OccupancyTypes o on f.occupancyTypeId = o.occupancyTypeId" + + " left join LotTypes l on f.lotTypeId = l.lotTypeId" + + " where f.recordDelete_timeMillis is null" + + " and f.feeId = ?") + .get(feeId); + + database.close(); + + return fee; +}; + + +export default getFee; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/getLotOccupancyFees.js b/helpers/lotOccupancyDB/getLotOccupancyFees.js index be0b7d1f..4a75ffb9 100644 --- a/helpers/lotOccupancyDB/getLotOccupancyFees.js +++ b/helpers/lotOccupancyDB/getLotOccupancyFees.js @@ -5,10 +5,12 @@ export const getLotOccupancyFees = (lotOccupancyId, connectedDatabase) => { readonly: true }); const lotOccupancyFees = database - .prepare("select o.lotOccupancyId, o.feeId, o.feeAmount," + - " f.feeName" + + .prepare("select o.lotOccupancyId," + + " o.feeId, c.feeCategory, f.feeName," + + " o.feeAmount, o.taxAmount, o.quantity" + " from LotOccupancyFees o" + " left join Fees f on o.feeId = f.feeId" + + " left join FeeCategories c on f.feeCategoryId = c.feeCategoryId" + " where o.recordDelete_timeMillis is null" + " and o.lotOccupancyId = ?" + " order by o.recordCreate_timeMillis") diff --git a/helpers/lotOccupancyDB/getLotOccupancyFees.ts b/helpers/lotOccupancyDB/getLotOccupancyFees.ts index 8f707790..efcce75e 100644 --- a/helpers/lotOccupancyDB/getLotOccupancyFees.ts +++ b/helpers/lotOccupancyDB/getLotOccupancyFees.ts @@ -15,10 +15,12 @@ export const getLotOccupancyFees = (lotOccupancyId: number | string, }); const lotOccupancyFees: recordTypes.LotOccupancyFee[] = database - .prepare("select o.lotOccupancyId, o.feeId, o.feeAmount," + - " f.feeName" + + .prepare("select o.lotOccupancyId," + + " o.feeId, c.feeCategory, f.feeName," + + " o.feeAmount, o.taxAmount, o.quantity" + " from LotOccupancyFees o" + " left join Fees f on o.feeId = f.feeId" + + " left join FeeCategories c on f.feeCategoryId = c.feeCategoryId" + " where o.recordDelete_timeMillis is null" + " and o.lotOccupancyId = ?" + " order by o.recordCreate_timeMillis") diff --git a/public-typescript/lotOccupancyEdit.js b/public-typescript/lotOccupancyEdit.js index 8f8660a4..264218bc 100644 --- a/public-typescript/lotOccupancyEdit.js +++ b/public-typescript/lotOccupancyEdit.js @@ -46,6 +46,36 @@ Object.defineProperty(exports, "__esModule", { value: true }); for (const formInputElement of formInputElements) { formInputElement.addEventListener("change", setUnsavedChanges); } + if (!isCreate) { + document.querySelector("#button--deleteLotOccupancy").addEventListener("click", (clickEvent) => { + clickEvent.preventDefault(); + const doDelete = () => { + cityssm.postJSON(urlPrefix + "/lotOccupancies/doDeleteLotOccupancy", { + lotOccupancyId + }, (responseJSON) => { + 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({ + title: "Delete " + exports.aliases.occupancy + " Record", + message: "Are you sure you want to delete this record?", + contextualColorName: "warning", + okButton: { + text: "Yes, Delete", + callbackFunction: doDelete + } + }); + }); + } const occupancyTypeIdElement = document.querySelector("#lotOccupancy--occupancyTypeId"); if (isCreate) { const lotOccupancyFieldsContainerElement = document.querySelector("#container--lotOccupancyFields"); @@ -261,6 +291,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); }, onshown: (modalElement, closeModalFunction) => { bulmaJS.toggleHtmlClipped(); + modalElement.querySelector("#lotOccupancyOccupantEdit--lotOccupantTypeId").focus(); editFormElement = modalElement.querySelector("form"); editFormElement.addEventListener("submit", editOccupant); editCloseModalFunction = closeModalFunction; @@ -337,7 +368,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); "" + " Edit" + "") + - ("") + "" + @@ -384,6 +415,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); }, onshown: (modalElement, closeModalFunction) => { bulmaJS.toggleHtmlClipped(); + modalElement.querySelector("#lotOccupancyOccupantAdd--lotOccupantTypeId").focus(); addFormElement = modalElement.querySelector("form"); addFormElement.addEventListener("submit", addOccupant); addCloseModalFunction = closeModalFunction; @@ -432,6 +464,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); }, onshown: (modalElement, closeModalFunction) => { bulmaJS.toggleHtmlClipped(); + modalElement.querySelector("#lotOccupancyCommentEdit--lotOccupancyComment").focus(); editFormElement = modalElement.querySelector("form"); editFormElement.addEventListener("submit", editComment); editCloseModalFunction = closeModalFunction; @@ -503,7 +536,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); "" + " Edit" + "") + - ("") + "" + @@ -542,6 +575,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); }, onshown: (modalElement, closeModalFunction) => { bulmaJS.toggleHtmlClipped(); + modalElement.querySelector("#lotOccupancyCommentAdd--lotOccupancyComment").focus(); addFormElement = modalElement.querySelector("form"); addFormElement.addEventListener("submit", addComment); addCloseModalFunction = closeModalFunction; @@ -556,7 +590,89 @@ Object.defineProperty(exports, "__esModule", { value: true }); if (!isCreate) { let lotOccupancyFees = exports.lotOccupancyFees; const lotOccupancyFeesContainerElement = document.querySelector("#container--lotOccupancyFees"); + const deleteLotOccupancyFee = (clickEvent) => { + const feeId = clickEvent.currentTarget.closest(".container--lotOccupancyFee").dataset.feeId; + const doDelete = () => { + cityssm.postJSON(urlPrefix + "/lotOccupancies/doDeleteLotOccupancyFee", { + lotOccupancyId, + feeId + }, (responseJSON) => { + if (responseJSON.success) { + lotOccupancyFees = responseJSON.lotOccupancyFees; + renderLotOccupancyFees(); + } + else { + bulmaJS.alert({ + title: "Error Deleting Fee", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); + }; + bulmaJS.confirm({ + title: "Delete Fee", + message: "Are you sure you want to delete this fee?", + contextualColorName: "warning", + okButton: { + text: "Yes, Delete Fee", + callbackFunction: doDelete, + } + }); + }; const renderLotOccupancyFees = () => { + if (lotOccupancyFees.length === 0) { + lotOccupancyFeesContainerElement.innerHTML = "
" + + "

There are no fees associated with this record.

" + + "
"; + return; + } + lotOccupancyFeesContainerElement.innerHTML = "" + + ("" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "") + + "" + + ("" + + "" + + "" + + "" + + "") + + "
FeeUnit Cost×QuantityequalsTotalOptions
Subtotal
Tax
Grand Total
"; + let feeAmountTotal = 0; + let taxAmountTotal = 0; + for (const lotOccupancyFee of lotOccupancyFees) { + const tableRowElement = document.createElement("tr"); + tableRowElement.className = "container--lotOccupancyFee"; + tableRowElement.dataset.feeId = lotOccupancyFee.feeId.toString(); + tableRowElement.innerHTML = ("" + + cityssm.escapeHTML(lotOccupancyFee.feeName) + + "") + + (lotOccupancyFee.quantity === 1 ? + "" : + "$" + lotOccupancyFee.feeAmount.toFixed(2) + "" + + "×" + + "" + lotOccupancyFee.quantity + "" + + "=") + + "$" + (lotOccupancyFee.feeAmount * lotOccupancyFee.quantity).toFixed(2) + "" + + ("" + + "" + + ""); + tableRowElement.querySelector("button").addEventListener("click", deleteLotOccupancyFee); + lotOccupancyFeesContainerElement.querySelector("tbody").append(tableRowElement); + feeAmountTotal += (lotOccupancyFee.feeAmount * lotOccupancyFee.quantity); + taxAmountTotal += (lotOccupancyFee.taxAmount * lotOccupancyFee.quantity); + } + 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); }; document.querySelector("#button--addFee").addEventListener("click", () => { if (hasUnsavedChanges) { @@ -568,16 +684,110 @@ Object.defineProperty(exports, "__esModule", { value: true }); } let feeCategories; let feeFilterElement; + let feeFilterResultsElement; + const doAddFee = (feeId, quantity = 1) => { + cityssm.postJSON(urlPrefix + "/lotOccupancies/doAddLotOccupancyFee", { + lotOccupancyId, + feeId, + quantity + }, (responseJSON) => { + if (responseJSON.success) { + lotOccupancyFees = responseJSON.lotOccupancyFees; + renderLotOccupancyFees(); + filterFees(); + } + else { + bulmaJS.alert({ + title: "Error Adding Fee", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); + }; + const doSetQuantityAndAddFee = (fee) => { + let quantityElement; + let quantityCloseModalFunction; + const doSetQuantity = (submitEvent) => { + submitEvent.preventDefault(); + doAddFee(fee.feeId, quantityElement.value); + quantityCloseModalFunction(); + }; + cityssm.openHtmlModal("lotOccupancy-setFeeQuantity", { + onshow: (modalElement) => { + modalElement.querySelector("#lotOccupancyFeeQuantity--quantityUnit").textContent = fee.quantityUnit; + }, + onshown: (modalElement, closeModalFunction) => { + quantityCloseModalFunction = closeModalFunction; + quantityElement = modalElement.querySelector("#lotOccupancyFeeQuantity--quantity"); + modalElement.querySelector("form").addEventListener("submit", doSetQuantity); + } + }); + }; + const tryAddFee = (clickEvent) => { + clickEvent.preventDefault(); + const feeId = Number.parseInt(clickEvent.currentTarget.dataset.feeId, 10); + const feeCategoryId = Number.parseInt(clickEvent.currentTarget.closest(".container--feeCategory").dataset.feeCategoryId, 10); + const feeCategory = feeCategories.find((currentFeeCategory) => { + return currentFeeCategory.feeCategoryId === feeCategoryId; + }); + const fee = feeCategory.fees.find((currentFee) => { + return currentFee.feeId === feeId; + }); + if (fee.includeQuantity) { + doSetQuantityAndAddFee(fee); + } + else { + doAddFee(feeId); + } + }; const filterFees = () => { const filterStringPieces = feeFilterElement.value.trim().toLowerCase().split(" "); + feeFilterResultsElement.innerHTML = ""; + for (const feeCategory of feeCategories) { + const categoryContainerElement = document.createElement("div"); + categoryContainerElement.className = "container--feeCategory"; + categoryContainerElement.dataset.feeCategoryId = feeCategory.feeCategoryId.toString(); + categoryContainerElement.innerHTML = "

" + cityssm.escapeHTML(feeCategory.feeCategory) + "

" + + "
"; + let hasFees = false; + for (const fee of feeCategory.fees) { + if (lotOccupancyFeesContainerElement.querySelector(".container--lotOccupancyFee[data-fee-id='" + fee.feeId + "']")) { + continue; + } + let includeFee = true; + for (const filterStringPiece of filterStringPieces) { + if (!fee.feeName.toLowerCase().includes(filterStringPiece)) { + includeFee = false; + break; + } + } + if (!includeFee) { + continue; + } + hasFees = true; + const panelBlockElement = document.createElement("a"); + panelBlockElement.className = "panel-block is-block container--fee"; + panelBlockElement.dataset.feeId = fee.feeId.toString(); + panelBlockElement.href = "#"; + panelBlockElement.innerHTML = "" + cityssm.escapeHTML(fee.feeName) + "
" + + "" + cityssm.escapeHTML(fee.feeDescription).replace(/\n/g, "
") + "
"; + panelBlockElement.addEventListener("click", tryAddFee); + categoryContainerElement.querySelector(".panel").append(panelBlockElement); + } + if (hasFees) { + feeFilterResultsElement.append(categoryContainerElement); + } + } }; cityssm.openHtmlModal("lotOccupancy-addFee", { onshow: (modalElement) => { + feeFilterElement = modalElement.querySelector("#feeSelect--feeName"); + feeFilterResultsElement = modalElement.querySelector("#resultsContainer--feeSelect"); cityssm.postJSON(urlPrefix + "/lotOccupancies/doGetFees", { lotOccupancyId }, (responseJSON) => { feeCategories = responseJSON.feeCategories; - feeFilterElement = modalElement.querySelector("#feeSelect--feeName"); feeFilterElement.disabled = false; feeFilterElement.addEventListener("keyup", filterFees); feeFilterElement.focus(); diff --git a/public-typescript/lotOccupancyEdit.ts b/public-typescript/lotOccupancyEdit.ts index e692778c..e0fd696f 100644 --- a/public-typescript/lotOccupancyEdit.ts +++ b/public-typescript/lotOccupancyEdit.ts @@ -84,6 +84,40 @@ declare const bulmaJS: BulmaJS; formInputElement.addEventListener("change", setUnsavedChanges); } + if (!isCreate) { + document.querySelector("#button--deleteLotOccupancy").addEventListener("click", (clickEvent) => { + clickEvent.preventDefault(); + + const doDelete = () => { + cityssm.postJSON(urlPrefix + "/lotOccupancies/doDeleteLotOccupancy", { + 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" + }); + } + }); + } + + 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: doDelete + } + }); + }); + } + // Occupancy Type const occupancyTypeIdElement = document.querySelector("#lotOccupancy--occupancyTypeId") as HTMLSelectElement; @@ -397,6 +431,8 @@ declare const bulmaJS: BulmaJS; bulmaJS.toggleHtmlClipped(); + (modalElement.querySelector("#lotOccupancyOccupantEdit--lotOccupantTypeId") as HTMLInputElement).focus(); + editFormElement = modalElement.querySelector("form"); editFormElement.addEventListener("submit", editOccupant); @@ -490,7 +526,7 @@ declare const bulmaJS: BulmaJS; "" + " Edit" + "") + - ("") + "" + @@ -556,6 +592,8 @@ declare const bulmaJS: BulmaJS; bulmaJS.toggleHtmlClipped(); + (modalElement.querySelector("#lotOccupancyOccupantAdd--lotOccupantTypeId") as HTMLInputElement).focus(); + addFormElement = modalElement.querySelector("form"); addFormElement.addEventListener("submit", addOccupant); @@ -629,6 +667,8 @@ declare const bulmaJS: BulmaJS; bulmaJS.toggleHtmlClipped(); + (modalElement.querySelector("#lotOccupancyCommentEdit--lotOccupancyComment") as HTMLTextAreaElement).focus(); + editFormElement = modalElement.querySelector("form"); editFormElement.addEventListener("submit", editComment); @@ -716,7 +756,7 @@ declare const bulmaJS: BulmaJS; "" + " Edit" + "") + - ("") + "" + @@ -774,6 +814,8 @@ declare const bulmaJS: BulmaJS; bulmaJS.toggleHtmlClipped(); + (modalElement.querySelector("#lotOccupancyCommentAdd--lotOccupancyComment") as HTMLTextAreaElement).focus(); + addFormElement = modalElement.querySelector("form"); addFormElement.addEventListener("submit", addComment); @@ -794,11 +836,111 @@ declare const bulmaJS: BulmaJS; */ if (!isCreate) { + let lotOccupancyFees: recordTypes.LotOccupancyFee[] = exports.lotOccupancyFees; const lotOccupancyFeesContainerElement = document.querySelector("#container--lotOccupancyFees") as HTMLElement; + const deleteLotOccupancyFee = (clickEvent: Event) => { + + const feeId = ((clickEvent.currentTarget as HTMLElement).closest(".container--lotOccupancyFee") as HTMLElement).dataset.feeId; + + const doDelete = () => { + cityssm.postJSON(urlPrefix + "/lotOccupancies/doDeleteLotOccupancyFee", { + lotOccupancyId, + feeId + }, + (responseJSON: { + success: boolean;errorMessage ? : string;lotOccupancyFees ? : recordTypes.LotOccupancyFee[]; + }) => { + + if (responseJSON.success) { + lotOccupancyFees = responseJSON.lotOccupancyFees; + renderLotOccupancyFees(); + } else { + bulmaJS.alert({ + title: "Error Deleting Fee", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); + }; + + bulmaJS.confirm({ + title: "Delete Fee", + message: "Are you sure you want to delete this fee?", + contextualColorName: "warning", + okButton: { + text: "Yes, Delete Fee", + callbackFunction: doDelete, + } + }) + }; + const renderLotOccupancyFees = () => { + if (lotOccupancyFees.length === 0) { + lotOccupancyFeesContainerElement.innerHTML = "
" + + "

There are no fees associated with this record.

" + + "
"; + + return; + } + + lotOccupancyFeesContainerElement.innerHTML = "" + + ("" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "") + + "" + + ("" + + "" + + "" + + "" + + "") + + "
FeeUnit Cost×QuantityequalsTotalOptions
Subtotal
Tax
Grand Total
"; + + let feeAmountTotal = 0; + let taxAmountTotal = 0; + + for (const lotOccupancyFee of lotOccupancyFees) { + + const tableRowElement = document.createElement("tr"); + tableRowElement.className = "container--lotOccupancyFee"; + tableRowElement.dataset.feeId = lotOccupancyFee.feeId.toString(); + + tableRowElement.innerHTML = ("" + + cityssm.escapeHTML(lotOccupancyFee.feeName) + + "") + + (lotOccupancyFee.quantity === 1 ? + "" : + "$" + lotOccupancyFee.feeAmount.toFixed(2) + "" + + "×" + + "" + lotOccupancyFee.quantity + "" + + "=") + + "$" + (lotOccupancyFee.feeAmount * lotOccupancyFee.quantity).toFixed(2) + "" + + ("" + + "" + + ""); + + tableRowElement.querySelector("button").addEventListener("click", deleteLotOccupancyFee); + + lotOccupancyFeesContainerElement.querySelector("tbody").append(tableRowElement); + + feeAmountTotal += (lotOccupancyFee.feeAmount * lotOccupancyFee.quantity); + taxAmountTotal += (lotOccupancyFee.taxAmount * lotOccupancyFee.quantity); + } + + (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); }; document.querySelector("#button--addFee").addEventListener("click", () => { @@ -814,30 +956,157 @@ declare const bulmaJS: BulmaJS; let feeCategories: recordTypes.FeeCategory[]; let feeFilterElement: HTMLInputElement; + let feeFilterResultsElement: HTMLElement; + + const doAddFee = (feeId: number, quantity: number | string = 1) => { + + cityssm.postJSON(urlPrefix + "/lotOccupancies/doAddLotOccupancyFee", { + lotOccupancyId, + feeId, + quantity + }, + (responseJSON: { + success: boolean; + errorMessage ? : string; + lotOccupancyFees ? : recordTypes.LotOccupancyFee[]; + }) => { + + if (responseJSON.success) { + lotOccupancyFees = responseJSON.lotOccupancyFees; + renderLotOccupancyFees(); + filterFees(); + + } else { + bulmaJS.alert({ + title: "Error Adding Fee", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); + }; + + const doSetQuantityAndAddFee = (fee: recordTypes.Fee) => { + + let quantityElement: HTMLInputElement; + let quantityCloseModalFunction: () => void; + + const doSetQuantity = (submitEvent: SubmitEvent) => { + + submitEvent.preventDefault(); + doAddFee(fee.feeId, quantityElement.value); + quantityCloseModalFunction(); + }; + + cityssm.openHtmlModal("lotOccupancy-setFeeQuantity", { + onshow: (modalElement) => { + (modalElement.querySelector("#lotOccupancyFeeQuantity--quantityUnit") as HTMLElement).textContent = fee.quantityUnit; + }, + onshown: (modalElement, closeModalFunction) => { + quantityCloseModalFunction = closeModalFunction; + quantityElement = modalElement.querySelector("#lotOccupancyFeeQuantity--quantity"); + modalElement.querySelector("form").addEventListener("submit", doSetQuantity); + } + }); + }; + + const tryAddFee = (clickEvent: Event) => { + clickEvent.preventDefault(); + + const feeId = Number.parseInt((clickEvent.currentTarget as HTMLElement).dataset.feeId, 10); + const feeCategoryId = Number.parseInt(((clickEvent.currentTarget as HTMLElement).closest(".container--feeCategory") as HTMLElement).dataset.feeCategoryId, 10); + + const feeCategory = feeCategories.find((currentFeeCategory) => { + return currentFeeCategory.feeCategoryId === feeCategoryId; + }); + + const fee = feeCategory.fees.find((currentFee) => { + return currentFee.feeId === feeId; + }); + + if (fee.includeQuantity) { + doSetQuantityAndAddFee(fee); + } else { + doAddFee(feeId); + } + }; const filterFees = () => { const filterStringPieces = feeFilterElement.value.trim().toLowerCase().split(" "); + feeFilterResultsElement.innerHTML = ""; + for (const feeCategory of feeCategories) { + + const categoryContainerElement = document.createElement("div"); + categoryContainerElement.className = "container--feeCategory"; + categoryContainerElement.dataset.feeCategoryId = feeCategory.feeCategoryId.toString(); + categoryContainerElement.innerHTML = "

" + cityssm.escapeHTML(feeCategory.feeCategory) + "

" + + "
"; + + let hasFees = false; + + for (const fee of feeCategory.fees) { + + if (lotOccupancyFeesContainerElement.querySelector(".container--lotOccupancyFee[data-fee-id='" + fee.feeId + "']")) { + continue; + } + + let includeFee = true; + + for (const filterStringPiece of filterStringPieces) { + if (!fee.feeName.toLowerCase().includes(filterStringPiece)) { + includeFee = false; + break; + } + } + + if (!includeFee) { + continue; + } + + hasFees = true; + + const panelBlockElement = document.createElement("a"); + panelBlockElement.className = "panel-block is-block container--fee"; + panelBlockElement.dataset.feeId = fee.feeId.toString(); + panelBlockElement.href = "#"; + + panelBlockElement.innerHTML = "" + cityssm.escapeHTML(fee.feeName) + "
" + + "" + cityssm.escapeHTML(fee.feeDescription).replace(/\n/g, "
") + "
"; + + panelBlockElement.addEventListener("click", tryAddFee); + + categoryContainerElement.querySelector(".panel").append(panelBlockElement); + } + + if (hasFees) { + feeFilterResultsElement.append(categoryContainerElement); + } + } }; cityssm.openHtmlModal("lotOccupancy-addFee", { onshow: (modalElement) => { - + + feeFilterElement = modalElement.querySelector("#feeSelect--feeName"); + feeFilterResultsElement = modalElement.querySelector("#resultsContainer--feeSelect"); + cityssm.postJSON(urlPrefix + "/lotOccupancies/doGetFees", { - lotOccupancyId - }, - (responseJSON: { feeCategories: recordTypes.FeeCategory[]}) => { - feeCategories = responseJSON.feeCategories; + lotOccupancyId + }, + (responseJSON: { + feeCategories: recordTypes.FeeCategory[] + }) => { + feeCategories = responseJSON.feeCategories; - feeFilterElement = modalElement.querySelector("#feeSelect--feeName"); - feeFilterElement.disabled = false; - feeFilterElement.addEventListener("keyup", filterFees); - feeFilterElement.focus(); + feeFilterElement.disabled = false; + feeFilterElement.addEventListener("keyup", filterFees); + feeFilterElement.focus(); - filterFees(); - }); + filterFees(); + }); }, onshown: () => { bulmaJS.toggleHtmlClipped(); diff --git a/public/html/adminFees-addFee.html b/public/html/adminFees-addFee.html index 9fcd269f..9bf662df 100644 --- a/public/html/adminFees-addFee.html +++ b/public/html/adminFees-addFee.html @@ -92,7 +92,6 @@
diff --git a/public/html/lotOccupancy-setFeeQuantity.html b/public/html/lotOccupancy-setFeeQuantity.html new file mode 100644 index 00000000..171c073a --- /dev/null +++ b/public/html/lotOccupancy-setFeeQuantity.html @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/public/javascripts/lotOccupancyEdit.min.js b/public/javascripts/lotOccupancyEdit.min.js index 934e4836..7984dd5f 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,o=""===c;let a=!1,n=o;const s=()=>{a||(a=!0,cityssm.enableNavBlocker())},l=document.querySelector("#form--lotOccupancy");l.addEventListener("submit",e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/"+(o?"doCreateLotOccupancy":"doUpdateLotOccupancy"),l,e=>{e.success?(a=!1,cityssm.disableNavBlocker(),o||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 u=l.querySelectorAll("input, select");for(const e of u)e.addEventListener("change",s);const r=document.querySelector("#lotOccupancy--occupancyTypeId");if(o){const e=document.querySelector("#container--lotOccupancyFields");r.addEventListener("change",()=>{""!==r.value?cityssm.postJSON(t+"/lotOccupancies/doGetOccupancyTypeFields",{occupancyTypeId:r.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 o of t.occupancyTypeFields){c+=","+o.occupancyTypeFieldId;const t=document.createElement("div");t.className="field",t.innerHTML='
',t.querySelector("label").textContent=o.occupancyTypeField;const a=document.createElement("input");a.className="input",a.id="lotOccupancy--lotOccupancyFieldValue_"+o.occupancyTypeFieldId,a.name="lotOccupancyFieldValue_"+o.occupancyTypeFieldId,a.type="text",a.required=o.isRequired,a.minLength=o.minimumLength,a.maxLength=o.maximumLength,o.pattern&&""!==o.pattern&&(a.pattern=o.pattern),t.querySelector(".control").append(a),e.append(t)}e.insertAdjacentHTML("beforeend",'')}):e.innerHTML='

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

"})}else{const e=r.value;r.addEventListener("change",()=>{r.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:()=>{r.value=e}}})})}if(document.querySelector("#lotOccupancy--lotName").addEventListener("click",c=>{const o=c.currentTarget.value;let a,n,l;const u=e=>{e.preventDefault();const t=e.currentTarget;document.querySelector("#lotOccupancy--lotId").value=t.dataset.lotId,document.querySelector("#lotOccupancy--lotName").value=t.dataset.lotName,s(),a()},r=()=>{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",u),t.append(e)}l.innerHTML="",l.append(t)})};cityssm.openHtmlModal("lotOccupancy-selectLot",{onshow:t=>{e.populateAliases(t)},onshown:(e,t)=>{bulmaJS.toggleHtmlClipped(),a=t;const c=e.querySelector("#lotSelect--lotName");c.value=o,c.focus(),c.addEventListener("change",r),e.querySelector("#lotSelect--occupancyStatus").addEventListener("change",r),n=e.querySelector("#form--lotSelect"),l=e.querySelector("#resultsContainer--lotSelect"),n.addEventListener("submit",e=>{e.preventDefault()}),r()},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),document.querySelector("#lotOccupancy--occupancyStartDateString").addEventListener("change",()=>{document.querySelector("#lotOccupancy--occupancyEndDateString").min=document.querySelector("#lotOccupancy--occupancyStartDateString").value}),e.initializeUnlockFieldButtons(l),!o){let o=exports.lotOccupancyOccupants;const a=a=>{const n=Number.parseInt(a.currentTarget.closest("tr").dataset.lotOccupantIndex,10),l=o.find(e=>e.lotOccupantIndex===n);let u,r;const p=e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/doUpdateLotOccupancyOccupant",u,e=>{e.success?(o=e.lotOccupancyOccupants,r(),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 o=t.querySelector("#lotOccupancyOccupantEdit--lotOccupantTypeId");let a=!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,a=!0),o.append(t)}if(!a){const e=document.createElement("option");e.value=l.lotOccupantTypeId.toString(),e.textContent=l.lotOccupantType,e.selected=!0,o.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(),(u=e.querySelector("form")).addEventListener("submit",p),r=t},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})},n=e=>{const a=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:a},e=>{e.success?(o=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===o.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 o){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",a),c.querySelector(".button--delete").addEventListener("click",n),t.querySelector("tbody").append(c)}e.append(t)};document.querySelector("#button--addOccupant").addEventListener("click",()=>{let a,n;const l=e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/doAddLotOccupancyOccupant",a,e=>{e.success?(o=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 o=t.querySelector("#lotOccupancyOccupantAdd--lotOccupantTypeId");for(const e of exports.lotOccupantTypes){const t=document.createElement("option");t.value=e.lotOccupantTypeId.toString(),t.textContent=e.lotOccupantType,o.append(t)}t.querySelector("#lotOccupancyOccupantAdd--occupantCity").value=exports.occupantCityDefault,t.querySelector("#lotOccupancyOccupantAdd--occupantProvince").value=exports.occupantProvinceDefault},onshown:(e,t)=>{bulmaJS.toggleHtmlClipped(),(a=e.querySelector("form")).addEventListener("submit",l),n=t},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),s()}if(!o){let o=exports.lotOccupancyComments;const a=a=>{const n=Number.parseInt(a.currentTarget.closest("tr").dataset.lotOccupancyCommentId,10),l=o.find(e=>e.lotOccupancyCommentId===n);let u,r;const p=e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/doUpdateLotOccupancyComment",u,e=>{e.success?(o=e.lotOccupancyComments,r(),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(),(u=e.querySelector("form")).addEventListener("submit",p),r=t},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})},n=e=>{const a=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:a},e=>{e.success?(o=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===o.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 o){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",a),c.querySelector(".button--delete").addEventListener("click",n),t.querySelector("tbody").append(c)}e.innerHTML="",e.append(t)};document.querySelector("#button--addComment").addEventListener("click",()=>{let a,n;const l=e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/doAddLotOccupancyComment",a,e=>{e.success?(o=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(),(a=e.querySelector("form")).addEventListener("submit",l),n=t},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),s()}if(!o){exports.lotOccupancyFees;document.querySelector("#container--lotOccupancyFees");const e=()=>{};document.querySelector("#button--addFee").addEventListener("click",()=>{if(a)return void bulmaJS.alert({message:"Please save all unsaved changes before adding fees.",contextualColorName:"warning"});let o,n;const s=()=>{n.value.trim().toLowerCase().split(" ")};cityssm.openHtmlModal("lotOccupancy-addFee",{onshow:e=>{cityssm.postJSON(t+"/lotOccupancies/doGetFees",{lotOccupancyId:c},t=>{o=t.feeCategories,(n=e.querySelector("#feeSelect--feeName")).disabled=!1,n.addEventListener("keyup",s),n.focus(),s()})},onshown:()=>{bulmaJS.toggleHtmlClipped()},onhidden:()=>{e()},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),e()}})(); \ No newline at end of file +"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 diff --git a/routes/lotOccupancies.js b/routes/lotOccupancies.js index a95fb810..0846dcfe 100644 --- a/routes/lotOccupancies.js +++ b/routes/lotOccupancies.js @@ -7,6 +7,7 @@ import handler_doGetOccupancyTypeFields from "../handlers/lotOccupancies-post/do import handler_doCreateLotOccupancy from "../handlers/lotOccupancies-post/doCreateLotOccupancy.js"; import handler_edit from "../handlers/lotOccupancies-get/edit.js"; import handler_doUpdateLotOccupancy from "../handlers/lotOccupancies-post/doUpdateLotOccupancy.js"; +import handler_doDeleteLotOccupancy from "../handlers/lotOccupancies-post/doDeleteLotOccupancy.js"; import handler_doAddLotOccupancyOccupant from "../handlers/lotOccupancies-post/doAddLotOccupancyOccupant.js"; import handler_doUpdateLotOccupancyOccupant from "../handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.js"; import handler_doDeleteLotOccupancyOccupant from "../handlers/lotOccupancies-post/doDeleteLotOccupancyOccupant.js"; @@ -14,6 +15,8 @@ import handler_doAddLotOccupancyComment from "../handlers/lotOccupancies-post/do import handler_doUpdateLotOccupancyComment from "../handlers/lotOccupancies-post/doUpdateLotOccupancyComment.js"; import handler_doDeleteLotOccupancyComment from "../handlers/lotOccupancies-post/doDeleteLotOccupancyComment.js"; 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 * as permissionHandlers from "../handlers/permissions.js"; export const router = Router(); router.get("/", handler_search); @@ -24,6 +27,7 @@ router.post("/doCreateLotOccupancy", permissionHandlers.updatePostHandler, handl router.get("/:lotOccupancyId", handler_view); router.get("/:lotOccupancyId/edit", permissionHandlers.updateGetHandler, handler_edit); router.post("/doUpdateLotOccupancy", permissionHandlers.updatePostHandler, handler_doUpdateLotOccupancy); +router.post("/doDeleteLotOccupancy", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancy); router.post("/doAddLotOccupancyOccupant", permissionHandlers.updatePostHandler, handler_doAddLotOccupancyOccupant); router.post("/doUpdateLotOccupancyOccupant", permissionHandlers.updatePostHandler, handler_doUpdateLotOccupancyOccupant); router.post("/doDeleteLotOccupancyOccupant", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancyOccupant); @@ -31,4 +35,6 @@ router.post("/doAddLotOccupancyComment", permissionHandlers.updatePostHandler, h router.post("/doUpdateLotOccupancyComment", permissionHandlers.updatePostHandler, handler_doUpdateLotOccupancyComment); router.post("/doDeleteLotOccupancyComment", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancyComment); router.post("/doGetFees", permissionHandlers.updatePostHandler, handler_doGetFees); +router.post("/doAddLotOccupancyFee", permissionHandlers.updatePostHandler, handler_doAddLotOccupancyFee); +router.post("/doDeleteLotOccupancyFee", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancyFee); export default router; diff --git a/routes/lotOccupancies.ts b/routes/lotOccupancies.ts index 83fd2681..2a94aa89 100644 --- a/routes/lotOccupancies.ts +++ b/routes/lotOccupancies.ts @@ -13,6 +13,7 @@ import handler_doCreateLotOccupancy from "../handlers/lotOccupancies-post/doCrea import handler_edit from "../handlers/lotOccupancies-get/edit.js"; import handler_doUpdateLotOccupancy from "../handlers/lotOccupancies-post/doUpdateLotOccupancy.js"; +import handler_doDeleteLotOccupancy from "../handlers/lotOccupancies-post/doDeleteLotOccupancy.js"; import handler_doAddLotOccupancyOccupant from "../handlers/lotOccupancies-post/doAddLotOccupancyOccupant.js"; import handler_doUpdateLotOccupancyOccupant from "../handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.js"; @@ -23,6 +24,8 @@ import handler_doUpdateLotOccupancyComment from "../handlers/lotOccupancies-post import handler_doDeleteLotOccupancyComment from "../handlers/lotOccupancies-post/doDeleteLotOccupancyComment.js"; 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 * as permissionHandlers from "../handlers/permissions.js"; @@ -66,6 +69,10 @@ router.post("/doUpdateLotOccupancy", permissionHandlers.updatePostHandler, handler_doUpdateLotOccupancy); +router.post("/doDeleteLotOccupancy", + permissionHandlers.updatePostHandler, + handler_doDeleteLotOccupancy); + // Occupants router.post("/doAddLotOccupancyOccupant", @@ -100,5 +107,13 @@ router.post("/doGetFees", permissionHandlers.updatePostHandler, handler_doGetFees); +router.post("/doAddLotOccupancyFee", + permissionHandlers.updatePostHandler, + handler_doAddLotOccupancyFee); + +router.post("/doDeleteLotOccupancyFee", + permissionHandlers.updatePostHandler, + handler_doDeleteLotOccupancyFee); + export default router; \ No newline at end of file diff --git a/views/lotOccupancy-edit.ejs b/views/lotOccupancy-edit.ejs index b08485dd..29585ca5 100644 --- a/views/lotOccupancy-edit.ejs +++ b/views/lotOccupancy-edit.ejs @@ -45,204 +45,249 @@ -
-
- -
-
-
- -
-
-
- -
-
-
-
- - -
-
- - <%= (isCreate ? "" : " disabled") %> /> -
-
- -
-
-
-
-
- -
- -
-
-
- -
- /> -
-
-
-
-
- <% if (isCreate) { %> -
-

- Select the <%= configFunctions.getProperty("aliases.occupancy").toLowerCase() %> type to load the available fields. -

-
- <% } else if (lotOccupancy.lotOccupancyFields.length === 0) { %> -
-

- The current <%= configFunctions.getProperty("aliases.occupancy").toLowerCase() %> type has no additional fields. -

-
- <% } else { %> - <% let occupancyTypeFieldIds = ""; %> - <% for (const lotOccupancyField of lotOccupancy.lotOccupancyFields) { %> - <% occupancyTypeFieldIds += "," + lotOccupancyField.occupancyTypeFieldId; %> -
- -
- - pattern="<%= lotOccupancyField.pattern %>" +
+
+
+
+ +
+
+
+
- <% } %> - - <% } %> +
+ +
+
+
+
+ + +
+
+ + <%= (isCreate ? "" : " disabled") %> /> +
+
+ Field"> + + +
+
+
+
+
+ +
+ +
+
+
+ +
+ /> +
+
+
+
+
+ <% if (isCreate) { %> +
+

+ Select the <%= configFunctions.getProperty("aliases.occupancy").toLowerCase() %> type to load the available fields. +

+
+ <% } else if (lotOccupancy.lotOccupancyFields.length === 0) { %> +
+

+ The current <%= configFunctions.getProperty("aliases.occupancy").toLowerCase() %> type has no additional fields. +

+
+ <% } else { %> + <% let occupancyTypeFieldIds = ""; %> + <% for (const lotOccupancyField of lotOccupancy.lotOccupancyFields) { %> + <% occupancyTypeFieldIds += "," + lotOccupancyField.occupancyTypeFieldId; %> +
+ +
+ + pattern="<%= lotOccupancyField.pattern %>" + <% } %> + minlength="<%= lotOccupancyField.minimumLength %>" + maxlength="<%= lotOccupancyField.maximumLength %>" /> +
+
+ <% } %> + + <% } %> +
+
-
- -
- +
+ + <% if (!isCreate) { %> + + <% } %> +
<% if (isCreate) { %> <% } else { %> -
- -
-
-
-

- <%= configFunctions.getProperty("aliases.occupants") %> -

-
-
-
-
- -
-
-
- -
- -
- -
-
-
-

Comments

-
-
-
-
- -
-
-
- -
- -
- -
-
+
+
-

Fees

+

+ <%= configFunctions.getProperty("aliases.occupants") %> +

-
-
+
+
+
+ +
+
+
+
+
+

Comments

+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+

Fees

+
+
+
+
+ +
+
+
+
+
+
-

Transactions

+
+
+
+
+
+

Transactions

+
+
+
+
+ +
+
+
+
+
<% } %> diff --git a/views/lotOccupancy-view.ejs b/views/lotOccupancy-view.ejs index 9abd85f6..a9463cbb 100644 --- a/views/lotOccupancy-view.ejs +++ b/views/lotOccupancy-view.ejs @@ -38,137 +38,206 @@
<% } %> -
-
-

- <%= configFunctions.getProperty("aliases.occupancy") %> Type
- <%= lotOccupancy.occupancyType %> -

-
-
-

- <%= configFunctions.getProperty("aliases.lot") %>
- <%= lotOccupancy.lotName %> -

-

- <%= configFunctions.getProperty("aliases.map") %>
- <%= lotOccupancy.mapName %> -

-
-
-

- Start Date
- <%= lotOccupancy.occupancyStartDateString %> -

-

- End Date
- <%= (lotOccupancy.occupancyEndDateString === "" ? "(No End Date)" : lotOccupancy.occupancyEndDateString) %> -

-
- <% if (lotOccupancy.lotOccupancyFields.length > 0) { %> -
- <% for (const lotOccupancyField of lotOccupancy.lotOccupancyFields) { %> -

- <%= lotOccupancyField.occupancyTypeField %>
- <%= lotOccupancyField.lotOccupancyFieldValue || "(No Value)" %> +

+
+
+
+

+ <%= configFunctions.getProperty("aliases.occupancy") %> Type
+ <%= lotOccupancy.occupancyType %>

+
+
+

+ <%= configFunctions.getProperty("aliases.lot") %>
+ <%= lotOccupancy.lotName %> +

+

+ <%= configFunctions.getProperty("aliases.map") %>
+ <%= lotOccupancy.mapName %> +

+
+
+

+ Start Date
+ <%= lotOccupancy.occupancyStartDateString %> +

+

+ End Date
+ <%= (lotOccupancy.occupancyEndDateString === "" ? "(No End Date)" : lotOccupancy.occupancyEndDateString) %> +

+
+ <% if (lotOccupancy.lotOccupancyFields.length > 0) { %> +
+ <% for (const lotOccupancyField of lotOccupancy.lotOccupancyFields) { %> +

+ <%= lotOccupancyField.occupancyTypeField %>
+ <%= lotOccupancyField.lotOccupancyFieldValue || "(No Value)" %> +

+ <% } %> +
<% } %>
- <% } %> +
-

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

- -<% if (lotOccupancy.lotOccupancyOccupants.length === 0) { %> -
-

- There are no <%= configFunctions.getProperty("aliases.occupants").toLowerCase() %> - associated with this record. -

-
-<% } else { %> - - - - - - - - - - - <% for (const lotOccupancyOccupant of lotOccupancy.lotOccupancyOccupants) { %> - - - - -
<%= configFunctions.getProperty("aliases.occupant") %> Type<%= configFunctions.getProperty("aliases.occupant") %>AddressPhone Number
<%= lotOccupancyOccupant.lotOccupantType %><%= lotOccupancyOccupant.occupantName %> - <%= lotOccupancyOccupant.occupantAddress1 %>
- <% if (lotOccupancyOccupant.occupantAddress2 && lotOccupancyOccupant.occupantAddress2 !== "") { %> - <%= lotOccupancyOccupant.occupantAddress2 %>
+
+

+ <%= configFunctions.getProperty("aliases.occupants") %> +

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

+ There are no <%= configFunctions.getProperty("aliases.occupants").toLowerCase() %> + associated with this record. +

+
+ <% } else { %> + + + + + + + + + + + <% for (const lotOccupancyOccupant of lotOccupancy.lotOccupancyOccupants) { %> + + + + + + <% } %> - <%= lotOccupancyOccupant.occupantCity %>, <%= lotOccupancyOccupant.occupantProvince %>
- <%= lotOccupancyOccupant.occupantPostalCode %> - - - + +
<%= configFunctions.getProperty("aliases.occupant") %> Type<%= configFunctions.getProperty("aliases.occupant") %>AddressPhone Number
<%= lotOccupancyOccupant.lotOccupantType %><%= lotOccupancyOccupant.occupantName %> + <%= lotOccupancyOccupant.occupantAddress1 %>
+ <% if (lotOccupancyOccupant.occupantAddress2 && lotOccupancyOccupant.occupantAddress2 !== "") { %> + <%= lotOccupancyOccupant.occupantAddress2 %>
+ <% } %> + <%= lotOccupancyOccupant.occupantCity %>, <%= lotOccupancyOccupant.occupantProvince %>
+ <%= lotOccupancyOccupant.occupantPostalCode %> +
+ <%= lotOccupancyOccupant.occupantPhoneNumber %> +
- <%= lotOccupancyOccupant.occupantPhoneNumber %> -
<% } %> -
-<% } %> +
+
+ <% if (lotOccupancy.lotOccupancyComments.length > 0) { %> -

Comments

- - - - - - - - - - <% for (const lotOccupancyComment of lotOccupancy.lotOccupancyComments) { %> - - - - - - <% } %> - -
CommentorComment DateComment
<%= lotOccupancyComment.recordCreate_userName %> - <%= lotOccupancyComment.lotOccupancyCommentDateString %> - <%= (lotOccupancyComment.lotOccupancyCommentTime === 0 ? "" : lotOccupancyComment.lotOccupancyCommentTimeString) %> - <%= lotOccupancyComment.lotOccupancyComment %>
+
+

Comments

+
+ + + + + + + + + + <% for (const lotOccupancyComment of lotOccupancy.lotOccupancyComments) { %> + + + + + + <% } %> + +
CommentorComment DateComment
<%= lotOccupancyComment.recordCreate_userName %> + <%= lotOccupancyComment.lotOccupancyCommentDateString %> + <%= (lotOccupancyComment.lotOccupancyCommentTime === 0 ? "" : lotOccupancyComment.lotOccupancyCommentTimeString) %> + <%= lotOccupancyComment.lotOccupancyComment %>
+
+
<% } %>
-

Fees

- - <% if (lotOccupancy.lotOccupancyFees.length === 0) { %> -
-

- There are no fees applied to this <%= configFunctions.getProperty("aliases.occupancy").toLowerCase() %> record. -

+
+

Fees

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

+ There are no fees applied to this <%= configFunctions.getProperty("aliases.occupancy").toLowerCase() %> record. +

+
+ <% } else { %> + <% + let feeAmountTotal = 0; + let taxAmountTotal = 0; + %> + + + + + + + + + + + + + <% for (const lotOccupancyFee of lotOccupancy.lotOccupancyFees) { %> + <% + feeAmountTotal += (lotOccupancyFee.feeAmount * lotOccupancyFee.quantity); + taxAmountTotal += (lotOccupancyFee.taxAmount * lotOccupancyFee.quantity); + %> + + + <% if (lotOccupancyFee.quantity !== 1) { %> + + + + + <% } %> + + + <% } %> + + + + + + + + + + + + + + + +
FeeUnit Cost×Quantity=Total
"> + <%= lotOccupancyFee.feeName %> + $<%= lotOccupancyFee.feeAmount.toFixed(2) %>×<%= lotOccupancyFee.quantity %>=$<%= (lotOccupancyFee.feeAmount * lotOccupancyFee.quantity).toFixed(2) %>
Subtotal$<%= feeAmountTotal.toFixed(2) %>
Tax$<%= taxAmountTotal.toFixed(2) %>
Grand Total$<%= (feeAmountTotal + taxAmountTotal).toFixed(2) %>
+ <% } %>
- <% } else { %> - - <% } %> +
-

Transactions

- - <% if (lotOccupancy.lotOccupancyTransactions.length === 0) { %> -
-

- There are no transactions associated with this <%= configFunctions.getProperty("aliases.occupancy").toLowerCase() %> record. -

+
+

Transactions

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

+ There are no transactions associated with this <%= configFunctions.getProperty("aliases.occupancy").toLowerCase() %> record. +

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