@7365admin1/core 3.41.0 → 3.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/dist/index.d.ts +22 -1
- package/dist/index.js +333 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +579 -272
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/test/site-name.util.test.mjs +146 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @iservice365/core
|
|
2
2
|
|
|
3
|
+
## 3.42.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ff7b915: release changes in payment api
|
|
8
|
+
|
|
9
|
+
## 3.41.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 8eb6601: Stop rejecting site names that only differ by their building number
|
|
14
|
+
|
|
15
|
+
Adding a site refused any name within a Levenshtein distance of 2 of an
|
|
16
|
+
existing site in the same organisation, and told the user to contact support.
|
|
17
|
+
That is exactly one character, so "Winsland House II" could not be added next
|
|
18
|
+
to "Winsland House I". The same rule blocked "Tower A" beside "Tower B",
|
|
19
|
+
"Phase 2" beside "Phase 1" and "Block 15" beside "Block 5" — the standard way
|
|
20
|
+
buildings are named here.
|
|
21
|
+
|
|
22
|
+
The check now treats trailing numbers, Roman numerals and single letters as the
|
|
23
|
+
part that tells two buildings apart: if they differ, the names are different
|
|
24
|
+
sites and the distance is never measured. Real duplicates are still refused —
|
|
25
|
+
an exact repeat, a different capitalisation, stray or doubled whitespace,
|
|
26
|
+
punctuation-only differences, and a one or two character typo within the same
|
|
27
|
+
building. "House 1" and "House I" are still read as the same building.
|
|
28
|
+
|
|
29
|
+
The refusal now names the site it matched and says what to do about it instead
|
|
30
|
+
of pointing the user at support.
|
|
31
|
+
|
|
32
|
+
`site.repo.getByExactName` also built its case-insensitive regex from the raw
|
|
33
|
+
name; a name containing regex characters either threw or matched a site it is
|
|
34
|
+
not. It is escaped.
|
|
35
|
+
|
|
3
36
|
## 3.41.0
|
|
4
37
|
|
|
5
38
|
### Minor Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -6907,6 +6907,9 @@ declare function useRedDotPaymentController(): {
|
|
|
6907
6907
|
redirectPaymentTransaction: (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
|
|
6908
6908
|
enquirePaymentTransaction: (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
|
|
6909
6909
|
createPayment: (req: Request, res: Response, next: NextFunction) => Promise<Response<any, Record<string, any>> | undefined>;
|
|
6910
|
+
getPaymentByReference: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
6911
|
+
updateStatus: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
6912
|
+
getPaidBills: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
6910
6913
|
};
|
|
6911
6914
|
|
|
6912
6915
|
interface IDirectPayment extends IBaseModel {
|
|
@@ -6945,6 +6948,8 @@ declare const useRedDotPaymentSvc: () => {
|
|
|
6945
6948
|
type TBillingItems = {
|
|
6946
6949
|
_id: ObjectId | string;
|
|
6947
6950
|
name: string;
|
|
6951
|
+
amount: number;
|
|
6952
|
+
referenceNumber: string;
|
|
6948
6953
|
};
|
|
6949
6954
|
type TBillingPayments = {
|
|
6950
6955
|
_id?: ObjectId;
|
|
@@ -6967,11 +6972,27 @@ type TBillingPayments = {
|
|
|
6967
6972
|
|
|
6968
6973
|
declare const useRedDotPaymentRepo: () => {
|
|
6969
6974
|
paySingleUnitBill: (refId: string, payload: Partial<TUnitBilling>) => Promise<mongodb.WithId<bson.Document> | null>;
|
|
6970
|
-
createPayment: (type: string, payload: Partial<TBillingPayments>, session?: ClientSession) => Promise<
|
|
6975
|
+
createPayment: (type: string, payload: Partial<TBillingPayments>, session?: ClientSession) => Promise<string | undefined>;
|
|
6971
6976
|
payMultipleUnitBill: (refId: string[], payload: Partial<TUnitBilling>) => Promise<{
|
|
6972
6977
|
message: string;
|
|
6973
6978
|
remainingBalance: number;
|
|
6974
6979
|
}>;
|
|
6980
|
+
getPaidBills: ({ search, page, limit, sort, month, year, unitId, }: {
|
|
6981
|
+
search?: string | undefined;
|
|
6982
|
+
page?: number | undefined;
|
|
6983
|
+
limit?: number | undefined;
|
|
6984
|
+
sort?: Record<string, any> | undefined;
|
|
6985
|
+
month?: string | undefined;
|
|
6986
|
+
year?: string | undefined;
|
|
6987
|
+
unitId: string | ObjectId;
|
|
6988
|
+
}, session?: ClientSession) => Promise<{
|
|
6989
|
+
items: any[];
|
|
6990
|
+
pages: number;
|
|
6991
|
+
pageRange: string;
|
|
6992
|
+
}>;
|
|
6993
|
+
createIndexes: () => Promise<void>;
|
|
6994
|
+
getPaymentByReference: (referenceNumber: string, session?: ClientSession) => Promise<TBillingPayments>;
|
|
6995
|
+
updateStatus: (referenceNumber: string, status: string, session?: ClientSession) => Promise<mongodb.UpdateResult<bson.Document>>;
|
|
6975
6996
|
};
|
|
6976
6997
|
|
|
6977
6998
|
declare enum VerificationType {
|
package/dist/index.js
CHANGED
|
@@ -536,13 +536,13 @@ var require_logger = __commonJS({
|
|
|
536
536
|
"use strict";
|
|
537
537
|
exports.__esModule = true;
|
|
538
538
|
var _utils = require_utils();
|
|
539
|
-
var
|
|
539
|
+
var logger202 = {
|
|
540
540
|
methodMap: ["debug", "info", "warn", "error"],
|
|
541
541
|
level: "info",
|
|
542
542
|
// Maps a given level value to the `methodMap` indexes above.
|
|
543
543
|
lookupLevel: function lookupLevel(level) {
|
|
544
544
|
if (typeof level === "string") {
|
|
545
|
-
var levelMap = _utils.indexOf(
|
|
545
|
+
var levelMap = _utils.indexOf(logger202.methodMap, level.toLowerCase());
|
|
546
546
|
if (levelMap >= 0) {
|
|
547
547
|
level = levelMap;
|
|
548
548
|
} else {
|
|
@@ -553,9 +553,9 @@ var require_logger = __commonJS({
|
|
|
553
553
|
},
|
|
554
554
|
// Can be overridden in the host environment
|
|
555
555
|
log: function log(level) {
|
|
556
|
-
level =
|
|
557
|
-
if (typeof console !== "undefined" &&
|
|
558
|
-
var method =
|
|
556
|
+
level = logger202.lookupLevel(level);
|
|
557
|
+
if (typeof console !== "undefined" && logger202.lookupLevel(logger202.level) <= level) {
|
|
558
|
+
var method = logger202.methodMap[level];
|
|
559
559
|
if (!console[method]) {
|
|
560
560
|
method = "log";
|
|
561
561
|
}
|
|
@@ -566,7 +566,7 @@ var require_logger = __commonJS({
|
|
|
566
566
|
}
|
|
567
567
|
}
|
|
568
568
|
};
|
|
569
|
-
exports["default"] =
|
|
569
|
+
exports["default"] = logger202;
|
|
570
570
|
module2.exports = exports["default"];
|
|
571
571
|
}
|
|
572
572
|
});
|
|
@@ -11666,8 +11666,9 @@ function useSiteRepo() {
|
|
|
11666
11666
|
} catch (error2) {
|
|
11667
11667
|
throw new import_node_server_utils19.BadRequestError("Invalid org ID format.");
|
|
11668
11668
|
}
|
|
11669
|
+
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11669
11670
|
const query = {
|
|
11670
|
-
name: { $regex: new RegExp(`^${
|
|
11671
|
+
name: { $regex: new RegExp(`^${escapedName}$`, "i") },
|
|
11671
11672
|
// Case-insensitive exact match
|
|
11672
11673
|
orgId,
|
|
11673
11674
|
status: { $ne: "deleted" }
|
|
@@ -32537,7 +32538,57 @@ function useCustomerSiteRepo() {
|
|
|
32537
32538
|
|
|
32538
32539
|
// src/services/customer-site.service.ts
|
|
32539
32540
|
var import_node_server_utils96 = require("@7365admin1/node-server-utils");
|
|
32541
|
+
|
|
32542
|
+
// src/utils/site-name.util.ts
|
|
32540
32543
|
var import_fast_levenshtein = __toESM(require("fast-levenshtein"));
|
|
32544
|
+
var ROMAN = /^(?=[ivx])(x{0,3})(ix|iv|v?i{0,3})$/;
|
|
32545
|
+
var ROMAN_VALUE = { i: 1, v: 5, x: 10 };
|
|
32546
|
+
function normalizeSiteName(name) {
|
|
32547
|
+
return String(name ?? "").normalize("NFKC").toLowerCase().replace(/[‘’']/g, "").replace(/[^a-z0-9]+/g, " ").trim();
|
|
32548
|
+
}
|
|
32549
|
+
function romanToNumber(token) {
|
|
32550
|
+
let total = 0;
|
|
32551
|
+
for (let i = 0; i < token.length; i++) {
|
|
32552
|
+
const value = ROMAN_VALUE[token[i]];
|
|
32553
|
+
const next = ROMAN_VALUE[token[i + 1]];
|
|
32554
|
+
total += next && next > value ? -value : value;
|
|
32555
|
+
}
|
|
32556
|
+
return total;
|
|
32557
|
+
}
|
|
32558
|
+
function distinguishingTokens(normalized) {
|
|
32559
|
+
if (!normalized)
|
|
32560
|
+
return [];
|
|
32561
|
+
return normalized.split(" ").filter((t) => /^\d+$/.test(t) || ROMAN.test(t) || /^[a-z]$/.test(t)).map((t) => ROMAN.test(t) ? String(romanToNumber(t)) : t);
|
|
32562
|
+
}
|
|
32563
|
+
function matchSiteName(candidate, existing) {
|
|
32564
|
+
const a = normalizeSiteName(candidate);
|
|
32565
|
+
const b = normalizeSiteName(existing);
|
|
32566
|
+
if (!a || !b)
|
|
32567
|
+
return null;
|
|
32568
|
+
if (a === b)
|
|
32569
|
+
return "duplicate";
|
|
32570
|
+
const tokensA = distinguishingTokens(a).join(" ");
|
|
32571
|
+
const tokensB = distinguishingTokens(b).join(" ");
|
|
32572
|
+
if (tokensA !== tokensB)
|
|
32573
|
+
return null;
|
|
32574
|
+
return import_fast_levenshtein.default.get(a, b) <= 2 ? "near-duplicate" : null;
|
|
32575
|
+
}
|
|
32576
|
+
function findSiteNameClash(candidate, existingNames) {
|
|
32577
|
+
for (const name of existingNames) {
|
|
32578
|
+
const match = matchSiteName(candidate, name);
|
|
32579
|
+
if (match)
|
|
32580
|
+
return { name, match };
|
|
32581
|
+
}
|
|
32582
|
+
return null;
|
|
32583
|
+
}
|
|
32584
|
+
function siteNameClashMessage(existingName, match) {
|
|
32585
|
+
if (match === "duplicate") {
|
|
32586
|
+
return `A site called "${existingName}" already exists in this organisation. Give the new site a different name, or edit "${existingName}" instead.`;
|
|
32587
|
+
}
|
|
32588
|
+
return `This name is within a character or two of "${existingName}", which already exists in this organisation. If it is the same property, edit "${existingName}" instead. If it is a different one, include what tells them apart \u2014 the block, tower, phase, or number \u2014 and save again.`;
|
|
32589
|
+
}
|
|
32590
|
+
|
|
32591
|
+
// src/services/customer-site.service.ts
|
|
32541
32592
|
function useCustomerSiteService() {
|
|
32542
32593
|
const { add: _add, updateCustomerSiteById, getById: _getById } = useCustomerSiteRepo();
|
|
32543
32594
|
const {
|
|
@@ -32559,20 +32610,18 @@ function useCustomerSiteService() {
|
|
|
32559
32610
|
const exactMatches = await getSiteByExactName(value.name, value.siteOrg);
|
|
32560
32611
|
if (exactMatches && exactMatches.length > 0) {
|
|
32561
32612
|
throw new import_node_server_utils96.BadRequestError(
|
|
32562
|
-
|
|
32613
|
+
siteNameClashMessage(exactMatches[0].name, "duplicate")
|
|
32563
32614
|
);
|
|
32564
32615
|
}
|
|
32565
|
-
const threshold = 2;
|
|
32566
32616
|
const sites = await getSiteByName(value.name);
|
|
32567
|
-
|
|
32568
|
-
|
|
32569
|
-
|
|
32570
|
-
|
|
32571
|
-
|
|
32572
|
-
|
|
32573
|
-
|
|
32574
|
-
|
|
32575
|
-
}
|
|
32617
|
+
const candidates = (sites ?? []).filter(
|
|
32618
|
+
(doc) => doc.orgId && doc.name && doc.orgId.toString() === value.siteOrg.toString()
|
|
32619
|
+
).map((doc) => doc.name);
|
|
32620
|
+
const clash = findSiteNameClash(value.name, candidates);
|
|
32621
|
+
if (clash) {
|
|
32622
|
+
throw new import_node_server_utils96.BadRequestError(
|
|
32623
|
+
siteNameClashMessage(clash.name, clash.match)
|
|
32624
|
+
);
|
|
32576
32625
|
}
|
|
32577
32626
|
const siteId = await createSite(
|
|
32578
32627
|
{
|
|
@@ -64835,9 +64884,19 @@ var import_mongodb135 = require("mongodb");
|
|
|
64835
64884
|
var import_node_server_utils223 = require("@7365admin1/node-server-utils");
|
|
64836
64885
|
var import_mongodb134 = require("mongodb");
|
|
64837
64886
|
var import_joi126 = __toESM(require("joi"));
|
|
64887
|
+
var BillingPaymentStatus = /* @__PURE__ */ ((BillingPaymentStatus2) => {
|
|
64888
|
+
BillingPaymentStatus2["PENDING"] = "pending";
|
|
64889
|
+
BillingPaymentStatus2["SUCCESS"] = "success";
|
|
64890
|
+
BillingPaymentStatus2["FAILED"] = "failed";
|
|
64891
|
+
BillingPaymentStatus2["PARTIAL"] = "partial";
|
|
64892
|
+
BillingPaymentStatus2["CANCELLED"] = "cancelled";
|
|
64893
|
+
return BillingPaymentStatus2;
|
|
64894
|
+
})(BillingPaymentStatus || {});
|
|
64838
64895
|
var schemaBillingItems = import_joi126.default.object({
|
|
64839
64896
|
_id: import_joi126.default.string().hex().optional(),
|
|
64840
|
-
name: import_joi126.default.string().optional().allow(null, "")
|
|
64897
|
+
name: import_joi126.default.string().optional().allow(null, ""),
|
|
64898
|
+
amount: import_joi126.default.number().optional().allow(null, ""),
|
|
64899
|
+
referenceNumber: import_joi126.default.string().optional().allow(null, "")
|
|
64841
64900
|
});
|
|
64842
64901
|
var schemaBillingPayments = import_joi126.default.object({
|
|
64843
64902
|
_id: import_joi126.default.string().hex().optional(),
|
|
@@ -64944,12 +65003,60 @@ var useRedDotPaymentRepo = () => {
|
|
|
64944
65003
|
const redDotMerchantCollection = () => {
|
|
64945
65004
|
return getDB2().collection("payment-gateways");
|
|
64946
65005
|
};
|
|
65006
|
+
async function createIndexes() {
|
|
65007
|
+
try {
|
|
65008
|
+
await paymentCollection().createIndexes([
|
|
65009
|
+
// A referenceNumber-prefixed compound index serves both the lookup by
|
|
65010
|
+
// reference alone and the { referenceNumber, status } lookups used by
|
|
65011
|
+
// the pay single / pay multiple flows.
|
|
65012
|
+
{
|
|
65013
|
+
key: { referenceNumber: 1, status: 1 },
|
|
65014
|
+
name: "referenceNumber_1_status_1"
|
|
65015
|
+
}
|
|
65016
|
+
]);
|
|
65017
|
+
} catch (error) {
|
|
65018
|
+
import_node_server_utils224.logger.log({
|
|
65019
|
+
level: "error",
|
|
65020
|
+
message: error.message
|
|
65021
|
+
});
|
|
65022
|
+
throw new import_node_server_utils224.InternalServerError(
|
|
65023
|
+
"Failed to create index on billing payments."
|
|
65024
|
+
);
|
|
65025
|
+
}
|
|
65026
|
+
}
|
|
65027
|
+
async function getPaymentByReference(referenceNumber, session) {
|
|
65028
|
+
const reference = typeof referenceNumber === "string" ? referenceNumber.trim() : "";
|
|
65029
|
+
if (!reference) {
|
|
65030
|
+
throw new import_node_server_utils224.BadRequestError("Reference number is required.");
|
|
65031
|
+
}
|
|
65032
|
+
try {
|
|
65033
|
+
const payment = await paymentCollection().findOne(
|
|
65034
|
+
{
|
|
65035
|
+
referenceNumber: reference
|
|
65036
|
+
},
|
|
65037
|
+
{ session }
|
|
65038
|
+
);
|
|
65039
|
+
if (!payment) {
|
|
65040
|
+
throw new import_node_server_utils224.NotFoundError("Payment not found.");
|
|
65041
|
+
}
|
|
65042
|
+
return payment;
|
|
65043
|
+
} catch (error) {
|
|
65044
|
+
if (error instanceof import_node_server_utils224.AppError) {
|
|
65045
|
+
throw error;
|
|
65046
|
+
}
|
|
65047
|
+
import_node_server_utils224.logger.log({
|
|
65048
|
+
level: "error",
|
|
65049
|
+
message: error.message
|
|
65050
|
+
});
|
|
65051
|
+
throw new import_node_server_utils224.InternalServerError("Failed to get payment.");
|
|
65052
|
+
}
|
|
65053
|
+
}
|
|
64947
65054
|
async function createPayment(type, payload, session) {
|
|
64948
65055
|
if (type == "single") {
|
|
64949
65056
|
try {
|
|
64950
65057
|
payload = MBillingItems(payload);
|
|
64951
|
-
|
|
64952
|
-
return
|
|
65058
|
+
await paymentCollection().insertOne(payload, { session });
|
|
65059
|
+
return payload.referenceNumber;
|
|
64953
65060
|
} catch (error) {
|
|
64954
65061
|
import_node_server_utils224.logger.log({
|
|
64955
65062
|
level: "error",
|
|
@@ -64978,7 +65085,7 @@ var useRedDotPaymentRepo = () => {
|
|
|
64978
65085
|
{ $set: { referenceNumber } },
|
|
64979
65086
|
{ session }
|
|
64980
65087
|
);
|
|
64981
|
-
return
|
|
65088
|
+
return referenceNumber;
|
|
64982
65089
|
} catch (error) {
|
|
64983
65090
|
import_node_server_utils224.logger.log({
|
|
64984
65091
|
level: "error",
|
|
@@ -64996,6 +65103,76 @@ var useRedDotPaymentRepo = () => {
|
|
|
64996
65103
|
}
|
|
64997
65104
|
}
|
|
64998
65105
|
}
|
|
65106
|
+
async function getPaidBills({
|
|
65107
|
+
search = "",
|
|
65108
|
+
page = 1,
|
|
65109
|
+
limit = 10,
|
|
65110
|
+
sort = {},
|
|
65111
|
+
month,
|
|
65112
|
+
year,
|
|
65113
|
+
unitId
|
|
65114
|
+
}, session) {
|
|
65115
|
+
let _unitId;
|
|
65116
|
+
try {
|
|
65117
|
+
_unitId = new import_mongodb135.ObjectId(unitId);
|
|
65118
|
+
} catch {
|
|
65119
|
+
throw new import_node_server_utils224.BadRequestError("Invalid unit ID format.");
|
|
65120
|
+
}
|
|
65121
|
+
page = page > 0 ? page - 1 : 0;
|
|
65122
|
+
let dateExpr = {};
|
|
65123
|
+
const datePaidRange = buildDatePaidRange(month, year);
|
|
65124
|
+
if (datePaidRange) {
|
|
65125
|
+
dateExpr.datePaid = datePaidRange;
|
|
65126
|
+
}
|
|
65127
|
+
const query = {
|
|
65128
|
+
unitId: _unitId,
|
|
65129
|
+
status: "success",
|
|
65130
|
+
...search && {
|
|
65131
|
+
$or: [
|
|
65132
|
+
{ referenceNumber: { $regex: search, $options: "i" } },
|
|
65133
|
+
{ unitOwner: { $regex: search, $options: "i" } },
|
|
65134
|
+
{ method: { $regex: search, $options: "i" } },
|
|
65135
|
+
{ transaction_id: { $regex: search, $options: "i" } },
|
|
65136
|
+
{ "unitBillingItems.name": { $regex: search, $options: "i" } },
|
|
65137
|
+
{
|
|
65138
|
+
$expr: {
|
|
65139
|
+
$regexMatch: {
|
|
65140
|
+
input: { $toString: "$totalAmount" },
|
|
65141
|
+
regex: search,
|
|
65142
|
+
options: "i"
|
|
65143
|
+
}
|
|
65144
|
+
}
|
|
65145
|
+
}
|
|
65146
|
+
]
|
|
65147
|
+
},
|
|
65148
|
+
...dateExpr
|
|
65149
|
+
};
|
|
65150
|
+
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
65151
|
+
try {
|
|
65152
|
+
const basePipeline = [
|
|
65153
|
+
{ $match: query },
|
|
65154
|
+
{ $sort: sort },
|
|
65155
|
+
{ $skip: page * limit },
|
|
65156
|
+
{ $limit: limit }
|
|
65157
|
+
];
|
|
65158
|
+
const [items, countResult] = await Promise.all([
|
|
65159
|
+
paymentCollection().aggregate(basePipeline, { session }).toArray(),
|
|
65160
|
+
paymentCollection().aggregate([{ $match: query }, { $count: "total" }], { session }).toArray()
|
|
65161
|
+
]);
|
|
65162
|
+
const totalCount = countResult[0]?.total || 0;
|
|
65163
|
+
const data = (0, import_node_server_utils224.paginate)(items, page, limit, totalCount);
|
|
65164
|
+
return data;
|
|
65165
|
+
} catch (error) {
|
|
65166
|
+
if (error instanceof import_node_server_utils224.AppError) {
|
|
65167
|
+
throw error;
|
|
65168
|
+
}
|
|
65169
|
+
import_node_server_utils224.logger.log({
|
|
65170
|
+
level: "error",
|
|
65171
|
+
message: error.message
|
|
65172
|
+
});
|
|
65173
|
+
throw new import_node_server_utils224.InternalServerError("Failed to retrieve paid bills.");
|
|
65174
|
+
}
|
|
65175
|
+
}
|
|
64999
65176
|
async function paySingleUnitBill(refId, payload) {
|
|
65000
65177
|
const session = import_node_server_utils224.useAtlas.getClient()?.startSession();
|
|
65001
65178
|
try {
|
|
@@ -65158,6 +65335,52 @@ var useRedDotPaymentRepo = () => {
|
|
|
65158
65335
|
session?.endSession();
|
|
65159
65336
|
}
|
|
65160
65337
|
}
|
|
65338
|
+
function buildDatePaidRange(month, year) {
|
|
65339
|
+
if (!year)
|
|
65340
|
+
return null;
|
|
65341
|
+
const yearNum = Number(year);
|
|
65342
|
+
if (!Number.isInteger(yearNum) || yearNum < 1970 || yearNum > 9999) {
|
|
65343
|
+
throw new import_node_server_utils224.BadRequestError("Invalid year filter.");
|
|
65344
|
+
}
|
|
65345
|
+
if (month) {
|
|
65346
|
+
const monthNum = Number(month);
|
|
65347
|
+
if (!Number.isInteger(monthNum) || monthNum < 1 || monthNum > 12) {
|
|
65348
|
+
throw new import_node_server_utils224.BadRequestError("Invalid month filter.");
|
|
65349
|
+
}
|
|
65350
|
+
return {
|
|
65351
|
+
$gte: new Date(yearNum, monthNum - 1, 1),
|
|
65352
|
+
$lt: new Date(yearNum, monthNum, 1)
|
|
65353
|
+
};
|
|
65354
|
+
}
|
|
65355
|
+
return {
|
|
65356
|
+
$gte: new Date(yearNum, 0, 1),
|
|
65357
|
+
$lt: new Date(yearNum + 1, 0, 1)
|
|
65358
|
+
};
|
|
65359
|
+
}
|
|
65360
|
+
async function updateStatus(referenceNumber, status, session) {
|
|
65361
|
+
const reference = typeof referenceNumber === "string" ? referenceNumber.trim() : "";
|
|
65362
|
+
if (!reference) {
|
|
65363
|
+
throw new import_node_server_utils224.BadRequestError("Reference number is required.");
|
|
65364
|
+
}
|
|
65365
|
+
try {
|
|
65366
|
+
const res = await paymentCollection().updateOne(
|
|
65367
|
+
{ referenceNumber: reference, status: "pending" },
|
|
65368
|
+
{ $set: { status, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } },
|
|
65369
|
+
{ session }
|
|
65370
|
+
);
|
|
65371
|
+
if (res.matchedCount === 0) {
|
|
65372
|
+
throw new import_node_server_utils224.NotFoundError(
|
|
65373
|
+
"Payment not found or is no longer pending."
|
|
65374
|
+
);
|
|
65375
|
+
}
|
|
65376
|
+
return res;
|
|
65377
|
+
} catch (error) {
|
|
65378
|
+
if (error instanceof import_node_server_utils224.AppError) {
|
|
65379
|
+
throw error;
|
|
65380
|
+
}
|
|
65381
|
+
throw new import_node_server_utils224.InternalServerError("Failed to update payment status.");
|
|
65382
|
+
}
|
|
65383
|
+
}
|
|
65161
65384
|
function formatDateString(today) {
|
|
65162
65385
|
today = typeof today === "string" ? new Date(today) : today;
|
|
65163
65386
|
let month = today.getMonth() + 1;
|
|
@@ -65166,7 +65389,15 @@ var useRedDotPaymentRepo = () => {
|
|
|
65166
65389
|
const formattedDate = `${month.toString().padStart(2, "0")}${day.toString().padStart(2, "0")}${year}`;
|
|
65167
65390
|
return formattedDate;
|
|
65168
65391
|
}
|
|
65169
|
-
return {
|
|
65392
|
+
return {
|
|
65393
|
+
paySingleUnitBill,
|
|
65394
|
+
createPayment,
|
|
65395
|
+
payMultipleUnitBill,
|
|
65396
|
+
getPaidBills,
|
|
65397
|
+
createIndexes,
|
|
65398
|
+
getPaymentByReference,
|
|
65399
|
+
updateStatus
|
|
65400
|
+
};
|
|
65170
65401
|
};
|
|
65171
65402
|
|
|
65172
65403
|
// src/services/reddot-payment.service.ts
|
|
@@ -65276,7 +65507,7 @@ var useRedDotPaymentSvc = () => {
|
|
|
65276
65507
|
const success = {
|
|
65277
65508
|
...basePayload,
|
|
65278
65509
|
paymentStatus: "paid" /* PAID */,
|
|
65279
|
-
amountPaid:
|
|
65510
|
+
amountPaid: Number(amount)
|
|
65280
65511
|
};
|
|
65281
65512
|
if (isBulk) {
|
|
65282
65513
|
await payMultipleUnitBill(refId, success);
|
|
@@ -65310,7 +65541,12 @@ var useRedDotPaymentSvc = () => {
|
|
|
65310
65541
|
var import_joi127 = __toESM(require("joi"));
|
|
65311
65542
|
var import_node_server_utils226 = require("@7365admin1/node-server-utils");
|
|
65312
65543
|
function useRedDotPaymentController() {
|
|
65313
|
-
const {
|
|
65544
|
+
const {
|
|
65545
|
+
createPayment: _createPayment,
|
|
65546
|
+
updateStatus: _updateStatus,
|
|
65547
|
+
getPaymentByReference: _getPaymentByReference,
|
|
65548
|
+
getPaidBills: _getPaidBills
|
|
65549
|
+
} = useRedDotPaymentRepo();
|
|
65314
65550
|
const redirectPaymentTransaction = async (req, res) => {
|
|
65315
65551
|
try {
|
|
65316
65552
|
const data = req.body;
|
|
@@ -65374,17 +65610,86 @@ function useRedDotPaymentController() {
|
|
|
65374
65610
|
try {
|
|
65375
65611
|
const payment = await _createPayment(type, value);
|
|
65376
65612
|
res.status(201).json({
|
|
65377
|
-
message: "
|
|
65613
|
+
message: "success",
|
|
65378
65614
|
data: payment
|
|
65379
65615
|
});
|
|
65380
65616
|
} catch (error) {
|
|
65381
|
-
|
|
65617
|
+
const status = error?.statusCode ?? 500;
|
|
65618
|
+
res.status(status).json({ message: "failed" });
|
|
65619
|
+
}
|
|
65620
|
+
}
|
|
65621
|
+
async function updateStatus(req, res, next) {
|
|
65622
|
+
const schema2 = import_joi127.default.object({
|
|
65623
|
+
referenceNumber: import_joi127.default.string().required(),
|
|
65624
|
+
status: import_joi127.default.string().valid(...Object.values(BillingPaymentStatus)).required()
|
|
65625
|
+
});
|
|
65626
|
+
const { error, value } = schema2.validate({
|
|
65627
|
+
referenceNumber: req.params.id,
|
|
65628
|
+
status: req.body.status
|
|
65629
|
+
});
|
|
65630
|
+
if (error) {
|
|
65631
|
+
import_node_server_utils226.logger.log({ level: "error", message: error.message });
|
|
65632
|
+
next(new import_node_server_utils226.BadRequestError(error.message));
|
|
65633
|
+
return;
|
|
65634
|
+
}
|
|
65635
|
+
try {
|
|
65636
|
+
const data = await _updateStatus(value.referenceNumber, value.status);
|
|
65637
|
+
res.status(200).json({ message: "success", data });
|
|
65638
|
+
return;
|
|
65639
|
+
} catch (error2) {
|
|
65640
|
+
import_node_server_utils226.logger.log({ level: "error", message: error2.message });
|
|
65641
|
+
next(error2);
|
|
65642
|
+
return;
|
|
65643
|
+
}
|
|
65644
|
+
}
|
|
65645
|
+
async function getPaymentByReference(req, res, next) {
|
|
65646
|
+
const schema2 = import_joi127.default.object({
|
|
65647
|
+
referenceNumber: import_joi127.default.string().required()
|
|
65648
|
+
});
|
|
65649
|
+
const { error, value } = schema2.validate(req.params);
|
|
65650
|
+
if (error) {
|
|
65651
|
+
next(new import_node_server_utils226.BadRequestError(error.message));
|
|
65652
|
+
return;
|
|
65653
|
+
}
|
|
65654
|
+
try {
|
|
65655
|
+
const payment = await _getPaymentByReference(value.referenceNumber);
|
|
65656
|
+
res.json({ data: payment });
|
|
65657
|
+
return;
|
|
65658
|
+
} catch (error2) {
|
|
65659
|
+
next(error2);
|
|
65660
|
+
return;
|
|
65661
|
+
}
|
|
65662
|
+
}
|
|
65663
|
+
async function getPaidBills(req, res, next) {
|
|
65664
|
+
const schema2 = import_joi127.default.object({
|
|
65665
|
+
unitId: import_joi127.default.string().hex().length(24).required(),
|
|
65666
|
+
page: import_joi127.default.number().integer().min(1).default(1),
|
|
65667
|
+
limit: import_joi127.default.number().integer().min(1).max(100).default(10),
|
|
65668
|
+
search: import_joi127.default.string().trim().allow("").default(""),
|
|
65669
|
+
month: import_joi127.default.string().pattern(/^(0?[1-9]|1[0-2])$/).allow("").optional(),
|
|
65670
|
+
year: import_joi127.default.string().pattern(/^\d{4}$/).allow("").optional()
|
|
65671
|
+
});
|
|
65672
|
+
const { error, value } = schema2.validate({ ...req.params, ...req.query });
|
|
65673
|
+
if (error) {
|
|
65674
|
+
next(new import_node_server_utils226.BadRequestError(error.message));
|
|
65675
|
+
return;
|
|
65676
|
+
}
|
|
65677
|
+
try {
|
|
65678
|
+
const bills = await _getPaidBills(value);
|
|
65679
|
+
res.json({ data: bills });
|
|
65680
|
+
return;
|
|
65681
|
+
} catch (error2) {
|
|
65682
|
+
next(error2);
|
|
65683
|
+
return;
|
|
65382
65684
|
}
|
|
65383
65685
|
}
|
|
65384
65686
|
return {
|
|
65385
65687
|
redirectPaymentTransaction,
|
|
65386
65688
|
enquirePaymentTransaction,
|
|
65387
|
-
createPayment
|
|
65689
|
+
createPayment,
|
|
65690
|
+
getPaymentByReference,
|
|
65691
|
+
updateStatus,
|
|
65692
|
+
getPaidBills
|
|
65388
65693
|
};
|
|
65389
65694
|
}
|
|
65390
65695
|
|