/* eslint-disable @typescript-eslint/no-non-null-assertion, unicorn/prefer-module */ import type * as globalTypes from "../../types/globalTypes"; import type * as recordTypes from "../../types/recordTypes"; import type { cityssmGlobal } from "@cityssm/bulma-webapp-js/src/types"; import type { BulmaJS } from "@cityssm/bulma-js/types"; declare const cityssm: cityssmGlobal; declare const bulmaJS: BulmaJS; declare const los: globalTypes.LOS; declare const lotOccupancyId: string; declare const hasUnsavedChanges: boolean; let lotOccupancyFees: recordTypes.LotOccupancyFee[] = exports.lotOccupancyFees; delete 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; const doDelete = () => { cityssm.postJSON( los.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.

' + "
"; renderLotOccupancyTransactions(); 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.dataset.includeQuantity = lotOccupancyFee.includeQuantity ? "1" : "0"; 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); renderLotOccupancyTransactions(); }; (document.querySelector("#button--addFee") as HTMLButtonElement).addEventListener("click", () => { if (hasUnsavedChanges) { bulmaJS.alert({ message: "Please save all unsaved changes before adding fees.", contextualColorName: "warning" }); return; } let feeCategories: recordTypes.FeeCategory[]; let feeFilterElement: HTMLInputElement; let feeFilterResultsElement: HTMLElement; const doAddFee = (feeId: number, quantity: number | string = 1) => { cityssm.postJSON( los.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" ) as HTMLInputElement; 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 + "'][data-include-quantity='0']" ) ) { 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") as HTMLElement).append( panelBlockElement ); } if (hasFees) { feeFilterResultsElement.append(categoryContainerElement); } } }; cityssm.openHtmlModal("lotOccupancy-addFee", { onshow: (modalElement) => { feeFilterElement = modalElement.querySelector( "#feeSelect--feeName" ) as HTMLInputElement; feeFilterResultsElement = modalElement.querySelector( "#resultsContainer--feeSelect" ) as HTMLElement; cityssm.postJSON( los.urlPrefix + "/lotOccupancies/doGetFees", { lotOccupancyId }, (responseJSON: { feeCategories: recordTypes.FeeCategory[] }) => { feeCategories = responseJSON.feeCategories; feeFilterElement.disabled = false; feeFilterElement.addEventListener("keyup", filterFees); feeFilterElement.focus(); filterFees(); } ); }, onshown: () => { bulmaJS.toggleHtmlClipped(); }, onhidden: () => { renderLotOccupancyFees(); }, onremoved: () => { bulmaJS.toggleHtmlClipped(); } }); }); let lotOccupancyTransactions: recordTypes.LotOccupancyTransaction[] = exports.lotOccupancyTransactions; delete 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( los.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", '
' + '
' + '
' + '
Outstanding Balance
' + '
$' + (feeGrandTotal - transactionGrandTotal).toFixed(2) + "
" + "
" + "
" + "
" ); } }; (document.querySelector("#button--addTransaction") as HTMLButtonElement).addEventListener( "click", () => { let addCloseModalFunction: () => void; const doAddTransaction = (submitEvent: SubmitEvent) => { submitEvent.preventDefault(); cityssm.postJSON( los.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();