Skip to main content

getPendingByUnit (stepApproval)

getPendingByUnit is the inbox โ€” the list of approval tasks waiting on a specific unit. It returns every stepApproval whose unit._id matches and whose status is still pending, newest first. This is what a UnitHead sees when they open "my pending approvals": each row links out to the PO, so they can review and then submitDecision. Manager/Admin/UnitHead/Employee roles can call it.

note

Package import The tutorial source imports the framework as "lesan" โ€” in this repo that alias maps to the local framework source (deno.json โ†’ ../../src/mod.ts). In your own app import from @hemedani/lesan (npm/Bun) or jsr:@hemedani/lesan (Deno). @lib and @model are the tutorial's aliases for utils/ and models/.

The validator (getPendingByUnit.val.ts)โ€‹

Just unitId plus a depth-2 projection of the stepApproval schema (so the embedded purchaseOrder and processStep snapshots come along).

import { object, objectIdValidation } from "lesan";
import { selectStruct } from "../../../mod.ts";
import { activeRoleMixin } from "@lib";

export const getPendingByUnitValidator = () => {
return object({
set: object({
...activeRoleMixin,
unitId: objectIdValidation,
}),
get: selectStruct("stepApproval", 2),
});
};

The implementation (getPendingByUnit.fn.ts)โ€‹

Two fixed filters: the embedded "unit._id" and status: "pending". Sorted by createdAt descending so the newest requests surface first.

import { type ActFn, type Document, ObjectId } from "lesan";
import { stepApproval } from "../../../mod.ts";

export const getPendingByUnitFn: ActFn = async (body) => {
const {
set: { unitId },
get,
} = body.details;

const filters: Document = {
"unit._id": new ObjectId(unitId as string),
status: "pending",
};

return await stepApproval
.aggregation({
pipeline: [
{ $match: filters },
{ $sort: { createdAt: -1 } },
] as Document[],
projection: get,
})
.toArray();
};

In the workflowโ€‹

getPendingByUnit drives the approval UI. A UnitHead polls it for their unit._id; each result carries the embedded purchaseOrder (title, amount, currentStep) and processStep (order, name) so the review screen can render without a second round trip. Once they call submitDecision the approval leaves this list (its status is no longer pending).

getPendingByUnit(unit) โ”€โ–ถ [approval A (pending), approval B (pending), ...]
โ”‚
submitDecision(A)
โ”‚
status: approved/rejected โ”€โ–ถ leaves the inbox

Links: overview, stepApproval model, submit-decision, get-step-approvals, po-submit.

Run itโ€‹

curl -X POST http://localhost:1380/lesan \
-H "Content-Type: application/json" \
-H "token: $TOKEN" \
-d '{
"model": "stepApproval",
"act": "getPendingByUnit",
"details": {
"set": {
"activeRoleId": "ghost-role",
"unitId": "<unitId>"
},
"get": {
"_id": 1,
"status": 1,
"purchaseOrder": { "_id": 1, "title": 1, "estimatedAmount": 1 },
"processStep": { "_id": 1, "name": 1, "order": 1 }
}
}
}'

Errors & fixesโ€‹

The fn throws nothing of its own. An invalid unitId fails objectIdValidation; a valid id with nothing waiting returns [] (success). Note the fixed status: "pending" filter โ€” a unit with only decided approvals gets an empty inbox by design. Shared auth errors apply; Ordinary users are not in the allowed roles.