development

primarily lot occupancy fees
deepsource-autofix-76c6eb20
Dan Gowans 2022-08-18 16:16:33 -04:00
parent de02b0d496
commit 14f996233d
35 changed files with 1415 additions and 309 deletions

View File

@ -0,0 +1,3 @@
import type { RequestHandler } from "express";
export declare const handler: RequestHandler;
export default handler;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,3 @@
import type { RequestHandler } from "express";
export declare const handler: RequestHandler;
export default handler;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,3 @@
import type { RequestHandler } from "express";
export declare const handler: RequestHandler;
export default handler;

View File

@ -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;

View File

@ -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;

3
helpers/functions.fee.d.ts vendored 100644
View File

@ -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;

View File

@ -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);
};

View File

@ -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);
};

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,3 @@
import type * as recordTypes from "../../types/recordTypes";
export declare const getFee: (feeId: number | string) => recordTypes.Fee;
export default getFee;

View File

@ -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;

View File

@ -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;

View File

@ -5,10 +5,12 @@ export const getLotOccupancyFees = (lotOccupancyId, connectedDatabase) => {
readonly: true readonly: true
}); });
const lotOccupancyFees = database const lotOccupancyFees = database
.prepare("select o.lotOccupancyId, o.feeId, o.feeAmount," + .prepare("select o.lotOccupancyId," +
" f.feeName" + " o.feeId, c.feeCategory, f.feeName," +
" o.feeAmount, o.taxAmount, o.quantity" +
" from LotOccupancyFees o" + " from LotOccupancyFees o" +
" left join Fees f on o.feeId = f.feeId" + " left join Fees f on o.feeId = f.feeId" +
" left join FeeCategories c on f.feeCategoryId = c.feeCategoryId" +
" where o.recordDelete_timeMillis is null" + " where o.recordDelete_timeMillis is null" +
" and o.lotOccupancyId = ?" + " and o.lotOccupancyId = ?" +
" order by o.recordCreate_timeMillis") " order by o.recordCreate_timeMillis")

View File

@ -15,10 +15,12 @@ export const getLotOccupancyFees = (lotOccupancyId: number | string,
}); });
const lotOccupancyFees: recordTypes.LotOccupancyFee[] = database const lotOccupancyFees: recordTypes.LotOccupancyFee[] = database
.prepare("select o.lotOccupancyId, o.feeId, o.feeAmount," + .prepare("select o.lotOccupancyId," +
" f.feeName" + " o.feeId, c.feeCategory, f.feeName," +
" o.feeAmount, o.taxAmount, o.quantity" +
" from LotOccupancyFees o" + " from LotOccupancyFees o" +
" left join Fees f on o.feeId = f.feeId" + " left join Fees f on o.feeId = f.feeId" +
" left join FeeCategories c on f.feeCategoryId = c.feeCategoryId" +
" where o.recordDelete_timeMillis is null" + " where o.recordDelete_timeMillis is null" +
" and o.lotOccupancyId = ?" + " and o.lotOccupancyId = ?" +
" order by o.recordCreate_timeMillis") " order by o.recordCreate_timeMillis")

View File

