development

deepsource-autofix-76c6eb20
Dan Gowans 2022-08-08 16:25:39 -04:00
parent 4a35da4b13
commit ea41b0e5e7
43 changed files with 1175 additions and 87 deletions

View File

@ -9,6 +9,11 @@ export const config = {
lots: "Burial Sites", lots: "Burial Sites",
map: "Cemetery", map: "Cemetery",
maps: "Cemeteries" maps: "Cemeteries"
},
settings: {
lotOccupancy: {
occupancyEndDateIsRequired: false
}
} }
}; };
export default config; export default config;

View File

@ -11,6 +11,11 @@ export const config: Config = {
lots: "Burial Sites", lots: "Burial Sites",
map: "Cemetery", map: "Cemetery",
maps: "Cemeteries" maps: "Cemeteries"
},
settings: {
lotOccupancy: {
occupancyEndDateIsRequired: false
}
} }
}; };

View File

@ -1,3 +1,4 @@
import { getOccupancyTypes } from "../../helpers/functions.cache.js";
import * as configFunctions from "../../helpers/functions.config.js"; import * as configFunctions from "../../helpers/functions.config.js";
import { getLotOccupancy } from "../../helpers/lotOccupancyDB/getLotOccupancy.js"; import { getLotOccupancy } from "../../helpers/lotOccupancyDB/getLotOccupancy.js";
export const handler = (request, response) => { export const handler = (request, response) => {
@ -5,9 +6,12 @@ export const handler = (request, response) => {
if (!lotOccupancy) { if (!lotOccupancy) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") + "/lotOccupancies/?error=lotOccupancyIdNotFound"); return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") + "/lotOccupancies/?error=lotOccupancyIdNotFound");
} }
const occupancyTypes = getOccupancyTypes();
return response.render("lotOccupancy-edit", { return response.render("lotOccupancy-edit", {
headTitle: configFunctions.getProperty("aliases.lot") + " " + configFunctions.getProperty("aliases.occupancy") + " View", headTitle: configFunctions.getProperty("aliases.lot") + " " + configFunctions.getProperty("aliases.occupancy") + " Update",
lotOccupancy lotOccupancy,
occupancyTypes,
isCreate: false
}); });
}; };
export default handler; export default handler;

View File

@ -1,23 +1,36 @@
import type { RequestHandler } from "express"; import type {
RequestHandler
} from "express";
import {
getOccupancyTypes
} from "../../helpers/functions.cache.js";
import * as configFunctions from "../../helpers/functions.config.js"; import * as configFunctions from "../../helpers/functions.config.js";
import { getLotOccupancy } from "../../helpers/lotOccupancyDB/getLotOccupancy.js"; import {
getLotOccupancy
} from "../../helpers/lotOccupancyDB/getLotOccupancy.js";
export const handler: RequestHandler = (request, response) => { export const handler: RequestHandler = (request, response) => {
const lotOccupancy = getLotOccupancy(request.params.lotOccupancyId); const lotOccupancy = getLotOccupancy(request.params.lotOccupancyId);
if (!lotOccupancy) { if (!lotOccupancy) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") + "/lotOccupancies/?error=lotOccupancyIdNotFound"); return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") + "/lotOccupancies/?error=lotOccupancyIdNotFound");
} }
return response.render("lotOccupancy-edit", { const occupancyTypes = getOccupancyTypes();
headTitle: configFunctions.getProperty("aliases.lot") + " " + configFunctions.getProperty("aliases.occupancy") + " View",
lotOccupancy return response.render("lotOccupancy-edit", {
}); headTitle: configFunctions.getProperty("aliases.lot") + " " + configFunctions.getProperty("aliases.occupancy") + " Update",
lotOccupancy,
occupancyTypes,
isCreate: false
});
}; };
export default handler; export default handler;

View File

@ -1,23 +1,27 @@
import type { RequestHandler } from "express"; import type {
RequestHandler
} from "express";
import * as configFunctions from "../../helpers/functions.config.js"; import * as configFunctions from "../../helpers/functions.config.js";
import { getLotOccupancy } from "../../helpers/lotOccupancyDB/getLotOccupancy.js"; import {
getLotOccupancy
} from "../../helpers/lotOccupancyDB/getLotOccupancy.js";
export const handler: RequestHandler = (request, response) => { export const handler: RequestHandler = (request, response) => {
const lotOccupancy = getLotOccupancy(request.params.lotOccupancyId); const lotOccupancy = getLotOccupancy(request.params.lotOccupancyId);
if (!lotOccupancy) { if (!lotOccupancy) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") + "/lotOccupancies/?error=lotOccupancyIdNotFound"); return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") + "/lotOccupancies/?error=lotOccupancyIdNotFound");
} }
return response.render("lotOccupancy-view", { return response.render("lotOccupancy-view", {
headTitle: configFunctions.getProperty("aliases.lot") + " " + configFunctions.getProperty("aliases.occupancy") + " View", headTitle: configFunctions.getProperty("aliases.lot") + " " + configFunctions.getProperty("aliases.occupancy") + " View",
lotOccupancy lotOccupancy
}); });
}; };
export default handler; 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,9 @@
import { updateLotOccupancy } from "../../helpers/lotOccupancyDB/updateLotOccupancy.js";
export const handler = async (request, response) => {
const success = updateLotOccupancy(request.body, request.session);
response.json({
success,
lotOccupancyId: request.body.lotOccupancyId
});
};
export default handler;

View File

@ -0,0 +1,17 @@
import type { RequestHandler } from "express";
import { updateLotOccupancy } from "../../helpers/lotOccupancyDB/updateLotOccupancy.js";
export const handler: RequestHandler = async (request, response) => {
const success = updateLotOccupancy(request.body, request.session);
response.json({
success,
lotOccupancyId: request.body.lotOccupancyId
});
};
export default handler;

View File

@ -23,4 +23,5 @@ export declare function getProperty(propertyName: "aliases.occupancy"): string;
export declare function getProperty(propertyName: "aliases.occupancies"): string; export declare function getProperty(propertyName: "aliases.occupancies"): string;
export declare function getProperty(propertyName: "aliases.occupant"): string; export declare function getProperty(propertyName: "aliases.occupant"): string;
export declare function getProperty(propertyName: "aliases.occupants"): string; export declare function getProperty(propertyName: "aliases.occupants"): string;
export declare function getProperty(propertyName: "settings.lotOccupancy.occupancyEndDateIsRequired"): boolean;
export declare const keepAliveMillis: number; export declare const keepAliveMillis: number;

View File

@ -23,19 +23,19 @@ configFallbackValues.set("aliases.occupancy", "Occupancy");
configFallbackValues.set("aliases.occupancies", "Occupancies"); configFallbackValues.set("aliases.occupancies", "Occupancies");
configFallbackValues.set("aliases.occupant", "Occupant"); configFallbackValues.set("aliases.occupant", "Occupant");
configFallbackValues.set("aliases.occupants", "Occupants"); configFallbackValues.set("aliases.occupants", "Occupants");
configFallbackValues.set("settings.lotOccupancy.occupancyEndDateIsRequired", true);
export function getProperty(propertyName) { export function getProperty(propertyName) {
const propertyNameSplit = propertyName.split("."); const propertyNameSplit = propertyName.split(".");
let currentObject = config; let currentObject = config;
for (const propertyNamePiece of propertyNameSplit) { for (const propertyNamePiece of propertyNameSplit) {
if (currentObject[propertyNamePiece]) { if (Object.prototype.hasOwnProperty.call(currentObject, propertyNamePiece)) {
currentObject = currentObject[propertyNamePiece]; currentObject = currentObject[propertyNamePiece];
continue;
} }
else { return configFallbackValues.get(propertyName);
return configFallbackValues.get(propertyName);
}
} }
return currentObject; return currentObject;
} }
export const keepAliveMillis = getProperty("session.doKeepAlive") export const keepAliveMillis = getProperty("session.doKeepAlive") ?
? Math.max(getProperty("session.maxAgeMillis") / 2, getProperty("session.maxAgeMillis") - (10 * 60 * 1000)) Math.max(getProperty("session.maxAgeMillis") / 2, getProperty("session.maxAgeMillis") - (10 * 60 * 1000)) :
: 0; 0;

