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.
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.