@7365admin1/core 3.48.1-staging.168 → 3.48.1-staging.170
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/.changeset/provider-billing-org-scope.md +21 -0
- package/.changeset/site-unit-billing-scope.md +25 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +2032 -1963
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +630 -561
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/test/e2e/harness.mjs +42 -0
- package/test/e2e/provider-billing-scope.e2e.test.mjs +360 -0
- package/test/e2e/site-unit-billing-scope.e2e.test.mjs +442 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": patch
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Scope the service provider billing routes to the organisation
|
|
6
|
+
|
|
7
|
+
`/api/provider-billings` carried `requireAuth` and nothing more on all five
|
|
8
|
+
handlers, and `service-provider-billing.controller.ts` held no authorization
|
|
9
|
+
identifier of any kind. A service provider billing record is what a contractor
|
|
10
|
+
invoices an organisation for, so any signed-in account on the platform could
|
|
11
|
+
read another organisation's invoices, swap the attachments that evidence one,
|
|
12
|
+
flip its status, add an acknowledgement that was never given, or delete it.
|
|
13
|
+
|
|
14
|
+
The scope here is the ORGANISATION, not the estate: every row carries `orgId`
|
|
15
|
+
and no `site`, so the rule is `requireOrgAccess` — Seven365 staff, or a live
|
|
16
|
+
member of that organisation — not `requireSiteReach`.
|
|
17
|
+
|
|
18
|
+
Adds `requireServiceProviderBillingReach`, plus an uncached
|
|
19
|
+
`getRawServiceProviderBillingById` so the check is not decided from a 15-minute
|
|
20
|
+
cache entry (the existing reader is cached and throws `NotFoundError`, which
|
|
21
|
+
would also answer before the guard ran).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": patch
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Scope the unit billing routes to the estate, and the resident routes to the caller's own unit
|
|
6
|
+
|
|
7
|
+
`/api/site-unit-billing` carried `requireAuth` and nothing more on all seven
|
|
8
|
+
handlers, and `site-unit-billing.controller.ts` held no authorization identifier
|
|
9
|
+
of any kind. Two separate problems followed.
|
|
10
|
+
|
|
11
|
+
On the estate side, any signed-in account could add a bill to any estate, read
|
|
12
|
+
another estate's whole billing run, restate what a resident owes, move a bill to
|
|
13
|
+
another estate, or delete it. Those five routes now use `requireSiteReach`, plus
|
|
14
|
+
a new `requireUnitBillingReach` on the stored row and an uncached
|
|
15
|
+
`getRawUnitBillingById`. `PUT /id/:id` checks both ends, because
|
|
16
|
+
`schemaUpdateSiteUnitBilling` accepts `site` and `updateById` writes it.
|
|
17
|
+
|
|
18
|
+
On the resident side, `GET /user` and `GET /user/unsettled` are named for the
|
|
19
|
+
signed-in resident but took `site` and `unitId` from the QUERY and identified the
|
|
20
|
+
caller not at all, so they were never "my bills" — they were any unit's bills at
|
|
21
|
+
any estate. A resident passing a neighbour's unit id got the neighbour's
|
|
22
|
+
maintenance bills, and omitting `unitId` dropped that filter too and returned the
|
|
23
|
+
whole estate's. Both routes now require `unitId` and check it against a live
|
|
24
|
+
`people` row joining the caller to that unit at that site, via a new
|
|
25
|
+
`requireOwnUnit`. Seven365 platform staff are exempt.
|
package/dist/index.d.ts
CHANGED
|
@@ -5438,6 +5438,7 @@ declare function useServiceProviderBillingRepo(): {
|
|
|
5438
5438
|
pageRange: string;
|
|
5439
5439
|
} | TServiceProviderBilling>;
|
|
5440
5440
|
getSiteServiceProviderBillingById: (_id: string | ObjectId, session?: ClientSession) => Promise<mongodb.WithId<bson.Document> | TServiceProviderBilling>;
|
|
5441
|
+
getRawServiceProviderBillingById: (_id: string | ObjectId) => Promise<mongodb.WithId<bson.Document> | null>;
|
|
5441
5442
|
updateServiceProviderBillingById: (_id: ObjectId | string, value: Partial<TServiceProviderBilling>, session?: ClientSession) => Promise<mongodb.UpdateResult<bson.Document>>;
|
|
5442
5443
|
deleteServiceProviderBillingById: (_id: string | ObjectId) => Promise<number>;
|
|
5443
5444
|
createIndexes: () => Promise<void>;
|
|
@@ -5448,6 +5449,36 @@ declare function useServiceProviderBillingService(): {
|
|
|
5448
5449
|
updateServiceProviderBillingById: (id: string | ObjectId, value: Partial<TServiceProviderBilling>) => Promise<string>;
|
|
5449
5450
|
};
|
|
5450
5451
|
|
|
5452
|
+
/**
|
|
5453
|
+
* Who may see, or change, a service provider's billing records.
|
|
5454
|
+
*
|
|
5455
|
+
* A service provider billing record is what a contractor invoices an
|
|
5456
|
+
* organisation for - the amount, the period, the attachments that evidence it,
|
|
5457
|
+
* and the people who acknowledged it. It is money.
|
|
5458
|
+
*
|
|
5459
|
+
* All five routes on `/api/provider-billings` carried `requireAuth` and nothing
|
|
5460
|
+
* more, and this controller held no authorization identifier of any kind, so
|
|
5461
|
+
* being signed in ANYWHERE on the platform was enough to read another
|
|
5462
|
+
* organisation's invoices, restate one, replace its evidence, or delete it.
|
|
5463
|
+
*
|
|
5464
|
+
* The scope here is the ORGANISATION, not the estate. Every row carries
|
|
5465
|
+
* `orgId` and no `site` - `getAll` is asked for an `org`, and the repository
|
|
5466
|
+
* builds `{ orgId: org, status }`. So the rule is `requireOrgAccess`, the same
|
|
5467
|
+
* one `/api/customer-sites` and `/api/members` already use: Seven365 staff, or
|
|
5468
|
+
* a live member of that organisation. Using `requireSiteReach` here would be
|
|
5469
|
+
* asking the wrong question of the wrong field.
|
|
5470
|
+
*
|
|
5471
|
+
* - `POST /` already REQUIRES `orgId` in the body and `GET /` already REQUIRES
|
|
5472
|
+
* `org` in the query, so the guard checks the organisation the request
|
|
5473
|
+
* already names and no caller's contract changes.
|
|
5474
|
+
* - The three record routes name only a record, so the organisation is read
|
|
5475
|
+
* off the STORED row via `requireServiceProviderBillingReach`, and an
|
|
5476
|
+
* `orgId` in the caller's own body cannot stand in for it.
|
|
5477
|
+
*
|
|
5478
|
+
* `PUT /id/:id` cannot MOVE a record between organisations:
|
|
5479
|
+
* `schemaUpdateServiceProviderBilling` does not accept `orgId` at all, so there
|
|
5480
|
+
* is no destination to check.
|
|
5481
|
+
*/
|
|
5451
5482
|
declare function useServiceProviderBillingController(): {
|
|
5452
5483
|
add: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
5453
5484
|
getAll: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
@@ -6227,6 +6258,7 @@ declare function useSiteUnitBillingRepo(): {
|
|
|
6227
6258
|
dateTo?: string | undefined;
|
|
6228
6259
|
}, session?: ClientSession) => Promise<{}>;
|
|
6229
6260
|
getById: (_id: string | ObjectId) => Promise<TUnitBilling | null>;
|
|
6261
|
+
getRawUnitBillingById: (_id: string | ObjectId) => Promise<mongodb.WithId<bson.Document> | null>;
|
|
6230
6262
|
updateById: (_id: string | ObjectId, value: Partial<TUnitBilling>, session?: ClientSession) => Promise<mongodb.UpdateResult<bson.Document>>;
|
|
6231
6263
|
deleteById: (_id: string | ObjectId) => Promise<number>;
|
|
6232
6264
|
getUnitBillingBySite: ({ status, site, dateFrom, dateTo, unitId, }: {
|
|
@@ -6276,6 +6308,39 @@ declare function useSiteUnitBillingService(): {
|
|
|
6276
6308
|
checkOverDueBills: () => Promise<"No overdue bills found." | "Successfully checked and updated overdue bills.">;
|
|
6277
6309
|
};
|
|
6278
6310
|
|
|
6311
|
+
/**
|
|
6312
|
+
* Who may see, or change, a unit's maintenance bills.
|
|
6313
|
+
*
|
|
6314
|
+
* A site unit billing row is one unit's bill for a period - the amount, what is
|
|
6315
|
+
* outstanding, what has been paid, and against which billing item. It is money,
|
|
6316
|
+
* and on the resident side it is somebody's household finances.
|
|
6317
|
+
*
|
|
6318
|
+
* All seven routes on `/api/site-unit-billing` carried `requireAuth` and
|
|
6319
|
+
* nothing more, and this controller held no authorization identifier of any
|
|
6320
|
+
* kind, so being signed in ANYWHERE on the platform was enough to read another
|
|
6321
|
+
* estate's billing run, restate a bill, move it to another estate, or delete
|
|
6322
|
+
* it.
|
|
6323
|
+
*
|
|
6324
|
+
* The two resident routes are the sharp ones and are guarded differently from
|
|
6325
|
+
* the rest - see `requireOwnUnit`. `GET /user` and `GET /user/unsettled` are
|
|
6326
|
+
* named for the signed-in resident but took `site` and `unitId` from the QUERY
|
|
6327
|
+
* and identified the caller not at all, so they were never "my bills": they
|
|
6328
|
+
* were "any unit's bills, at any estate". A resident passing a NEIGHBOUR's unit
|
|
6329
|
+
* id got the neighbour's maintenance bills, and omitting `unitId` dropped that
|
|
6330
|
+
* filter as well.
|
|
6331
|
+
*
|
|
6332
|
+
* For the five estate-side routes the scope is the estate, and `requireSiteReach`
|
|
6333
|
+
* is the rule `/api/patrol-logs`, `/api/feedbacks`, `/api/work-orders` and
|
|
6334
|
+
* `/api/occurrence-books` already use.
|
|
6335
|
+
*
|
|
6336
|
+
* - `POST /` and `GET /` name a site, so the guard checks the site the request
|
|
6337
|
+
* already names.
|
|
6338
|
+
* - The three record routes name only a record, so the site is read off the
|
|
6339
|
+
* STORED row via `requireUnitBillingReach`.
|
|
6340
|
+
*
|
|
6341
|
+
* `PUT /id/:id` CAN move a bill: `schemaUpdateSiteUnitBilling` accepts `site`
|
|
6342
|
+
* and `updateById` writes it, so both ends are checked.
|
|
6343
|
+
*/
|
|
6279
6344
|
declare function useSiteUnitBillingController(): {
|
|
6280
6345
|
add: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
6281
6346
|
getAll: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|