View File

@ -1,5 +1,7 @@
// eslint-disable-next-line node/no-unpublished-import // eslint-disable-next-line node/no-unpublished-import
import { config } from "../data/config.js"; import {
config
} from "../data/config.js";
import type * as configTypes from "../types/configTypes"; import type * as configTypes from "../types/configTypes";
@ -8,7 +10,8 @@ import type * as configTypes from "../types/configTypes";
* SET UP FALLBACK VALUES * SET UP FALLBACK VALUES
*/ */
const configFallbackValues = new Map<string, unknown>(); const configFallbackValues = new Map < string,
unknown > ();
configFallbackValues.set("application.applicationName", "Lot Occupancy System"); configFallbackValues.set("application.applicationName", "Lot Occupancy System");
configFallbackValues.set("application.backgroundURL", "/images/cemetery-background.jpg"); configFallbackValues.set("application.backgroundURL", "/images/cemetery-background.jpg");
@ -38,6 +41,8 @@ configFallbackValues.set("aliases.occupancies", "Occupancies");
configFallbackValues.set("aliases.occupant", "Occupant"); configFallbackValues.set("aliases.occupant", "Occupant");
configFallbackValues.set("aliases.occupants", "Occupants"); configFallbackValues.set("aliases.occupants", "Occupants");
configFallbackValues.set("settings.lotOccupancy.occupancyEndDateIsRequired", true);
/* /*
* Set up function overloads * Set up function overloads
@ -73,29 +78,31 @@ export function getProperty(propertyName: "aliases.occupancies"): string;
export function getProperty(propertyName: "aliases.occupant"): string; export function getProperty(propertyName: "aliases.occupant"): string;
export function getProperty(propertyName: "aliases.occupants"): string; export function getProperty(propertyName: "aliases.occupants"): string;
export function getProperty(propertyName: "settings.lotOccupancy.occupancyEndDateIsRequired"): boolean;
export function getProperty(propertyName: string): unknown { export function getProperty(propertyName: string): unknown {
const propertyNameSplit = propertyName.split("."); const propertyNameSplit = propertyName.split(".");
let currentObject = config; let currentObject = config;
for (const propertyNamePiece of propertyNameSplit) { for (const propertyNamePiece of propertyNameSplit) {
if (currentObject[propertyNamePiece]) { if (Object.prototype.hasOwnProperty.call(currentObject, propertyNamePiece)) {
currentObject = currentObject[propertyNamePiece]; currentObject = currentObject[propertyNamePiece];
} else { continue;
return configFallbackValues.get(propertyName); }
return configFallbackValues.get(propertyName);
} }
}
return currentObject;
return currentObject;
} }
export const keepAliveMillis = export const keepAliveMillis =
getProperty("session.doKeepAlive") getProperty("session.doKeepAlive") ?
? Math.max( Math.max(
getProperty("session.maxAgeMillis") / 2, getProperty("session.maxAgeMillis") / 2,
getProperty("session.maxAgeMillis") - (10 * 60 * 1000) getProperty("session.maxAgeMillis") - (10 * 60 * 1000)
) ) :
: 0; 0;

View File

@ -2,6 +2,10 @@ import { dateIntegerToString } from "@cityssm/expressjs-server-js/dateTimeFns.js
import sqlite from "better-sqlite3"; import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
import { getLotOccupancyOccupants } from "./getLotOccupancyOccupants.js"; import { getLotOccupancyOccupants } from "./getLotOccupancyOccupants.js";
import { getLotOccupancyComments } from "./getLotOccupancyComments.js";
import { getLotOccupancyFields } from "./getLotOccupancyFields.js";
import { getLotOccupancyFees } from "./getLotOccupancyFees.js";
import { getLotOccupancyTransactions } from "./getLotOccupancyTransactions.js";
export const getLotOccupancy = (lotOccupancyId) => { export const getLotOccupancy = (lotOccupancyId) => {
const database = sqlite(databasePath, { const database = sqlite(databasePath, {
readonly: true readonly: true
@ -22,7 +26,11 @@ export const getLotOccupancy = (lotOccupancyId) => {
" and o.lotOccupancyId = ?") " and o.lotOccupancyId = ?")
.get(lotOccupancyId); .get(lotOccupancyId);
if (lotOccupancy) { if (lotOccupancy) {
lotOccupancy.lotOccupancyFields = getLotOccupancyFields(lotOccupancyId, database);
lotOccupancy.lotOccupancyOccupants = getLotOccupancyOccupants(lotOccupancyId, database); lotOccupancy.lotOccupancyOccupants = getLotOccupancyOccupants(lotOccupancyId, database);
lotOccupancy.lotOccupancyComments = getLotOccupancyComments(lotOccupancyId, database);
lotOccupancy.lotOccupancyFees = getLotOccupancyFees(lotOccupancyId, database);
lotOccupancy.lotOccupancyTransactions = getLotOccupancyTransactions(lotOccupancyId, database);
} }
database.close(); database.close();
return lotOccupancy; return lotOccupancy;

View File

@ -12,6 +12,22 @@ import {
getLotOccupancyOccupants getLotOccupancyOccupants
} from "./getLotOccupancyOccupants.js"; } from "./getLotOccupancyOccupants.js";
import {
getLotOccupancyComments
} from "./getLotOccupancyComments.js";
import {
getLotOccupancyFields
} from "./getLotOccupancyFields.js";
import {
getLotOccupancyFees
} from "./getLotOccupancyFees.js";
import {
getLotOccupancyTransactions
} from "./getLotOccupancyTransactions.js";
import type * as recordTypes from "../../types/recordTypes"; import type * as recordTypes from "../../types/recordTypes";
@ -40,7 +56,11 @@ export const getLotOccupancy = (lotOccupancyId: number | string): recordTypes.Lo
.get(lotOccupancyId); .get(lotOccupancyId);
if (lotOccupancy) { if (lotOccupancy) {
lotOccupancy.lotOccupancyFields = getLotOccupancyFields(lotOccupancyId, database);
lotOccupancy.lotOccupancyOccupants = getLotOccupancyOccupants(lotOccupancyId, database); lotOccupancy.lotOccupancyOccupants = getLotOccupancyOccupants(lotOccupancyId, database);
lotOccupancy.lotOccupancyComments = getLotOccupancyComments(lotOccupancyId, database);
lotOccupancy.lotOccupancyFees = getLotOccupancyFees(lotOccupancyId, database);
lotOccupancy.lotOccupancyTransactions = getLotOccupancyTransactions(lotOccupancyId, database);
} }
database.close(); database.close();

View File

@ -0,0 +1,4 @@
import sqlite from "better-sqlite3";
import type * as recordTypes from "../../types/recordTypes";
export declare const getLotOccupancyFees: (lotOccupancyId: number | string, connectedDatabase?: sqlite.Database) => recordTypes.LotOccupancyFee[];
export default getLotOccupancyFees;

View File

@ -0,0 +1,21 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const getLotOccupancyFees = (lotOccupancyId, connectedDatabase) => {
const database = connectedDatabase || sqlite(databasePath, {
readonly: true
});
const lotOccupancyFees = database
.prepare("select o.lotOccupancyId, o.feeId, o.feeAmount," +
" f.feeName" +
" from LotOccupancyFees o" +
" left join Fees f on o.feeId = f.feeId" +
" where o.recordDelete_timeMillis is null" +
" and o.lotOccupancyId = ?" +
" order by o.recordCreate_timeMillis")
.all(lotOccupancyId);
if (!connectedDatabase) {
database.close();
}
return lotOccupancyFees;
};
export default getLotOccupancyFees;

View File

@ -0,0 +1,35 @@
import sqlite from "better-sqlite3";
import {
lotOccupancyDB as databasePath
} from "../../data/databasePaths.js";
import type * as recordTypes from "../../types/recordTypes";
export const getLotOccupancyFees = (lotOccupancyId: number | string,
connectedDatabase ? : sqlite.Database): recordTypes.LotOccupancyFee[] => {
const database = connectedDatabase || sqlite(databasePath, {
readonly: true
});
const lotOccupancyFees: recordTypes.LotOccupancyFee[] = database
.prepare("select o.lotOccupancyId, o.feeId, o.feeAmount," +
" f.feeName" +
" from LotOccupancyFees o" +
" left join Fees f on o.feeId = f.feeId" +
" where o.recordDelete_timeMillis is null" +
" and o.lotOccupancyId = ?" +
" order by o.recordCreate_timeMillis")
.all(lotOccupancyId);
if (!connectedDatabase) {
database.close();
}
return lotOccupancyFees;
};
export default getLotOccupancyFees;

View File

@ -0,0 +1,4 @@
import sqlite from "better-sqlite3";
import type * as recordTypes from "../../types/recordTypes";
export declare const getLotOccupancyFields: (lotOccupancyId: number | string, connectedDatabase?: sqlite.Database) => recordTypes.LotOccupancyField[];
export default getLotOccupancyFields;

View File

@ -0,0 +1,30 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const getLotOccupancyFields = (lotOccupancyId, connectedDatabase) => {
const database = connectedDatabase || sqlite(databasePath, {
readonly: true
});
const lotOccupancyFields = database
.prepare("select o.lotOccupancyId," +
" o.occupancyTypeFieldId, o.lotOccupancyFieldValue," +
" f.occupancyTypeField, f.orderNumber" +
" from LotOccupancyFields o" +
" left join OccupancyTypeFields f on o.occupancyTypeFieldId = f.occupancyTypeFieldId" +
" where o.recordDelete_timeMillis is null" +
" and o.lotOccupancyId = ?" +
" union" +
" select ? as lotOccupancyId," +
" f.occupancyTypeFieldId, '' as lotOccupancyFieldValue," +
" f.occupancyTypeField, f.orderNumber" +
" from OccupancyTypeFields f" +
" where f.recordDelete_timeMillis is null" +
" and f.occupancyTypeId in (select occupancyTypeId from LotOccupancies where lotOccupancyId = ?)" +
" and f.occupancyTypeFieldId not in (select occupancyTypeFieldId from LotOccupancyFields where lotOccupancyId = ?)" +
" order by orderNumber, occupancyTypeField")
.all(lotOccupancyId, lotOccupancyId, lotOccupancyId, lotOccupancyId);
if (!connectedDatabase) {
database.close();
}
return lotOccupancyFields;
};
export default getLotOccupancyFields;

View File

@ -0,0 +1,44 @@
import sqlite from "better-sqlite3";
import {
lotOccupancyDB as databasePath
} from "../../data/databasePaths.js";
import type * as recordTypes from "../../types/recordTypes";
export const getLotOccupancyFields = (lotOccupancyId: number | string,
connectedDatabase ? : sqlite.Database): recordTypes.LotOccupancyField[] => {
const database = connectedDatabase || sqlite(databasePath, {
readonly: true
});
const lotOccupancyFields: recordTypes.LotOccupancyField[] = database
.prepare("select o.lotOccupancyId," +
" o.occupancyTypeFieldId, o.lotOccupancyFieldValue," +
" f.occupancyTypeField, f.orderNumber" +
" from LotOccupancyFields o" +
" left join OccupancyTypeFields f on o.occupancyTypeFieldId = f.occupancyTypeFieldId" +
" where o.recordDelete_timeMillis is null" +
" and o.lotOccupancyId = ?" +
" union" +
" select ? as lotOccupancyId," +
" f.occupancyTypeFieldId, '' as lotOccupancyFieldValue," +
" f.occupancyTypeField, f.orderNumber" +
" from OccupancyTypeFields f" +
" where f.recordDelete_timeMillis is null" +
" and f.occupancyTypeId in (select occupancyTypeId from LotOccupancies where lotOccupancyId = ?)" +
" and f.occupancyTypeFieldId not in (select occupancyTypeFieldId from LotOccupancyFields where lotOccupancyId = ?)" +
" order by orderNumber, occupancyTypeField")
.all(lotOccupancyId, lotOccupancyId, lotOccupancyId, lotOccupancyId);
if (!connectedDatabase) {
database.close();
}
return lotOccupancyFields;
};
export default getLotOccupancyFields;

View File

@ -1,4 +1,5 @@
import sqlite from "better-sqlite3"; import sqlite from "better-sqlite3";
import { import {
lotOccupancyDB as databasePath lotOccupancyDB as databasePath
} from "../../data/databasePaths.js"; } from "../../data/databasePaths.js";

View File

@ -0,0 +1,4 @@
import sqlite from "better-sqlite3";
import type * as recordTypes from "../../types/recordTypes";
export declare const getLotOccupancyTransactions: (lotOccupancyId: number | string, connectedDatabase?: sqlite.Database) => recordTypes.LotOccupancyTransaction[];
export default getLotOccupancyTransactions;

View File

@ -0,0 +1,25 @@
import { dateIntegerToString, timeIntegerToString } from "@cityssm/expressjs-server-js/dateTimeFns.js";
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const getLotOccupancyTransactions = (lotOccupancyId, connectedDatabase) => {
const database = connectedDatabase || sqlite(databasePath, {
readonly: true
});
database.function("userFn_dateIntegerToString", dateIntegerToString);
database.function("userFn_timeIntegerToString", timeIntegerToString);
const lotOccupancyTransactions = database
.prepare("select lotOccupancyId, transactionIndex," +
" transactionDate, userFn_dateIntegerToString(transactionDate) as transactionDateString," +
" transactionTime, userFn_timeIntegerToString(transactionTime) as transactionTimeString," +
" transactionAmount, externalReceiptNumber, transactionNote" +
" from LotOccupancyTransactions" +
" where recordDelete_timeMillis is null" +
" and lotOccupancyId = ?" +
" order by transactionDate, transactionTime, transactionIndex")
.all(lotOccupancyId);
if (!connectedDatabase) {
database.close();
}
return lotOccupancyTransactions;
};
export default getLotOccupancyTransactions;

View File

@ -0,0 +1,40 @@
import { dateIntegerToString, timeIntegerToString } from "@cityssm/expressjs-server-js/dateTimeFns.js";
import sqlite from "better-sqlite3";
import {
lotOccupancyDB as databasePath
} from "../../data/databasePaths.js";
import type * as recordTypes from "../../types/recordTypes";
export const getLotOccupancyTransactions = (lotOccupancyId: number | string,
connectedDatabase ? : sqlite.Database): recordTypes.LotOccupancyTransaction[] => {
const database = connectedDatabase || sqlite(databasePath, {
readonly: true
});
database.function("userFn_dateIntegerToString", dateIntegerToString);
database.function("userFn_timeIntegerToString", timeIntegerToString);
const lotOccupancyTransactions: recordTypes.LotOccupancyTransaction[] = database
.prepare("select lotOccupancyId, transactionIndex," +
" transactionDate, userFn_dateIntegerToString(transactionDate) as transactionDateString," +
" transactionTime, userFn_timeIntegerToString(transactionTime) as transactionTimeString," +
" transactionAmount, externalReceiptNumber, transactionNote" +
" from LotOccupancyTransactions" +
" where recordDelete_timeMillis is null" +
" and lotOccupancyId = ?" +
" order by transactionDate, transactionTime, transactionIndex")
.all(lotOccupancyId);
if (!connectedDatabase) {
database.close();
}
return lotOccupancyTransactions;
};
export default getLotOccupancyTransactions;

View File

@ -4,6 +4,7 @@ interface GetLotsFilters {
mapId?: number | string; mapId?: number | string;
lotTypeId?: number | string; lotTypeId?: number | string;
lotStatusId?: number | string; lotStatusId?: number | string;
occupancyStatus?: "" | "occupied" | "unoccupied";
} }
interface GetLotsOptions { interface GetLotsOptions {
limit: number; limit: number;

View File

@ -26,14 +26,30 @@ export const getLots = (filters, options) => {
sqlWhereClause += " and l.lotStatusId = ?"; sqlWhereClause += " and l.lotStatusId = ?";
sqlParameters.push(filters.lotStatusId); sqlParameters.push(filters.lotStatusId);
} }
if (filters.occupancyStatus) {
if (filters.occupancyStatus === "occupied") {
sqlWhereClause += " and lotOccupancyCount > 0";
}
else if (filters.occupancyStatus === "unoccupied") {
sqlWhereClause += " and (lotOccupancyCount is null or lotOccupancyCount = 0)";
}
}
const currentDate = dateToInteger(new Date());
const count = database.prepare("select count(*) as recordCount" + const count = database.prepare("select count(*) as recordCount" +
" from Lots l" + " from Lots l" +
(" left join (" +
"select lotId, count(lotOccupancyId) as lotOccupancyCount" +
" from LotOccupancies" +
" where recordDelete_timeMillis is null" +
" and occupancyStartDate <= " + currentDate +
" and (occupancyEndDate is null or occupancyEndDate >= " + currentDate + ")" +
" group by lotId" +
") o on l.lotId = o.lotId") +
sqlWhereClause) sqlWhereClause)
.get(sqlParameters) .get(sqlParameters)
.recordCount; .recordCount;
let lots = []; let lots = [];
if (count > 0) { if (count > 0) {
const currentDate = dateToInteger(new Date());
lots = database lots = database
.prepare("select l.lotId, l.lotName," + .prepare("select l.lotId, l.lotName," +
" t.lotType," + " t.lotType," +

View File

@ -14,6 +14,7 @@ interface GetLotsFilters {
mapId ? : number | string; mapId ? : number | string;
lotTypeId ? : number | string; lotTypeId ? : number | string;
lotStatusId ? : number | string; lotStatusId ? : number | string;
occupancyStatus ? : "" | "occupied" | "unoccupied";
} }
interface GetLotsOptions { interface GetLotsOptions {
@ -57,8 +58,26 @@ export const getLots = (filters ? : GetLotsFilters, options ? : GetLotsOptions):
sqlParameters.push(filters.lotStatusId); sqlParameters.push(filters.lotStatusId);
} }
if (filters.occupancyStatus) {
if (filters.occupancyStatus === "occupied") {
sqlWhereClause += " and lotOccupancyCount > 0";
} else if (filters.occupancyStatus === "unoccupied") {
sqlWhereClause += " and (lotOccupancyCount is null or lotOccupancyCount = 0)";
}
}
const currentDate = dateToInteger(new Date());
const count: number = database.prepare("select count(*) as recordCount" + const count: number = database.prepare("select count(*) as recordCount" +
" from Lots l" + " from Lots l" +
(" left join (" +
"select lotId, count(lotOccupancyId) as lotOccupancyCount" +
" from LotOccupancies" +
" where recordDelete_timeMillis is null" +
" and occupancyStartDate <= " + currentDate +
" and (occupancyEndDate is null or occupancyEndDate >= " + currentDate + ")" +
" group by lotId" +
") o on l.lotId = o.lotId") +
sqlWhereClause) sqlWhereClause)
.get(sqlParameters) .get(sqlParameters)
.recordCount; .recordCount;
@ -67,8 +86,6 @@ export const getLots = (filters ? : GetLotsFilters, options ? : GetLotsOptions):
if (count > 0) { if (count > 0) {
const currentDate = dateToInteger(new Date());
lots = database lots = database
.prepare("select l.lotId, l.lotName," + .prepare("select l.lotId, l.lotName," +
" t.lotType," + " t.lotType," +

View File

@ -0,0 +1,10 @@
import type * as recordTypes from "../../types/recordTypes";
interface UpdateLotOccupancyForm {
lotOccupancyId: string | number;
occupancyTypeId: string | number;
lotId: string | number;
occupancyStartDateString: string;
occupancyEndDateString: string;
}
export declare function updateLotOccupancy(lotOccupancyForm: UpdateLotOccupancyForm, requestSession: recordTypes.PartialSession): boolean;
export default updateLotOccupancy;

View File

@ -0,0 +1,21 @@
import { dateStringToInteger } from "@cityssm/expressjs-server-js/dateTimeFns.js";
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export function updateLotOccupancy(lotOccupancyForm, requestSession) {
const database = sqlite(databasePath);
const rightNowMillis = Date.now();
const result = database
.prepare("update LotOccupancies" +
" set occupancyTypeId = ?," +
" lotId = ?," +
" occupancyStartDate = ?," +
" occupancyEndDate = ?," +
" recordUpdate_userName = ?," +
" recordUpdate_timeMillis = ?" +
" where lotOccupancyId = ?" +
" and recordDelete_timeMillis is null")
.run(lotOccupancyForm.occupancyTypeId, (lotOccupancyForm.lotId === "" ? undefined : lotOccupancyForm.lotId), dateStringToInteger(lotOccupancyForm.occupancyStartDateString), (lotOccupancyForm.occupancyEndDateString === "" ? undefined : dateStringToInteger(lotOccupancyForm.occupancyEndDateString)), requestSession.user.userName, rightNowMillis, lotOccupancyForm.lotOccupancyId);
database.close();
return result.changes > 0;
}
export default updateLotOccupancy;

View File

@ -0,0 +1,48 @@
import { dateStringToInteger } from "@cityssm/expressjs-server-js/dateTimeFns.js";
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
import type * as recordTypes from "../../types/recordTypes";
interface UpdateLotOccupancyForm {
lotOccupancyId: string | number;
occupancyTypeId: string | number;
lotId: string | number;
occupancyStartDateString: string;
occupancyEndDateString: string;
}
export function updateLotOccupancy(lotOccupancyForm: UpdateLotOccupancyForm, requestSession: recordTypes.PartialSession): boolean {
const database = sqlite(databasePath);
const rightNowMillis = Date.now();
const result = database
.prepare("update LotOccupancies" +
" set occupancyTypeId = ?," +
" lotId = ?," +
" occupancyStartDate = ?," +
" occupancyEndDate = ?," +
" recordUpdate_userName = ?," +
" recordUpdate_timeMillis = ?" +
" where lotOccupancyId = ?" +
" and recordDelete_timeMillis is null")
.run(lotOccupancyForm.occupancyTypeId,
(lotOccupancyForm.lotId === "" ? undefined : lotOccupancyForm.lotId),
dateStringToInteger(lotOccupancyForm.occupancyStartDateString),
(lotOccupancyForm.occupancyEndDateString === "" ? undefined : dateStringToInteger(lotOccupancyForm.occupancyEndDateString)),
requestSession.user.userName,
rightNowMillis,
lotOccupancyForm.lotOccupancyId);
database.close();
return result.changes > 0;
}
export default updateLotOccupancy;

View File

@ -6,6 +6,7 @@ import type * as recordTypes from "../types/recordTypes";
import type { import type {
cityssmGlobal cityssmGlobal
} from "@cityssm/bulma-webapp-js/src/types"; } from "@cityssm/bulma-webapp-js/src/types";
import type { import type {
BulmaJS BulmaJS
} from "@cityssm/bulma-js/types"; } from "@cityssm/bulma-js/types";

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,121 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
(() => {
const los = exports.los;
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const lotOccupancyId = document.querySelector("#lotOccupancy--lotOccupancyId").value;
const isCreate = (lotOccupancyId === "");
let hasUnsavedChanges = false;
const setUnsavedChanges = () => {
if (!hasUnsavedChanges) {
hasUnsavedChanges = true;
cityssm.enableNavBlocker();
}
};
const clearUnsavedChanges = () => {
hasUnsavedChanges = false;
cityssm.disableNavBlocker();
};
const formElement = document.querySelector("#form--lotOccupancy");
formElement.addEventListener("submit", (formEvent) => {
formEvent.preventDefault();
cityssm.postJSON(urlPrefix + "/lotOccupancies/" + (isCreate ? "doCreateLotOccupancy" : "doUpdateLotOccupancy"), formElement, (responseJSON) => {
if (responseJSON.success) {
clearUnsavedChanges();
if (isCreate) {
window.location.href = urlPrefix + "/lotOccupancies/" + responseJSON.lotOccupancyId + "/edit";
}
else {
bulmaJS.alert({
message: exports.aliases.occupancy + " Updated Successfully",
contextualColorName: "success"
});
}
}
else {
bulmaJS.alert({
title: "Error Saving " + exports.aliases.occupancy,
message: responseJSON.errorMessage,
contextualColorName: "danger"
});
}
});
});
document.querySelector("#lotOccupancy--lotName").addEventListener("click", () => {
let lotSelectCloseModalFunction;
let lotSelectFormElement;
let lotSelectResultsElement;
const selectLot = (clickEvent) => {
clickEvent.preventDefault();
const selectedLotElement = clickEvent.currentTarget;
document.querySelector("#lotOccupancy--lotId").value = selectedLotElement.dataset.lotId;
document.querySelector("#lotOccupancy--lotName").value = selectedLotElement.dataset.lotName;
setUnsavedChanges();
lotSelectCloseModalFunction();
};
const searchLots = () => {
lotSelectResultsElement.innerHTML = "<p class=\"has-text-centered\">" +
"<i class=\"fas fa-3x fa-pulse fa-spinner\" aria-hidden=\"true\"></i><br />" +
"Searching..." +
"</p>";
cityssm.postJSON(urlPrefix + "/lots/doSearchLots", lotSelectFormElement, (responseJSON) => {
if (responseJSON.count === 0) {
lotSelectResultsElement.innerHTML = "<div class=\"message is-info\">" +
"<p class=\"message-body\">" +
"No results." +
"</p>" +
"</div>";
return;
}
const panelElement = document.createElement("div");
panelElement.className = "panel";
for (const lot of responseJSON.lots) {
const panelBlockElement = document.createElement("a");
panelBlockElement.className = "panel-block is-block";
panelBlockElement.href = "#";
panelBlockElement.dataset.lotId = lot.lotId.toString();
panelBlockElement.dataset.lotName = lot.lotName;
panelBlockElement.innerHTML = "<div class=\"columns\">" +
("<div class=\"column\">" +
cityssm.escapeHTML(lot.lotName) + "<br />" +
"<span class=\"is-size-7\">" + cityssm.escapeHTML(lot.mapName) + "</span>" +
"</div>") +
("<div class=\"column\">" +
cityssm.escapeHTML(lot.lotStatus) + "<br />" +
"<span class=\"is-size-7\">" +
(lot.lotOccupancyCount > 0 ?
"Currently Occupied" : "") +
"</span>" +
"</div>") +
"</div>";
panelBlockElement.addEventListener("click", selectLot);
panelElement.append(panelBlockElement);
}
lotSelectResultsElement.innerHTML = "";
lotSelectResultsElement.append(panelElement);
});
};
cityssm.openHtmlModal("lotOccupancy-selectLot", {
onshow: (modalElement) => {
los.populateAliases(modalElement);
},
onshown: (modalElement, closeModalFunction) => {
bulmaJS.toggleHtmlClipped();
lotSelectCloseModalFunction = closeModalFunction;
const lotNameFilterElement = modalElement.querySelector("#lotSelect--lotName");
lotNameFilterElement.focus();
lotNameFilterElement.addEventListener("change", searchLots);
modalElement.querySelector("#lotSelect--occupancyStatus").addEventListener("change", searchLots);
lotSelectFormElement = modalElement.querySelector("#form--lotSelect");
lotSelectResultsElement = modalElement.querySelector("#resultsContainer--lotSelect");
lotSelectFormElement.addEventListener("submit", (submitEvent) => {
submitEvent.preventDefault();
});
},
onremoved: () => {
bulmaJS.toggleHtmlClipped();
}
});
});
los.initializeUnlockFieldButtons(formElement);
})();

View File

@ -0,0 +1,188 @@
/* eslint-disable 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;
(() => {
const los = (exports.los as globalTypes.LOS);
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const lotOccupancyId = (document.querySelector("#lotOccupancy--lotOccupancyId") as HTMLInputElement).value;
const isCreate = (lotOccupancyId === "");
// Main form
let hasUnsavedChanges = false;
const setUnsavedChanges = () => {
if (!hasUnsavedChanges) {
hasUnsavedChanges = true;
cityssm.enableNavBlocker();
}
};
const clearUnsavedChanges = () => {
hasUnsavedChanges = false;
cityssm.disableNavBlocker();
};
const formElement = document.querySelector("#form--lotOccupancy") as HTMLFormElement;
formElement.addEventListener("submit", (formEvent) => {
formEvent.preventDefault();
cityssm.postJSON(urlPrefix + "/lotOccupancies/" + (isCreate ? "doCreateLotOccupancy" : "doUpdateLotOccupancy"),
formElement,
(responseJSON: {
success: boolean;
lotOccupancyId ? : number;
errorMessage ? : string;
}) => {
if (responseJSON.success) {
clearUnsavedChanges();
if (isCreate) {
window.location.href = urlPrefix + "/lotOccupancies/" + responseJSON.lotOccupancyId + "/edit";
} else {
bulmaJS.alert({
message: exports.aliases.occupancy + " Updated Successfully",
contextualColorName: "success"
});
}
} else {
bulmaJS.alert({
title: "Error Saving " + exports.aliases.occupancy,
message: responseJSON.errorMessage,
contextualColorName: "danger"
});
}
});
});
// Lot Selector
document.querySelector("#lotOccupancy--lotName").addEventListener("click", () => {
let lotSelectCloseModalFunction: () => void;
let lotSelectFormElement: HTMLFormElement;
let lotSelectResultsElement: HTMLElement;
const selectLot = (clickEvent: Event) => {
clickEvent.preventDefault();
const selectedLotElement = clickEvent.currentTarget as HTMLElement;
(document.querySelector("#lotOccupancy--lotId") as HTMLInputElement).value = selectedLotElement.dataset.lotId;
(document.querySelector("#lotOccupancy--lotName") as HTMLInputElement).value = selectedLotElement.dataset.lotName;
setUnsavedChanges();
lotSelectCloseModalFunction();
};
const searchLots = () => {
lotSelectResultsElement.innerHTML = "<p class=\"has-text-centered\">" +
"<i class=\"fas fa-3x fa-pulse fa-spinner\" aria-hidden=\"true\"></i><br />" +
"Searching..." +
"</p>";
cityssm.postJSON(urlPrefix + "/lots/doSearchLots", lotSelectFormElement, (responseJSON: {
count: number;
lots: recordTypes.Lot[];
}) => {
if (responseJSON.count === 0) {
lotSelectResultsElement.innerHTML = "<div class=\"message is-info\">" +
"<p class=\"message-body\">" +
"No results." +
"</p>" +
"</div>";
return;
}
const panelElement = document.createElement("div");
panelElement.className = "panel";
for (const lot of responseJSON.lots) {
const panelBlockElement = document.createElement("a");
panelBlockElement.className = "panel-block is-block";
panelBlockElement.href = "#";
panelBlockElement.dataset.lotId = lot.lotId.toString();
panelBlockElement.dataset.lotName = lot.lotName;
panelBlockElement.innerHTML = "<div class=\"columns\">" +
("<div class=\"column\">" +
cityssm.escapeHTML(lot.lotName) + "<br />" +
"<span class=\"is-size-7\">" + cityssm.escapeHTML(lot.mapName) + "</span>" +
"</div>") +
("<div class=\"column\">" +
cityssm.escapeHTML(lot.lotStatus as string) + "<br />" +
"<span class=\"is-size-7\">" +
(lot.lotOccupancyCount > 0 ?
"Currently Occupied" : "") +
"</span>" +
"</div>") +
"</div>";
panelBlockElement.addEventListener("click", selectLot);
panelElement.append(panelBlockElement);
}
lotSelectResultsElement.innerHTML = "";
lotSelectResultsElement.append(panelElement);
});
}
cityssm.openHtmlModal("lotOccupancy-selectLot", {
onshow: (modalElement) => {
los.populateAliases(modalElement);
},
onshown: (modalElement, closeModalFunction) => {
bulmaJS.toggleHtmlClipped();
lotSelectCloseModalFunction = closeModalFunction;
const lotNameFilterElement = modalElement.querySelector("#lotSelect--lotName") as HTMLInputElement;
lotNameFilterElement.focus();
lotNameFilterElement.addEventListener("change", searchLots);
modalElement.querySelector("#lotSelect--occupancyStatus").addEventListener("change", searchLots);
lotSelectFormElement = modalElement.querySelector("#form--lotSelect");
lotSelectResultsElement = modalElement.querySelector("#resultsContainer--lotSelect");
lotSelectFormElement.addEventListener("submit", (submitEvent) => {
submitEvent.preventDefault();
});
},
onremoved: () => {
bulmaJS.toggleHtmlClipped();
}
});
});
los.initializeUnlockFieldButtons(formElement);
})();

View File

@ -0,0 +1,45 @@
<div class="modal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<h3 class="modal-card-title">
Select a <span class="alias" data-alias="Lot"></span>
</h3>
<button class="delete is-close-modal-button" aria-label="close" type="button"></button>
</header>
<section class="modal-card-body">
<div class="box">
<form id="form--lotSelect">
<input name="limit" type="hidden" value="100" />
<input name="offset" type="hidden" value="0" />
<div class="field">
<div class="control has-icons-left">
<input class="input" id="lotSelect--lotName" name="lotName" type="text" />
<span class="icon is-small is-left">
<i class="fas fa-search" aria-hidden="true"></i>
</span>
</div>
</div>
<div class="field">
<div class="control has-icons-left">
<div class="select is-fullwidth">
<select id="lotSelect--occupancyStatus" name="occupancyStatus">
<option value="">(All Statuses)</option>
<option value="unoccupied" selected>Currently Unoccupied</option>
<option value="occupied">Currently Occupied</option>
</select>
</div>
<span class="icon is-small is-left">
<i class="fas fa-filter" aria-hidden="true"></i>
</span>
</div>
</div>
</form>
</div>
<div id="resultsContainer--lotSelect"></div>
</section>
<footer class="modal-card-foot justify-right">
<button class="button is-close-modal-button" type="button">Cancel</button>
</footer>
</div>
</div>

View File

@ -0,0 +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;let s=!1;const a=document.querySelector("#form--lotOccupancy");a.addEventListener("submit",e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/"+(c?"doCreateLotOccupancy":"doUpdateLotOccupancy"),a,e=>{e.success?(s=!1,cityssm.disableNavBlocker(),c?window.location.href=t+"/lotOccupancies/"+e.lotOccupancyId+"/edit":bulmaJS.alert({message:exports.aliases.occupancy+" Updated Successfully",contextualColorName:"success"})):bulmaJS.alert({title:"Error Saving "+exports.aliases.occupancy,message:e.errorMessage,contextualColorName:"danger"})})}),document.querySelector("#lotOccupancy--lotName").addEventListener("click",()=>{let c,a,o;const l=e=>{e.preventDefault();const t=e.currentTarget;document.querySelector("#lotOccupancy--lotId").value=t.dataset.lotId,document.querySelector("#lotOccupancy--lotName").value=t.dataset.lotName,s||(s=!0,cityssm.enableNavBlocker()),c()},n=()=>{o.innerHTML='<p class="has-text-centered"><i class="fas fa-3x fa-pulse fa-spinner" aria-hidden="true"></i><br />Searching...</p>',cityssm.postJSON(t+"/lots/doSearchLots",a,e=>{if(0===e.count)return void(o.innerHTML='<div class="message is-info"><p class="message-body">No results.</p></div>');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='<div class="columns"><div class="column">'+cityssm.escapeHTML(c.lotName)+'<br /><span class="is-size-7">'+cityssm.escapeHTML(c.mapName)+'</span></div><div class="column">'+cityssm.escapeHTML(c.lotStatus)+'<br /><span class="is-size-7">'+(c.lotOccupancyCount>0?"Currently Occupied":"")+"</span></div></div>",e.addEventListener("click",l),t.append(e)}o.innerHTML="",o.append(t)})};cityssm.openHtmlModal("lotOccupancy-selectLot",{onshow:t=>{e.populateAliases(t)},onshown:(e,t)=>{bulmaJS.toggleHtmlClipped(),c=t;const s=e.querySelector("#lotSelect--lotName");s.focus(),s.addEventListener("change",n),e.querySelector("#lotSelect--occupancyStatus").addEventListener("change",n),a=e.querySelector("#form--lotSelect"),o=e.querySelector("#resultsContainer--lotSelect"),a.addEventListener("submit",e=>{e.preventDefault()})},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),e.initializeUnlockFieldButtons(a)})();

View File

@ -3,10 +3,12 @@ import handler_search from "../handlers/lotOccupancies-get/search.js";
import handler_doSearchLotOccupancies from "../handlers/lotOccupancies-post/doSearchLotOccupancies.js"; import handler_doSearchLotOccupancies from "../handlers/lotOccupancies-post/doSearchLotOccupancies.js";
import handler_view from "../handlers/lotOccupancies-get/view.js"; import handler_view from "../handlers/lotOccupancies-get/view.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 * 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);
router.post("/doSearchLotOccupancies", handler_doSearchLotOccupancies); router.post("/doSearchLotOccupancies", handler_doSearchLotOccupancies);
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);
export default router; export default router;

View File

@ -6,7 +6,9 @@ import handler_search from "../handlers/lotOccupancies-get/search.js";
import handler_doSearchLotOccupancies from "../handlers/lotOccupancies-post/doSearchLotOccupancies.js"; import handler_doSearchLotOccupancies from "../handlers/lotOccupancies-post/doSearchLotOccupancies.js";
import handler_view from "../handlers/lotOccupancies-get/view.js"; import handler_view from "../handlers/lotOccupancies-get/view.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 * as permissionHandlers from "../handlers/permissions.js"; import * as permissionHandlers from "../handlers/permissions.js";
@ -22,7 +24,7 @@ router.post("/doSearchLotOccupancies",
handler_doSearchLotOccupancies); handler_doSearchLotOccupancies);
router.get("/:lotOccupancyId", router.get("/:lotOccupancyId",
handler_view); handler_view);
@ -30,5 +32,9 @@ router.get("/:lotOccupancyId/edit",
permissionHandlers.updateGetHandler, permissionHandlers.updateGetHandler,
handler_edit); handler_edit);
router.post("/doUpdateLotOccupancy",
permissionHandlers.updatePostHandler,
handler_doUpdateLotOccupancy);
export default router; export default router;

View File

@ -23,6 +23,12 @@ export interface Config {
occupant?: string; occupant?: string;
occupants?: string; occupants?: string;
}; };
settings?: {
lotOccupancy?: {
lotIdIsRequired?: boolean;
occupancyEndDateIsRequired?: boolean;
};
};
} }
interface ConfigApplication { interface ConfigApplication {
applicationName?: string; applicationName?: string;

View File

@ -12,7 +12,7 @@ export interface Config {
canLogin?: string[]; canLogin?: string[];
canUpdate?: string[]; canUpdate?: string[];
isAdmin?: string[]; isAdmin?: string[];
}, };
aliases?: { aliases?: {
lot?: string; lot?: string;
lots?: string; lots?: string;
@ -22,7 +22,13 @@ export interface Config {
occupancies?: string; occupancies?: string;
occupant?: string; occupant?: string;
occupants?: string; occupants?: string;
} };
settings?: {
lotOccupancy?: {
lotIdIsRequired?: boolean;
occupancyEndDateIsRequired?: boolean;
}
};
} }
interface ConfigApplication { interface ConfigApplication {

View File

@ -107,6 +107,30 @@ export interface Occupant extends Record {
occupantPostalCode?: string; occupantPostalCode?: string;
occupantPhoneNumber?: string; occupantPhoneNumber?: string;
} }
export interface Fee extends Record {
feeId?: number;
feeName?: string;
occupancyTypeId?: number;
lotTypeId?: number;
feeAmount?: number;
feeFunction?: string;
isRequired?: boolean;
}
export interface LotOccupancyFee extends Fee, Record {
lotOccupancyId?: number;
feeAmount?: number;
}
export interface LotOccupancyTransaction extends Record {
lotOccupancyId?: number;
transactionIndex?: number;
transactionDate?: number;
transactionDateString?: string;
transactionTime?: number;
transactionTimeString?: string;
tranactionAmount?: number;
externalReceiptNumber?: string;
transactionNote?: string;
}
export interface LotOccupancyOccupant extends Occupant, Record { export interface LotOccupancyOccupant extends Occupant, Record {
lotOccupancyId?: number; lotOccupancyId?: number;
lotOccupantIndex?: number; lotOccupantIndex?: number;
@ -122,6 +146,11 @@ export interface LotOccupancyComment extends Record {
lotOccupancyCommentTimeString?: string; lotOccupancyCommentTimeString?: string;
lotOccupancyComment?: string; lotOccupancyComment?: string;
} }
export interface LotOccupancyField extends OccupancyTypeField, Record {
lotOccupancyId?: number;
occupancyTypeFieldId?: number;
lotOccupancyFieldValue?: string;
}
export interface LotOccupancy extends Record { export interface LotOccupancy extends Record {
lotOccupancyId?: number; lotOccupancyId?: number;
occupancyTypeId?: number; occupancyTypeId?: number;
@ -134,8 +163,11 @@ export interface LotOccupancy extends Record {
occupancyStartDateString?: string; occupancyStartDateString?: string;
occupancyEndDate?: number; occupancyEndDate?: number;
occupancyEndDateString?: string; occupancyEndDateString?: string;
lotOccupancyFields?: LotOccupancyField[];
lotOccupancyComments?: LotOccupancyComment[]; lotOccupancyComments?: LotOccupancyComment[];
lotOccupancyOccupants?: LotOccupancyOccupant[]; lotOccupancyOccupants?: LotOccupancyOccupant[];
lotOccupancyFees?: LotOccupancyFee[];
lotOccupancyTransactions?: LotOccupancyTransaction[];
} }
export interface User { export interface User {
userName: string; userName: string;

View File

@ -151,6 +151,39 @@ export interface Occupant extends Record {
} }
export interface Fee extends Record {
feeId?: number;
feeName?: string;
occupancyTypeId?: number;
lotTypeId?: number;
feeAmount?: number;
feeFunction?: string;
isRequired?: boolean;
}
export interface LotOccupancyFee extends Fee, Record {
lotOccupancyId?: number;
feeAmount?: number;
}
export interface LotOccupancyTransaction extends Record {
lotOccupancyId?: number;
transactionIndex?: number;
transactionDate?: number;
transactionDateString?: string;
transactionTime?: number;
transactionTimeString?: string;
tranactionAmount?: number;
externalReceiptNumber?: string;
transactionNote?: string;
}
export interface LotOccupancyOccupant extends Occupant, Record { export interface LotOccupancyOccupant extends Occupant, Record {
lotOccupancyId ? : number; lotOccupancyId ? : number;
lotOccupantIndex ? : number; lotOccupantIndex ? : number;
@ -174,6 +207,13 @@ export interface LotOccupancyComment extends Record {
} }
export interface LotOccupancyField extends OccupancyTypeField, Record {
lotOccupancyId?: number;
occupancyTypeFieldId?: number;
lotOccupancyFieldValue?: string;
}
export interface LotOccupancy extends Record { export interface LotOccupancy extends Record {
lotOccupancyId ? : number; lotOccupancyId ? : number;
@ -192,8 +232,11 @@ export interface LotOccupancy extends Record {
occupancyEndDate ? : number; occupancyEndDate ? : number;
occupancyEndDateString ? : string; occupancyEndDateString ? : string;
lotOccupancyFields? : LotOccupancyField[];
lotOccupancyComments ? : LotOccupancyComment[]; lotOccupancyComments ? : LotOccupancyComment[];
lotOccupancyOccupants ? : LotOccupancyOccupant[]; lotOccupancyOccupants ? : LotOccupancyOccupant[];
lotOccupancyFees?: LotOccupancyFee[];
lotOccupancyTransactions?: LotOccupancyTransaction[];
} }

View File

@ -16,28 +16,142 @@
</li> </li>
<li> <li>
<a href="<%= urlPrefix %>/lotOccupancies/<%= lotOccupancy.lotOccupancyId %>"> <a href="<%= urlPrefix %>/lotOccupancies/<%= lotOccupancy.lotOccupancyId %>">
<%= configFunctions.getProperty("aliases.occupancy") %> View <%= configFunctions.getProperty("aliases.occupancy") %>: <%= lotOccupancy.lotName %>
</a> </a>
</li> </li>
<li class="is-active"> <li class="is-active">
<a href="#" aria-current="page"> <a href="#" aria-current="page">
<%= configFunctions.getProperty("aliases.occupancy") %> Edit Update <%= configFunctions.getProperty("aliases.occupancy") %>
</a> </a>
</li> </li>
</ul> </ul>
</nav> </nav>
<h1 class="title is-1"> <h1 class="title is-1">
<%= configFunctions.getProperty("aliases.occupancy") %> Edit <%= configFunctions.getProperty("aliases.occupancy") %> Update
</h1> </h1>
<div class="fixed-container is-fixed-bottom-right mx-4 my-4 has-text-right is-hidden-print"> <form id="form--lotOccupancy">
<button class="button is-circle is-primary has-tooltip-left" data-tooltip="Update <%= configFunctions.getProperty("aliases.occupancy") %>" type="submit"> <div class="fixed-container is-fixed-bottom-right mx-4 my-4 has-text-right is-hidden-print">
<i class="fas fa-save" aria-hidden="true"></i> <button class="button is-circle is-primary has-tooltip-left" data-tooltip="Update <%= configFunctions.getProperty("aliases.occupancy") %>" type="submit">
<span class="sr-only">Update <%= configFunctions.getProperty("aliases.occupancy") %></span> <i class="fas fa-save" aria-hidden="true"></i>
</button> <span class="sr-only">Update <%= configFunctions.getProperty("aliases.occupancy") %></span>
</div> </button>
</div>
<input id="lotOccupancy--lotOccupancyId" name="lotOccupancyId" type="hidden" value="<%= lotOccupancy.lotOccupancyId %>" />
<div class="columns">
<div class="column">
<label class="label" for="lotOccupancy--occupancyTypeId">
<%= configFunctions.getProperty("aliases.occupancy") %> Type
</label>
<div class="field has-addons">
<div class="control is-expanded">
<div class="select is-fullwidth">
<select id="lotOccupancy--occupancyTypeId" name="occupancyTypeId" required>
<% if (isCreate) { %>
<option value="">(No Type)</option>
<% } %>
<% let typeIsFound = false; %>
<% for (const occupancyType of occupancyTypes) { %>
<%
if (lotOccupancy.occupancyTypeId === occupancyType.occupancyTypeId) {
typeIsFound = true;
}
%>
<option value="<%= occupancyType.occupancyTypeId %>"
<%= (lotOccupancy.occupancyTypeId === occupancyType.occupancyTypeId ? " selected" : "") %>
<%= (!isCreate && lotOccupancy.occupancyTypeId !== occupancyType.occupancyTypeId ? " disabled" : "") %>>
<%= occupancyType.occupancyType %>
</option>
<% } %>
<% if (lotOccupancy.occupancyTypeId && !typeIsFound) { %>
<option value="<%= lotOccupancy.occupancyTypeId %>" selected>
<%= lotOccupancy.occupancyType %>
</option>
<% } %>
</select>
</div>
</div>
<div class="control">
<button class="button is-unlock-field-button" type="button" title="Unlock Field">
<i class="fas fa-unlock" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
<div class="column">
<input id="lotOccupancy--lotId" name="lotId" type="hidden" value="<%= lotOccupancy.lotId %>" />
<label class="label" for="lotOccupancy--lotName">
<%= configFunctions.getProperty("aliases.lot") %>
</label>
<div class="field has-addons">
<div class="control is-expanded">
<input class="input has-text-left" id="lotOccupancy--lotName" type="button" value="<%= lotOccupancy.lotName %>"
<%= (configFunctions.getProperty("settings.lotOccupancy.lotIdIsRequired") ? " required" : "") %>
disabled />
</div>
<div class="control">
<button class="button is-unlock-field-button" type="button" title="Unlock Field">
<i class="fas fa-unlock" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</div>
<div class="columns">
<div class="column">
<div class="field">
<label class="label" for="lotOccupancy--occupancyStartDateString">Start Date</label>
<div class="control">
<input class="input" id="lotOccupancy--occupancyStartDateString" name="occupancyStartDateString" type="date"
value="<%= lotOccupancy.occupancyStartDateString %>" required />
</div>
</div>
</div>
<div class="column">
<div class="field">
<label class="label" for="lotOccupancy--occupancyEndDateString">End Date</label>
<div class="control">
<input class="input" id="lotOccupancy--occupancyEndDateString" name="occupancyEndDateString" type="date"
value="<%= lotOccupancy.occupancyEndDateString %>"
min=""<%= lotOccupancy.occupancyStartDateString %>"
<%= (configFunctions.getProperty("settings.lotOccupancy.occupancyEndDateIsRequired") ? " required" : "") %> />
</div>
</div>
</div>
</div>
</form>
<% if (isCreate) { %>
<% } else { %>
<h2 class="title is-4"><%= configFunctions.getProperty("aliases.occupants") %></h2>
<h2 class="title is-4">Comments</h2>
<div class="columns">
<div class="column">
<h2 class="title is-4">Fees</h2>
</div>
<div class="column">
<h2 class="title is-4">Transactions</h2>
</div>
</div>
<% } %>
<%- include('_footerA'); -%> <%- include('_footerA'); -%>
<script>
exports.lotOccupancyOccupants = <%- JSON.stringify(lotOccupancy.lotOccupancyOccupants) %>;
exports.lotOccupancyComments = <%- JSON.stringify(lotOccupancy.lotOccupancyComments) %>;
exports.lotOccupancyFees = <%- JSON.stringify(lotOccupancy.lotOccupancyFees) %>;
exports.lotOccupancyTransactions = <%- JSON.stringify(lotOccupancy.lotOccupancyTransactions) %>;
</script>
<script src="<%= urlPrefix %>/javascripts/lotOccupancyEdit.min.js"></script>
<%- include('_footerB'); -%> <%- include('_footerB'); -%>

View File

@ -1,39 +1,144 @@
<%- include('_header'); -%> <%- include('_header'); -%>
<nav class="breadcrumb"> <nav class="breadcrumb">
<ul> <ul>
<li><a href="<%= urlPrefix %>/dashboard">Home</a></li> <li><a href="<%= urlPrefix %>/dashboard">Home</a></li>
<li> <li>
<a href="<%= urlPrefix %>/lotOccupancies"> <a href="<%= urlPrefix %>/lotOccupancies">
<span class="icon is-small"> <span class="icon is-small">
<span class="fa-layers fa-fw" aria-hidden="true"> <span class="fa-layers fa-fw" aria-hidden="true">
<i class="fas fa-vector-square"></i> <i class="fas fa-vector-square"></i>
<i class="fas fa-user" data-fa-transform="shrink-10"></i> <i class="fas fa-user" data-fa-transform="shrink-10"></i>
</span>
</span> </span>
</span> <span><%= configFunctions.getProperty("aliases.lots") %>
<span><%= configFunctions.getProperty("aliases.lots") %> <%= configFunctions.getProperty("aliases.occupancies") %></span> <%= configFunctions.getProperty("aliases.occupancies") %></span>
</a> </a>
</li> </li>
<li class="is-active"><a href="#" aria-current="page"> <li class="is-active">
<%= configFunctions.getProperty("aliases.occupancy") %> View <a href="#" aria-current="page">
</a></li> <%= configFunctions.getProperty("aliases.occupancy") %>: <%= lotOccupancy.lotName %>
</ul> </a>
</li>
</ul>
</nav> </nav>
<h1 class="title is-1"> <h1 class="title is-1">
<%= configFunctions.getProperty("aliases.occupancy") %> View <%= configFunctions.getProperty("aliases.occupancy") %>: <%= lotOccupancy.lotName %>
</h1> </h1>
<% if (user.userProperties.canUpdate) { %> <% if (user.userProperties.canUpdate) { %>
<div class="fixed-container is-fixed-bottom-right mx-4 my-4 has-text-right is-hidden-print"> <div class="fixed-container is-fixed-bottom-right mx-4 my-4 has-text-right is-hidden-print">
<a class="button is-circle is-primary has-tooltip-left" data-tooltip="Update <%= configFunctions.getProperty("aliases.occupancy") %>" href="<%= urlPrefix %>/lotOccupancies/<%= lotOccupancy.lotOccupancyId %>/edit"> <a class="button is-circle is-primary has-tooltip-left"
<i class="fas fa-pencil-alt" aria-hidden="true"></i> data-tooltip="Update <%= configFunctions.getProperty("aliases.occupancy") %>"
<span class="sr-only">Update <%= configFunctions.getProperty("aliases.occupancy") %></span> href="<%= urlPrefix %>/lotOccupancies/<%= lotOccupancy.lotOccupancyId %>/edit">
<i class="fas fa-pencil-alt" aria-hidden="true"></i>
<span class="sr-only">Update <%= configFunctions.getProperty("aliases.occupancy") %></span>
</a> </a>
</div> </div>
<% } %> <% } %>
<div class="columns">
<div class="column">
<p>
<strong><%= configFunctions.getProperty("aliases.occupancy") %> Type</strong><br />
<%= lotOccupancy.occupancyType %>
</p>
</div>
<div class="column">
<p>
<strong><%= configFunctions.getProperty("aliases.lot") %></strong><br />
<a href="<%= urlPrefix %>/lots/<%= lotOccupancy.lotId %>"><%= lotOccupancy.lotName %></a>
</p>
<p class="mt-2">
<strong><%= configFunctions.getProperty("aliases.map") %></strong><br />
<a href="<%= urlPrefix %>/maps/<%= lotOccupancy.mapId %>"><%= lotOccupancy.mapName %></a>
</p>
</div>
<div class="column">
<p>
<strong>Start Date</strong><br />
<%= lotOccupancy.occupancyStartDateString %>
</p>
<p class="mt-2">
<strong>End Date</strong><br />
<%= (lotOccupancy.occupancyEndDateString === "" ? "(No End Date)" : lotOccupancy.occupancyEndDateString) %>
</p>
</div>
</div>
<h2 class="title is-4"><%= configFunctions.getProperty("aliases.occupants") %></h2>
<% if (lotOccupancy.lotOccupancyOccupants.length === 0) { %>
<div class="message is-warning">
<p class="message-body">There are no <%= configFunctions.getProperty("aliases.occupants").toLowerCase() %>
associated with this record.</p>
</div>
<% } else { %>
<table class="table is-fullwidth is-striped is-hoverable">
<thead>
<tr>
<th><%= configFunctions.getProperty("aliases.occupant") %> Type</th>
<th><%= configFunctions.getProperty("aliases.occupant") %></th>
<th>Address</th>
<th>Phone Number</th>
</tr>
</thead>
<tbody>
<% for (const lotOccupancyOccupant of lotOccupancy.lotOccupancyOccupants) { %>
<tr>
<td><%= lotOccupancyOccupant.lotOccupantType %></td>
<td><%= lotOccupancyOccupant.occupantName %></td>
<td>
<%= lotOccupancyOccupant.occupantAddress1 %><br />
<% if (lotOccupancyOccupant.occupantAddress2 && lotOccupancyOccupant.occupantAddress2 !== "") { %>
<%= lotOccupancyOccupant.occupantAddress2 %><br />
<% } %>
<%= lotOccupancyOccupant.occupantCity %>, <%= lotOccupancyOccupant.occupantProvince %><br />
<%= lotOccupancyOccupant.occupantPostalCode %>
</td>
<td>
<%= lotOccupancyOccupant.occupantPhoneNumber %>
</td>
</tr>
<% } %>
</tbody>
</table>
<% } %>
<% if (lotOccupancy.lotOccupancyComments.length > 0) { %>
<h2 class="title is-4">Comments</h2>
<% } %>
<div class="columns">
<div class="column">
<h2 class="title is-4">Fees</h2>
<% if (lotOccupancy.lotOccupancyFees.length === 0) { %>
<div class="message is-info">
<p class="message-body">
There are no fees applied to this <%= configFunctions.getProperty("aliases.occupancy").toLowerCase() %> record.
</p>
</div>
<% } else { %>
<% } %>
</div>
<div class="column">
<h2 class="title is-4">Transactions</h2>
<% if (lotOccupancy.lotOccupancyTransactions.length === 0) { %>
<div class="message is-info">
<p class="message-body">
There are no transactions associated with this <%= configFunctions.getProperty("aliases.occupancy").toLowerCase() %> record.
</p>
</div>
<% } else { %>
<% } %>
</div>
</div>
<%- include('_footerA'); -%> <%- include('_footerA'); -%>
<%- include('_footerB'); -%> <%- include('_footerB'); -%>