@ -46,6 +46,36 @@ Object.defineProperty(exports, "__esModule", { value: true });
for (const formInputElement of formInputElements) { for (const formInputElement of formInputElements) {
formInputElement.addEventListener("change", setUnsavedChanges); 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"); const occupancyTypeIdElement = document.querySelector("#lotOccupancy--occupancyTypeId");
if (isCreate) { if (isCreate) {
const lotOccupancyFieldsContainerElement = document.querySelector("#container--lotOccupancyFields"); const lotOccupancyFieldsContainerElement = document.querySelector("#container--lotOccupancyFields");
@ -261,6 +291,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
}, },
onshown: (modalElement, closeModalFunction) => { onshown: (modalElement, closeModalFunction) => {
bulmaJS.toggleHtmlClipped(); bulmaJS.toggleHtmlClipped();
modalElement.querySelector("#lotOccupancyOccupantEdit--lotOccupantTypeId").focus();
editFormElement = modalElement.querySelector("form"); editFormElement = modalElement.querySelector("form");
editFormElement.addEventListener("submit", editOccupant); editFormElement.addEventListener("submit", editOccupant);
editCloseModalFunction = closeModalFunction; editCloseModalFunction = closeModalFunction;
@ -337,7 +368,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
"<span class=\"icon is-small\"><i class=\"fas fa-pencil-alt\" aria-hidden=\"true\"></i></span>" + "<span class=\"icon is-small\"><i class=\"fas fa-pencil-alt\" aria-hidden=\"true\"></i></span>" +
" <span>Edit</span>" + " <span>Edit</span>" +
"</button>") + "</button>") +
("<button class=\"button is-light is-danger button--delete\" type=\"button\" aria-label=\"Delete\">" + ("<button class=\"button is-light is-danger button--delete\" data-tooltip=\"Delete " + cityssm.escapeHTML(exports.aliases.occupant) + "\" type=\"button\" aria-label=\"Delete\">" +
"<i class=\"fas fa-trash\" aria-hidden=\"true\"></i>" + "<i class=\"fas fa-trash\" aria-hidden=\"true\"></i>" +
"</button>") + "</button>") +
"</div>" + "</div>" +
@ -384,6 +415,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
}, },
onshown: (modalElement, closeModalFunction) => { onshown: (modalElement, closeModalFunction) => {
bulmaJS.toggleHtmlClipped(); bulmaJS.toggleHtmlClipped();
modalElement.querySelector("#lotOccupancyOccupantAdd--lotOccupantTypeId").focus();
addFormElement = modalElement.querySelector("form"); addFormElement = modalElement.querySelector("form");
addFormElement.addEventListener("submit", addOccupant); addFormElement.addEventListener("submit", addOccupant);
addCloseModalFunction = closeModalFunction; addCloseModalFunction = closeModalFunction;
@ -432,6 +464,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
}, },
onshown: (modalElement, closeModalFunction) => { onshown: (modalElement, closeModalFunction) => {
bulmaJS.toggleHtmlClipped(); bulmaJS.toggleHtmlClipped();
modalElement.querySelector("#lotOccupancyCommentEdit--lotOccupancyComment").focus();
editFormElement = modalElement.querySelector("form"); editFormElement = modalElement.querySelector("form");
editFormElement.addEventListener("submit", editComment); editFormElement.addEventListener("submit", editComment);
editCloseModalFunction = closeModalFunction; editCloseModalFunction = closeModalFunction;
@ -503,7 +536,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
"<span class=\"icon is-small\"><i class=\"fas fa-pencil-alt\" aria-hidden=\"true\"></i></span>" + "<span class=\"icon is-small\"><i class=\"fas fa-pencil-alt\" aria-hidden=\"true\"></i></span>" +
" <span>Edit</span>" + " <span>Edit</span>" +
"</button>") + "</button>") +
("<button class=\"button is-light is-danger button--delete\" type=\"button\" aria-label=\"Delete\">" + ("<button class=\"button is-light is-danger button--delete\" data-tooltip=\"Delete Comment\" type=\"button\" aria-label=\"Delete\">" +
"<i class=\"fas fa-trash\" aria-hidden=\"true\"></i>" + "<i class=\"fas fa-trash\" aria-hidden=\"true\"></i>" +
"</button>") + "</button>") +
"</div>" + "</div>" +
@ -542,6 +575,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
}, },
onshown: (modalElement, closeModalFunction) => { onshown: (modalElement, closeModalFunction) => {
bulmaJS.toggleHtmlClipped(); bulmaJS.toggleHtmlClipped();
modalElement.querySelector("#lotOccupancyCommentAdd--lotOccupancyComment").focus();
addFormElement = modalElement.querySelector("form"); addFormElement = modalElement.querySelector("form");
addFormElement.addEventListener("submit", addComment); addFormElement.addEventListener("submit", addComment);
addCloseModalFunction = closeModalFunction; addCloseModalFunction = closeModalFunction;
@ -556,7 +590,89 @@ Object.defineProperty(exports, "__esModule", { value: true });
if (!isCreate) { if (!isCreate) {
let lotOccupancyFees = exports.lotOccupancyFees; let lotOccupancyFees = exports.lotOccupancyFees;
const lotOccupancyFeesContainerElement = document.querySelector("#container--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 = () => { const renderLotOccupancyFees = () => {
if (lotOccupancyFees.length === 0) {
lotOccupancyFeesContainerElement.innerHTML = "<div class=\"message is-info\">" +
"<p class=\"message-body\">There are no fees associated with this record.</p>" +
"</div>";
return;
}
lotOccupancyFeesContainerElement.innerHTML = "<table class=\"table is-fullwidth is-striped is-hoverable\">" +
("<thead><tr>" +
"<th>Fee</th>" +
"<th><span class=\"is-sr-only\">Unit Cost</span></th>" +
"<th class=\"has-width-1\"><span class=\"is-sr-only\">&times;</span></th>" +
"<th class=\"has-width-1\"><span class=\"is-sr-only\">Quantity</span></th>" +
"<th class=\"has-width-1\"><span class=\"is-sr-only\">equals</span></th>" +
"<th class=\"has-width-1 has-text-right\">Total</th>" +
"<th class=\"has-width-1\"><span class=\"is-sr-only\">Options</span></th>" +
"</tr></thead>") +
"<tbody></tbody>" +
("<tfoot>" +
"<tr><th colspan=\"5\">Subtotal</th><td class=\"has-text-weight-bold has-text-right\" id=\"lotOccupancyFees--feeAmountTotal\"></td><td></td></tr>" +
"<tr><th colspan=\"5\">Tax</th><td class=\"has-text-right\" id=\"lotOccupancyFees--taxAmountTotal\"></td><td></td></tr>" +
"<tr><th colspan=\"5\">Grand Total</th><td class=\"has-text-weight-bold has-text-right\" id=\"lotOccupancyFees--grandTotal\"></td><td></td></tr>" +
"</tfoot>") +
"</table>";
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 = ("<td colspan=\"" + (lotOccupancyFee.quantity === 1 ? "5" : "1") + "\">" +
cityssm.escapeHTML(lotOccupancyFee.feeName) +
"</td>") +
(lotOccupancyFee.quantity === 1 ?
"" :
"<td class=\"has-text-right\">$" + lotOccupancyFee.feeAmount.toFixed(2) + "</td>" +
"<td>&times;</td>" +
"<td class=\"has-text-right\">" + lotOccupancyFee.quantity + "</td>" +
"<td>=</td>") +
"<td class=\"has-text-right\">$" + (lotOccupancyFee.feeAmount * lotOccupancyFee.quantity).toFixed(2) + "</td>" +
("<td>" +
"<button class=\"button is-small is-danger is-light\" data-tooltip=\"Delete Fee\" type=\"button\">" +
"<i class=\"fas fa-trash\" aria-hidden=\"true\"></i>" +
"</button>" +
"</td>");
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", () => { document.querySelector("#button--addFee").addEventListener("click", () => {
if (hasUnsavedChanges) { if (hasUnsavedChanges) {
@ -568,16 +684,110 @@ Object.defineProperty(exports, "__esModule", { value: true });
} }
let feeCategories; let feeCategories;
let feeFilterElement; 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 filterFees = () => {
const filterStringPieces = feeFilterElement.value.trim().toLowerCase().split(" "); 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 = "<h4 class=\"title is-5\">" + cityssm.escapeHTML(feeCategory.feeCategory) + "</h4>" +
"<div class=\"panel\"></div>";
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 = "<strong>" + cityssm.escapeHTML(fee.feeName) + "</strong><br />" +
"<small>" + cityssm.escapeHTML(fee.feeDescription).replace(/\n/g, "<br />") + "</small>";
panelBlockElement.addEventListener("click", tryAddFee);
categoryContainerElement.querySelector(".panel").append(panelBlockElement);
}
if (hasFees) {
feeFilterResultsElement.append(categoryContainerElement);
}
}
}; };
cityssm.openHtmlModal("lotOccupancy-addFee", { cityssm.openHtmlModal("lotOccupancy-addFee", {
onshow: (modalElement) => { onshow: (modalElement) => {
feeFilterElement = modalElement.querySelector("#feeSelect--feeName");
feeFilterResultsElement = modalElement.querySelector("#resultsContainer--feeSelect");
cityssm.postJSON(urlPrefix + "/lotOccupancies/doGetFees", { cityssm.postJSON(urlPrefix + "/lotOccupancies/doGetFees", {
lotOccupancyId lotOccupancyId
}, (responseJSON) => { }, (responseJSON) => {
feeCategories = responseJSON.feeCategories; feeCategories = responseJSON.feeCategories;
feeFilterElement = modalElement.querySelector("#feeSelect--feeName");
feeFilterElement.disabled = false; feeFilterElement.disabled = false;
feeFilterElement.addEventListener("keyup", filterFees); feeFilterElement.addEventListener("keyup", filterFees);
feeFilterElement.focus(); feeFilterElement.focus();

View File

@ -84,6 +84,40 @@ declare const bulmaJS: BulmaJS;
formInputElement.addEventListener("change", setUnsavedChanges); 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 // Occupancy Type
const occupancyTypeIdElement = document.querySelector("#lotOccupancy--occupancyTypeId") as HTMLSelectElement; const occupancyTypeIdElement = document.querySelector("#lotOccupancy--occupancyTypeId") as HTMLSelectElement;
@ -397,6 +431,8 @@ declare const bulmaJS: BulmaJS;
bulmaJS.toggleHtmlClipped(); bulmaJS.toggleHtmlClipped();
(modalElement.querySelector("#lotOccupancyOccupantEdit--lotOccupantTypeId") as HTMLInputElement).focus();
editFormElement = modalElement.querySelector("form"); editFormElement = modalElement.querySelector("form");
editFormElement.addEventListener("submit", editOccupant); editFormElement.addEventListener("submit", editOccupant);
@ -490,7 +526,7 @@ declare const bulmaJS: BulmaJS;
"<span class=\"icon is-small\"><i class=\"fas fa-pencil-alt\" aria-hidden=\"true\"></i></span>" + "<span class=\"icon is-small\"><i class=\"fas fa-pencil-alt\" aria-hidden=\"true\"></i></span>" +
" <span>Edit</span>" + " <span>Edit</span>" +
"</button>") + "</button>") +
("<button class=\"button is-light is-danger button--delete\" type=\"button\" aria-label=\"Delete\">" + ("<button class=\"button is-light is-danger button--delete\" data-tooltip=\"Delete " + cityssm.escapeHTML(exports.aliases.occupant) + "\" type=\"button\" aria-label=\"Delete\">" +
"<i class=\"fas fa-trash\" aria-hidden=\"true\"></i>" + "<i class=\"fas fa-trash\" aria-hidden=\"true\"></i>" +
"</button>") + "</button>") +
"</div>" + "</div>" +
@ -556,6 +592,8 @@ declare const bulmaJS: BulmaJS;
bulmaJS.toggleHtmlClipped(); bulmaJS.toggleHtmlClipped();
(modalElement.querySelector("#lotOccupancyOccupantAdd--lotOccupantTypeId") as HTMLInputElement).focus();
addFormElement = modalElement.querySelector("form"); addFormElement = modalElement.querySelector("form");
addFormElement.addEventListener("submit", addOccupant); addFormElement.addEventListener("submit", addOccupant);
@ -629,6 +667,8 @@ declare const bulmaJS: BulmaJS;
bulmaJS.toggleHtmlClipped(); bulmaJS.toggleHtmlClipped();
(modalElement.querySelector("#lotOccupancyCommentEdit--lotOccupancyComment") as HTMLTextAreaElement).focus();
editFormElement = modalElement.querySelector("form"); editFormElement = modalElement.querySelector("form");
editFormElement.addEventListener("submit", editComment); editFormElement.addEventListener("submit", editComment);
@ -716,7 +756,7 @@ declare const bulmaJS: BulmaJS;
"<span class=\"icon is-small\"><i class=\"fas fa-pencil-alt\" aria-hidden=\"true\"></i></span>" + "<span class=\"icon is-small\"><i class=\"fas fa-pencil-alt\" aria-hidden=\"true\"></i></span>" +
" <span>Edit</span>" + " <span>Edit</span>" +
"</button>") + "</button>") +
("<button class=\"button is-light is-danger button--delete\" type=\"button\" aria-label=\"Delete\">" + ("<button class=\"button is-light is-danger button--delete\" data-tooltip=\"Delete Comment\" type=\"button\" aria-label=\"Delete\">" +
"<i class=\"fas fa-trash\" aria-hidden=\"true\"></i>" + "<i class=\"fas fa-trash\" aria-hidden=\"true\"></i>" +
"</button>") + "</button>") +
"</div>" + "</div>" +
@ -774,6 +814,8 @@ declare const bulmaJS: BulmaJS;
bulmaJS.toggleHtmlClipped(); bulmaJS.toggleHtmlClipped();
(modalElement.querySelector("#lotOccupancyCommentAdd--lotOccupancyComment") as HTMLTextAreaElement).focus();
addFormElement = modalElement.querySelector("form"); addFormElement = modalElement.querySelector("form");
addFormElement.addEventListener("submit", addComment); addFormElement.addEventListener("submit", addComment);
@ -794,11 +836,111 @@ declare const bulmaJS: BulmaJS;
*/ */
if (!isCreate) { if (!isCreate) {
let lotOccupancyFees: recordTypes.LotOccupancyFee[] = exports.lotOccupancyFees; let lotOccupancyFees: recordTypes.LotOccupancyFee[] = exports.lotOccupancyFees;
const lotOccupancyFeesContainerElement = document.querySelector("#container--lotOccupancyFees") as HTMLElement; 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 = () => { const renderLotOccupancyFees = () => {
if (lotOccupancyFees.length === 0) {
lotOccupancyFeesContainerElement.innerHTML = "<div class=\"message is-info\">" +
"<p class=\"message-body\">There are no fees associated with this record.</p>" +
"</div>";
return;
}
lotOccupancyFeesContainerElement.innerHTML = "<table class=\"table is-fullwidth is-striped is-hoverable\">" +
("<thead><tr>" +
"<th>Fee</th>" +
"<th><span class=\"is-sr-only\">Unit Cost</span></th>" +
"<th class=\"has-width-1\"><span class=\"is-sr-only\">&times;</span></th>" +
"<th class=\"has-width-1\"><span class=\"is-sr-only\">Quantity</span></th>" +
"<th class=\"has-width-1\"><span class=\"is-sr-only\">equals</span></th>" +
"<th class=\"has-width-1 has-text-right\">Total</th>" +
"<th class=\"has-width-1\"><span class=\"is-sr-only\">Options</span></th>" +
"</tr></thead>") +
"<tbody></tbody>" +
("<tfoot>" +
"<tr><th colspan=\"5\">Subtotal</th><td class=\"has-text-weight-bold has-text-right\" id=\"lotOccupancyFees--feeAmountTotal\"></td><td></td></tr>" +
"<tr><th colspan=\"5\">Tax</th><td class=\"has-text-right\" id=\"lotOccupancyFees--taxAmountTotal\"></td><td></td></tr>" +
"<tr><th colspan=\"5\">Grand Total</th><td class=\"has-text-weight-bold has-text-right\" id=\"lotOccupancyFees--grandTotal\"></td><td></td></tr>" +
"</tfoot>") +
"</table>";
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 = ("<td colspan=\"" + (lotOccupancyFee.quantity === 1 ? "5" : "1") + "\">" +
cityssm.escapeHTML(lotOccupancyFee.feeName) +
"</td>") +
(lotOccupancyFee.quantity === 1 ?
"" :
"<td class=\"has-text-right\">$" + lotOccupancyFee.feeAmount.toFixed(2) + "</td>" +
"<td>&times;</td>" +
"<td class=\"has-text-right\">" + lotOccupancyFee.quantity + "</td>" +
"<td>=</td>") +
"<td class=\"has-text-right\">$" + (lotOccupancyFee.feeAmount * lotOccupancyFee.quantity).toFixed(2) + "</td>" +
("<td>" +
"<button class=\"button is-small is-danger is-light\" data-tooltip=\"Delete Fee\" type=\"button\">" +
"<i class=\"fas fa-trash\" aria-hidden=\"true\"></i>" +
"</button>" +
"</td>");
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", () => { document.querySelector("#button--addFee").addEventListener("click", () => {
@ -814,24 +956,151 @@ declare const bulmaJS: BulmaJS;
let feeCategories: recordTypes.FeeCategory[]; let feeCategories: recordTypes.FeeCategory[];
let feeFilterElement: HTMLInputElement; 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 filterFees = () => {
const filterStringPieces = feeFilterElement.value.trim().toLowerCase().split(" "); 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 = "<h4 class=\"title is-5\">" + cityssm.escapeHTML(feeCategory.feeCategory) + "</h4>" +
"<div class=\"panel\"></div>";
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 = "<strong>" + cityssm.escapeHTML(fee.feeName) + "</strong><br />" +
"<small>" + cityssm.escapeHTML(fee.feeDescription).replace(/\n/g, "<br />") + "</small>";
panelBlockElement.addEventListener("click", tryAddFee);
categoryContainerElement.querySelector(".panel").append(panelBlockElement);
}
if (hasFees) {
feeFilterResultsElement.append(categoryContainerElement);
}
}
}; };
cityssm.openHtmlModal("lotOccupancy-addFee", { cityssm.openHtmlModal("lotOccupancy-addFee", {
onshow: (modalElement) => { onshow: (modalElement) => {
feeFilterElement = modalElement.querySelector("#feeSelect--feeName");
feeFilterResultsElement = modalElement.querySelector("#resultsContainer--feeSelect");
cityssm.postJSON(urlPrefix + "/lotOccupancies/doGetFees", { cityssm.postJSON(urlPrefix + "/lotOccupancies/doGetFees", {
lotOccupancyId lotOccupancyId
}, },
(responseJSON: { feeCategories: recordTypes.FeeCategory[]}) => { (responseJSON: {
feeCategories: recordTypes.FeeCategory[]
}) => {
feeCategories = responseJSON.feeCategories; feeCategories = responseJSON.feeCategories;
feeFilterElement = modalElement.querySelector("#feeSelect--feeName");
feeFilterElement.disabled = false; feeFilterElement.disabled = false;
feeFilterElement.addEventListener("keyup", filterFees); feeFilterElement.addEventListener("keyup", filterFees);
feeFilterElement.focus(); feeFilterElement.focus();

View File

@ -92,7 +92,6 @@
<div class="select is-fullwidth"> <div class="select is-fullwidth">
<select id="feeAdd--feeFunction" name="feeFunction"> <select id="feeAdd--feeFunction" name="feeFunction">
<option value="">(No Function Selected)</option> <option value="">(No Function Selected)</option>
<option value="t">test</option>
</select> </select>
</div> </div>
</div> </div>

View File

@ -0,0 +1,31 @@
<div class="modal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<h3 class="modal-card-title">
Set Quantity
</h3>
<button class="delete is-close-modal-button" aria-label="close" type="button"></button>
</header>
<section class="modal-card-body">
<form id="form--lotOccupancyFeeQuantity">
<label class="label" for="lotOccupancyFeeQuantity--quantity">Quantity</label>
<div class="field has-addons">
<div class="control is-expanded">
<input class="input" id="lotOccupancyFeeQuantity--quantity" name="quantity" type="number" value="1" min="0.1" max="999.9" step="0.1" required />
</div>
<div class="control">
<span class="button is-static" id="lotOccupancyFeeQuantity--quantityUnit"></span>
</div>
</div>
</form>
</section>
<footer class="modal-card-foot justify-right">
<button class="button is-success" type="submit" form="form--lotOccupancyFeeQuantity">
<span class="icon"><i class="fas fa-plus" aria-hidden="true"></i></span>
<span>Set Quantity and Add Fee</span>
</button>
<button class="button is-close-modal-button" type="button">Cancel</button>
</footer>
</div>
</div>

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,7 @@ import handler_doGetOccupancyTypeFields from "../handlers/lotOccupancies-post/do
import handler_doCreateLotOccupancy from "../handlers/lotOccupancies-post/doCreateLotOccupancy.js"; import handler_doCreateLotOccupancy from "../handlers/lotOccupancies-post/doCreateLotOccupancy.js";
import handler_edit from "../handlers/lotOccupancies-get/edit.js"; import handler_edit from "../handlers/lotOccupancies-get/edit.js";
import handler_doUpdateLotOccupancy from "../handlers/lotOccupancies-post/doUpdateLotOccupancy.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_doAddLotOccupancyOccupant from "../handlers/lotOccupancies-post/doAddLotOccupancyOccupant.js";
import handler_doUpdateLotOccupancyOccupant from "../handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.js"; import handler_doUpdateLotOccupancyOccupant from "../handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.js";
import handler_doDeleteLotOccupancyOccupant from "../handlers/lotOccupancies-post/doDeleteLotOccupancyOccupant.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_doUpdateLotOccupancyComment from "../handlers/lotOccupancies-post/doUpdateLotOccupancyComment.js";
import handler_doDeleteLotOccupancyComment from "../handlers/lotOccupancies-post/doDeleteLotOccupancyComment.js"; import handler_doDeleteLotOccupancyComment from "../handlers/lotOccupancies-post/doDeleteLotOccupancyComment.js";
import handler_doGetFees from "../handlers/lotOccupancies-post/doGetFees.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"; import * as permissionHandlers from "../handlers/permissions.js";
export const router = Router(); export const router = Router();
router.get("/", handler_search); router.get("/", handler_search);
@ -24,6 +27,7 @@ router.post("/doCreateLotOccupancy", permissionHandlers.updatePostHandler, handl
router.get("/:lotOccupancyId", handler_view); router.get("/:lotOccupancyId", handler_view);
router.get("/:lotOccupancyId/edit", permissionHandlers.updateGetHandler, handler_edit); router.get("/:lotOccupancyId/edit", permissionHandlers.updateGetHandler, handler_edit);
router.post("/doUpdateLotOccupancy", permissionHandlers.updatePostHandler, handler_doUpdateLotOccupancy); router.post("/doUpdateLotOccupancy", permissionHandlers.updatePostHandler, handler_doUpdateLotOccupancy);
router.post("/doDeleteLotOccupancy", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancy);
router.post("/doAddLotOccupancyOccupant", permissionHandlers.updatePostHandler, handler_doAddLotOccupancyOccupant); router.post("/doAddLotOccupancyOccupant", permissionHandlers.updatePostHandler, handler_doAddLotOccupancyOccupant);
router.post("/doUpdateLotOccupancyOccupant", permissionHandlers.updatePostHandler, handler_doUpdateLotOccupancyOccupant); router.post("/doUpdateLotOccupancyOccupant", permissionHandlers.updatePostHandler, handler_doUpdateLotOccupancyOccupant);
router.post("/doDeleteLotOccupancyOccupant", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancyOccupant); 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("/doUpdateLotOccupancyComment", permissionHandlers.updatePostHandler, handler_doUpdateLotOccupancyComment);
router.post("/doDeleteLotOccupancyComment", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancyComment); router.post("/doDeleteLotOccupancyComment", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancyComment);
router.post("/doGetFees", permissionHandlers.updatePostHandler, handler_doGetFees); router.post("/doGetFees", permissionHandlers.updatePostHandler, handler_doGetFees);
router.post("/doAddLotOccupancyFee", permissionHandlers.updatePostHandler, handler_doAddLotOccupancyFee);
router.post("/doDeleteLotOccupancyFee", permissionHandlers.updatePostHandler, handler_doDeleteLotOccupancyFee);
export default router; export default router;

View File

@ -13,6 +13,7 @@ import handler_doCreateLotOccupancy from "../handlers/lotOccupancies-post/doCrea
import handler_edit from "../handlers/lotOccupancies-get/edit.js"; import handler_edit from "../handlers/lotOccupancies-get/edit.js";
import handler_doUpdateLotOccupancy from "../handlers/lotOccupancies-post/doUpdateLotOccupancy.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_doAddLotOccupancyOccupant from "../handlers/lotOccupancies-post/doAddLotOccupancyOccupant.js";
import handler_doUpdateLotOccupancyOccupant from "../handlers/lotOccupancies-post/doUpdateLotOccupancyOccupant.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_doDeleteLotOccupancyComment from "../handlers/lotOccupancies-post/doDeleteLotOccupancyComment.js";
import handler_doGetFees from "../handlers/lotOccupancies-post/doGetFees.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"; import * as permissionHandlers from "../handlers/permissions.js";
@ -66,6 +69,10 @@ router.post("/doUpdateLotOccupancy",
permissionHandlers.updatePostHandler, permissionHandlers.updatePostHandler,
handler_doUpdateLotOccupancy); handler_doUpdateLotOccupancy);
router.post("/doDeleteLotOccupancy",
permissionHandlers.updatePostHandler,
handler_doDeleteLotOccupancy);
// Occupants // Occupants
router.post("/doAddLotOccupancyOccupant", router.post("/doAddLotOccupancyOccupant",
@ -100,5 +107,13 @@ router.post("/doGetFees",
permissionHandlers.updatePostHandler, permissionHandlers.updatePostHandler,
handler_doGetFees); handler_doGetFees);
router.post("/doAddLotOccupancyFee",
permissionHandlers.updatePostHandler,
handler_doAddLotOccupancyFee);
router.post("/doDeleteLotOccupancyFee",
permissionHandlers.updatePostHandler,
handler_doDeleteLotOccupancyFee);
export default router; export default router;

View File

@ -45,6 +45,8 @@
<input id="lotOccupancy--lotOccupancyId" name="lotOccupancyId" type="hidden" value="<%= lotOccupancy.lotOccupancyId %>" /> <input id="lotOccupancy--lotOccupancyId" name="lotOccupancyId" type="hidden" value="<%= lotOccupancy.lotOccupancyId %>" />
<div class="panel">
<div class="panel-block is-block">
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<label class="label" for="lotOccupancy--occupancyTypeId"> <label class="label" for="lotOccupancy--occupancyTypeId">
@ -79,7 +81,7 @@
</div> </div>
</div> </div>
<div class="control"> <div class="control">
<button class="button is-unlock-field-button" type="button" title="Unlock Field"> <button class="button is-unlock-field-button" data-tooltip="Unlock Field" type="button" aria-label="Unlock <%= configFunctions.getProperty("aliases.occupancy") %> Type Field">
<i class="fas fa-unlock" aria-hidden="true"></i> <i class="fas fa-unlock" aria-hidden="true"></i>
</button> </button>
</div> </div>
@ -97,7 +99,7 @@
<%= (isCreate ? "" : " disabled") %> /> <%= (isCreate ? "" : " disabled") %> />
</div> </div>
<div class="control"> <div class="control">
<button class="button is-unlock-field-button" type="button" title="Unlock Field"> <button class="button is-unlock-field-button" data-tooltip="Unlock Field" type="button" aria-label="Unlock <%= configFunctions.getProperty("aliases.lot") %></button> Field">
<i class="fas fa-unlock" aria-hidden="true"></i> <i class="fas fa-unlock" aria-hidden="true"></i>
</button> </button>
</div> </div>
@ -162,8 +164,8 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="has-text-right"> <div class="panel-block is-justify-content-right">
<button class="button is-primary" type="submit"> <button class="button is-primary" type="submit">
<span class="icon is-small"><i class="fas fa-save" aria-hidden="true"></i></span> <span class="icon is-small"><i class="fas fa-save" aria-hidden="true"></i></span>
<span> <span>
@ -172,14 +174,35 @@
Record Record
</span> </span>
</button> </button>
<% if (!isCreate) { %>
<div class="dropdown is-right ml-2">
<div class="dropdown-trigger">
<button class="button" type="button">
<span>More Options</span>
<span class="icon is-small">
<i class="fas fa-angle-down" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu">
<div class="dropdown-content">
<a class="dropdown-item" id="button--deleteLotOccupancy" href="#">
<span class="icon is-small"><i class="fas fa-trash has-text-danger" aria-hidden="true"></i></span>
<span>Delete <%= configFunctions.getProperty("aliases.occupancy") %> Record</span>
</a>
</div>
</div>
</div>
<% } %>
</div>
</div> </div>
</form> </form>
<% if (isCreate) { %> <% if (isCreate) { %>
<% } else { %> <% } else { %>
<hr /> <div class="panel mt-5">
<div class="panel-heading">
<div class="level is-mobile"> <div class="level is-mobile">
<div class="level-left"> <div class="level-left">
<div class="level-item"> <div class="level-item">
@ -197,15 +220,16 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="panel-block is-block" id="container--lotOccupancyOccupants"></div>
</div>
<div id="container--lotOccupancyOccupants"></div> <div class="panel">
<div class="panel-heading">
<hr />
<div class="level is-mobile"> <div class="level is-mobile">
<div class="level-left"> <div class="level-left">
<div class="level-item"> <div class="level-item">
<h2 class="title is-4 mt-2">Comments</h2> <h2 class="title is-4">Comments</h2>
</div> </div>
</div> </div>
<div class="level-right"> <div class="level-right">
@ -217,13 +241,14 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div id="container--lotOccupancyComments"></div> <div class="panel-block is-block" id="container--lotOccupancyComments"></div>
</div>
<hr />
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<div class="panel">
<div class="panel-heading">
<div class="level is-mobile"> <div class="level is-mobile">
<div class="level-left"> <div class="level-left">
<div class="level-item"> <div class="level-item">
@ -239,12 +264,32 @@
</div> </div>
</div> </div>
</div> </div>
<div id="container--lotOccupancyFees"></div> </div>
<div class="panel-block is-block" id="container--lotOccupancyFees"></div>
</div>
</div> </div>
<div class="column"> <div class="column">
<div class="panel">
<div class="panel-heading">
<div class="level is-mobile">
<div class="level-left">
<div class="level-item">
<h2 class="title is-4">Transactions</h2> <h2 class="title is-4">Transactions</h2>
</div> </div>
</div> </div>
<div class="level-right">
<div class="level-item">
<button class="button is-small is-success" id="button--addTransaction" type="button">
<span class="icon is-small"><i class="fas fa-plus" aria-hidden="true"></i></span>
<span>Add Transaction</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<% } %> <% } %>
<%- include('_footerA'); -%> <%- include('_footerA'); -%>

View File

@ -38,6 +38,8 @@
</div> </div>
<% } %> <% } %>
<div class="panel">
<div class="panel-block is-block">
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<p> <p>
@ -76,9 +78,14 @@
</div> </div>
<% } %> <% } %>
</div> </div>
</div>
</div>
<h2 class="title is-4"><%= configFunctions.getProperty("aliases.occupants") %></h2> <div class="panel">
<h2 class="panel-heading">
<%= configFunctions.getProperty("aliases.occupants") %>
</h2>
<div class="panel-block is-block">
<% if (lotOccupancy.lotOccupancyOccupants.length === 0) { %> <% if (lotOccupancy.lotOccupancyOccupants.length === 0) { %>
<div class="message is-warning"> <div class="message is-warning">
<p class="message-body"> <p class="message-body">
@ -117,9 +124,14 @@
</tbody> </tbody>
</table> </table>
<% } %> <% } %>
</div>
</div>
<% if (lotOccupancy.lotOccupancyComments.length > 0) { %> <% if (lotOccupancy.lotOccupancyComments.length > 0) { %>
<h2 class="title is-4">Comments</h2> <div class="panel">
<h2 class="panel-heading">Comments</h2>
<div class="panel-block is-block">
<table class="table is-fullwidth is-striped is-hoverable"> <table class="table is-fullwidth is-striped is-hoverable">
<thead> <thead>
<tr> <tr>
@ -141,12 +153,15 @@
<% } %> <% } %>
</tbody> </tbody>
</table> </table>
</div>
</div>
<% } %> <% } %>
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<h2 class="title is-4">Fees</h2> <div class="panel">
<h2 class="panel-heading">Fees</h2>
<div class="panel-block is-block">
<% if (lotOccupancy.lotOccupancyFees.length === 0) { %> <% if (lotOccupancy.lotOccupancyFees.length === 0) { %>
<div class="message is-info"> <div class="message is-info">
<p class="message-body"> <p class="message-body">
@ -154,12 +169,64 @@
</p> </p>
</div> </div>
<% } else { %> <% } else { %>
<%
let feeAmountTotal = 0;
let taxAmountTotal = 0;
%>
<table class="table is-fullwidth is-striped is-hoverable">
<thead>
<tr>
<th>Fee</th>
<th class="has-text-right"><span class="is-sr-only">Unit Cost</span></th>
<th class="has-width-1"><span class="is-sr-only">&times;</span></th>
<th class="has-width-1 has-text-right"><span class="is-sr-only">Quantity</span></th>
<th class="has-width-1"><span class="is-sr-only">=</span></th>
<th class="has-width-1 has-text-right">Total</th>
</tr>
</thead>
<tbody>
<% for (const lotOccupancyFee of lotOccupancy.lotOccupancyFees) { %>
<%
feeAmountTotal += (lotOccupancyFee.feeAmount * lotOccupancyFee.quantity);
taxAmountTotal += (lotOccupancyFee.taxAmount * lotOccupancyFee.quantity);
%>
<tr>
<td colspan="<%= (lotOccupancyFee.quantity === 1 ? "5" : "1") %>">
<%= lotOccupancyFee.feeName %>
</td>
<% if (lotOccupancyFee.quantity !== 1) { %>
<td class="has-text-right">$<%= lotOccupancyFee.feeAmount.toFixed(2) %></td>
<td>&times;</td>
<td class="has-text-right"><%= lotOccupancyFee.quantity %></td>
<td>=</td>
<% } %>
<td class="has-text-right">$<%= (lotOccupancyFee.feeAmount * lotOccupancyFee.quantity).toFixed(2) %></td>
</tr>
<% } %>
</tbody>
<tfoot>
<tr>
<th colspan="5">Subtotal</th>
<td class="has-text-right has-text-weight-bold">$<%= feeAmountTotal.toFixed(2) %></td>
</tr>
<tr>
<th colspan="5">Tax</th>
<td class="has-text-right">$<%= taxAmountTotal.toFixed(2) %></td>
</tr>
<tr>
<th colspan="5">Grand Total</th>
<td class="has-text-right has-text-weight-bold">$<%= (feeAmountTotal + taxAmountTotal).toFixed(2) %></td>
</tr>
</tfoot>
</table>
<% } %> <% } %>
</div> </div>
</div>
</div>
<div class="column"> <div class="column">
<h2 class="title is-4">Transactions</h2> <div class="panel">
<h2 class="panel-heading">Transactions</h2>
<div class="panel-block is-block">
<% if (lotOccupancy.lotOccupancyTransactions.length === 0) { %> <% if (lotOccupancy.lotOccupancyTransactions.length === 0) { %>
<div class="message is-info"> <div class="message is-info">
<p class="message-body"> <p class="message-body">
@ -171,6 +238,8 @@
<% } %> <% } %>
</div> </div>
</div> </div>
</div>
</div>
<%- include('_footerA'); -%> <%- include('_footerA'); -%>