Purchase Requisition Approvals — Functional Design
Status: Draft for review · Audience: Theta consultants and developers · Applies to: Purchase Requisition Portal, BC 27.5
This document is internal. It is not part of the AppSource-published documentation set (
DOCS/config.json), and it is deliberately not numbered0NN-*.It supersedes
DOCS/TechnicalArchitecture.md§8 and §26 for everything to do with approvals. That document predates the approvals module entirely and its object IDs are stale; do not use it as an approvals reference.
1. Purpose and scope
A purchase requisition is a header with many lines. Lines carry catalogue items, vendors, projects and project tasks, dimensions and an estimated amount — and any of those can determine who must approve.
This document describes the approval system: how a line is matched to an approval path, how lines are grouped so that financial authority is tested against the right total, how roles resolve to actual people, the rules that govern who may approve, and the operational concerns around it — audit, notifications, security, governance and reporting.
What is in scope: approval routing, role resolution, delegated financial authority, quorum, requester exclusion, delegation and absence cover, and the approval lifecycle.
What is not: requisition creation, the vendor catalogue, SharePoint document handling, and purchase order conversion beyond where approval status gates it.
1.1 Design principles
Five principles explain most of the decisions that follow.
- Matching rules stay generic; specificity lives in role resolution. Adding a branch adds one row of data, not a new rule. This is what keeps configuration maintainable as an organisation grows.
- Nothing is approved by accident. Where the old engine silently released a requisition it could not route, this design blocks and raises it to an administrator. A control that fails open is not a control.
- Decisions are reconstructable. Every approval records the amount, the authority limit and the exchange rate date as they were at the time, so a decision can still be explained a year later.
- Configuration is previewable. An administrator can simulate a requisition and see exactly who would be asked, before anything goes live.
- One rule, applied uniformly. Completion is governed by a single predicate (§7.3) rather than a collection of special cases.
2. Overview
LINE (catalogue item, vendor, project/task, dimensions, amount)
│
│ 1. MATCHING Approval Groups evaluated by Priority.
│ First match wins → binds the line to a PATH.
▼
PATH
│
│ 2. GROUPING Lines sharing (Path + Group Key) form an
│ APPROVAL UNIT with one aggregated LCY amount.
▼
UNIT
│
│ 3. LEVELS Ordered. Review (no authority) or Approval (DFA).
│
│ 4. RESOLUTION Role + this line's context → a specific BC user.
│ (branch dimension → branch manager;
│ project → the project's manager)
│
│ 5. COMPLETION All mandatory reviews done
│ AND one approver's DFA covers the amount
│ AND distinct approvers ≥ path minimum
│ AND no barred person among them
│ ─ else open the next level
▼ ─ levels exhausted → BLOCKED
APPROVED
Approval runs on Business Central's standard Approval Entry (table 454) and Workflow engine. What is custom is everything above: which path applies, how lines group, who fills a role, and when a unit is finished.
3. Concepts
| Term | Meaning |
|---|---|
| Condition Set | A reusable, named set of criteria (dimension value, vendor, project, item category, amount range, or a Business Central filter). Criteria within a set are ANDed. |
| Approval Group | A prioritised matcher. Pairs a Condition Set with an Approval Path. The first group whose conditions match a line determines that line's path. |
| Approval Path | The approval chain: an ordered set of Levels plus the path-wide rules (aggregation basis, quorum, requester exclusion). |
| Approval Unit | The thing actually approved. One or more lines that share a path and a group key, carrying one aggregated amount. |
| Level | One ordered step in a path. Either a Review (mandatory sign-off, no financial authority) or an Approval (carries DFA). |
| Approval Role | An abstract role — Branch Manager, Regional Manager, CFO, CapEx Reviewer — that resolves to a specific user using the line's context. |
| DFA | Delegated Financial Authority. The monetary limit up to which a person may approve. Always expressed in local currency (LCY). |
| Quorum | The minimum number of distinct people who must approve a unit, counted across the whole path. |
| Blocked | A unit that cannot proceed — no eligible approver, or a rule that cannot be satisfied. Raised to an administrator; never auto-approved. |
3.1 Why the Approval Unit exists
Routing has to be line-aware, because a requisition's lines can belong to different branches, projects and spend types. But financial authority is rarely meaningful per line: someone splitting a $90,000 purchase across three $30,000 lines should not thereby stay under a $50,000 limit.
The Approval Unit reconciles the two. Lines are matched individually, then grouped by whatever the path says is the meaningful unit of authority — the whole requisition, one branch's lines, one project's lines — and the financial test runs against the group's total.
4. Matching — which path applies to a line
4.1 How it works
Each line is evaluated against enabled Approval Groups in ascending Priority. The first match wins. That group's Approval Path binds to the line, and evaluation stops.
| Field | Purpose |
|---|---|
Code, Description | Identity |
Priority | Evaluation order; lowest first |
Enabled | Disabled groups are skipped |
Condition Set Code | Criteria; blank means "matches everything" |
Approval Path Code | The path this group routes to |
A catch-all group must exist — an enabled group with a blank Condition Set at the highest Priority number. A line that matches nothing is a configuration error, and the engine blocks the unit rather than releasing it.
There is no nested boolean logic. To express "A or B", create two groups pointing at the same path. This keeps evaluation order obvious, which matters more than expressive power when someone is debugging why the wrong person was asked to approve.
4.2 Example configuration
| Priority | Group | Condition Set | Path |
|---|---|---|---|
| 10 | CAPEX | Item Category = CAPEX or Amount > 50,000 | CAPEX |
| 20 | PROJECT | Project No. is not blank | PROJECT |
| 30 | BRANCH | Branch dimension is not blank | STANDARD |
| 99 | DEFAULT | (blank — catch-all) | STANDARD |
4.3 Configuration at scale
This is the part that determines whether the system stays maintainable after two years and forty branches.
| Grows with the business | Stays constant |
|---|---|
| Approval Role Assignment rows — one per (role, branch) or (role, project) | Approval Groups — one per kind of spend |
| Condition Sets | |
| Approval Paths and Levels |
Opening a new branch means one row in Role Assignment — "Branch Manager, dimension BRANCH, value WLG, user JSMITH". No new group, path or level. The matching rule already says "any line with a branch dimension"; it does not enumerate branches.
Three tools keep this safe as the data grows:
- Coverage report (§14.2) — lists dimension values, projects and vendors that no role can resolve an approver for.
- Simulation (§14.3) — shows who would be asked for a given requisition, before go-live.
- Configuration package import — Role Assignment is a flat table suitable for Excel import and bulk maintenance.
5. Roles and contextual resolution
An Approval Role is not a list of people. It is a question — "who is the branch manager for this line?" — answered using the line's own attributes.
5.1 Resolution methods
| Method | How the user is found |
|---|---|
Fixed Users | A static list. Right for CFO and CapEx Reviewer, where the role is not context-dependent. |
By Dimension Value | Role Assignment keyed on (role, dimension code, the line's dimension value). The branch and region case. |
By Vendor | Role Assignment keyed on the line's vendor. |
By Item Category | Role Assignment keyed on the line's item category. |
From Project | Read directly from the project record — see §5.3. |
From Project Task | Falls back to the project's manager — see §5.3. |
From Requester's Manager Chain | Walks User Setup."Approver ID" upward from the requester. Depth-capped and cycle-guarded. |
Each role declares Fallback Role Code. If resolution produces nobody — a branch with no assigned manager — resolution falls through to the fallback role rather than silently yielding an empty level. This is what turns a data gap into a routing decision instead of a stalled requisition.
5.2 Role Assignment
| Field | Notes |
|---|---|
Role Code | The role being assigned |
Context Type | Dimension · Vendor · Item Category · Project · Project Task |
Context Code | The dimension code, where relevant |
Context Value | The dimension value, vendor no., project no. … |
User ID | The approver |
DFA Limit | Optional override of the role's default authority |
Valid From / Valid To | Lets a successor be staged ahead of a handover |
5.3 Deriving the project manager
The requirement is to derive the project manager from the project record rather than maintain the same relationship twice. Business Central supports this directly:
field(1036; "Project Manager"; Code[50]) // table Job
{
TableRelation = "User Setup";
}
User Setup."User ID" relates to User."User Name", so Job."Project Manager" already holds a Business Central user name. Resolution is a single hop and needs no additional table: change the project manager on the project, and the approver changes with it.
Two things to know, both verified against the BC 27.5 base application:
Job."Person Responsible"is not used. It isCode[20]relating toResource where(Type = const(Person))— a resource, not a user. Reaching a user from it takes three hops viaResource."Time Sheet Owner User ID"and only works for resources configured for time sheets.Job Taskhas no responsible-person or manager field at all. This is a Business Central limitation, not a design choice.From Project Tasktherefore resolves to the parent project's manager; where a specific task genuinely needs a different approver, add a Role Assignment row with Context Type = Project Task.
5.4 Dimensions: read the line, not the header
The requisition line carries eight shortcut dimensions and its own Dimension Set ID. The header carries only two.
All dimension-based matching and role resolution reads the line. Resolving from the header would silently fail for dimensions 3–8 — the routing would not error, it would just quietly ask the wrong person.
6. Grouping, aggregation and currency
6.1 Aggregation basis
Every path declares how its lines group:
| Basis | Effect |
|---|---|
Per Line | Each line is its own unit. DFA is tested against the line amount. |
Per Group Key | Lines sharing the key form one unit. DFA is tested against the group total. |
Whole Request | All lines on the requisition matching this path form one unit. |
When the basis is Per Group Key, the path selects which attributes compose the key — any combination of a nominated dimension, vendor, project and project task.
6.2 Worked grouping example
A requisition with five lines, where the STANDARD path groups by the BRANCH dimension:
| Line | Branch | Amount | Path | Unit |
|---|---|---|---|---|
| 10000 | AKL | 12,000 | STANDARD | STANDARD/AKL |
| 20000 | AKL | 8,000 | STANDARD | STANDARD/AKL |
| 30000 | WLG | 15,000 | STANDARD | STANDARD/WLG |
| 40000 | — | 60,000 | CAPEX | CAPEX |
| 50000 | — | 5,000 | CAPEX | CAPEX |
Three units result, and they progress independently and in parallel:
| Unit | Amount (LCY) | Tested against |
|---|---|---|
STANDARD/AKL | 20,000 | Auckland branch manager's authority |
STANDARD/WLG | 15,000 | Wellington branch manager's authority |
CAPEX | 65,000 | The CapEx path's levels, as one decision |
The requisition is approved when every unit is approved.
6.3 Currency
Requisition lines carry their own Currency Code, and Estimated Amount is stored in that currency. There is no stored local-currency amount and no stored exchange rate on either the header or the line, so LCY is computed.
Each unit stores:
| Field | Purpose |
|---|---|
Amount (LCY) | The figure DFA is tested against. Always populated. |
Currency Code + Amount | Populated only when every member line shares one currency, so ordinary single-currency requisitions display naturally. |
Mixed Currency | Flag shown to approvers, who would otherwise see an LCY total matching no line on screen. |
Exchange Rate Date | Frozen when the unit is created, re-stamped only on re-plan. |
Freezing the rate date is not a refinement. Without it, an exchange-rate movement partway through an approval could change which authority level is required between one approval and the next, and the audit trail could not explain why.
Do not use
Purch. Req. Hdr."Estimated Cost"for any financial test. It is a FlowField that sumsEstimated Amountacross lines regardless of each line's currency. On a mixed-currency requisition it is a meaningless number.
Business Central's own User Setup approval limits (Purchase Amount Approval Limit, Request Amount Approval Limit) are Integer and in LCY. The extension's DFA Level is Decimal, which is the better basis and is the one used.
7. Levels, authority and completion
7.1 Level types
| Type | Behaviour |
|---|---|
| Review | A mandatory sign-off that carries no financial authority and can never satisfy the DFA test. A non-financial CapEx reviewer is simply level 10 of type Review. |
| Approval | Carries DFA authority and can satisfy the financial test. |
7.2 Path and level configuration
Path-level rules:
| Rule | Default | Effect |
|---|---|---|
Aggregation Basis | Per Line | §6.1 |
Minimum Distinct Approvers | 1 | The quorum. 2 gives four-eyes. |
Requester May Approve | Allowed | When Not Allowed, §8 applies. |
Count Reviews Toward Quorum | No | A reviewer explicitly without authority should not constitute one of the "two eyes" unless an organisation decides otherwise. |
Same Person May Approve Multiple Levels | Allowed | They still count once toward the quorum. |
All Approvers Must Cover DFA | No | When No, one approver must cover; the others need not. |
Level fields: Level No., Description, Level Type, Approver Role Code (or an explicit User ID), DFA Source / Limit, Mandatory, Condition Set Code (the level applies only if matched), Escalate To Role Code.
7.3 The completion predicate
This single rule governs every path. There are no special cases.
A unit is Approved when all four conditions hold:
- Every mandatory Review level has been signed off.
- At least one Approval-type approver whose DFA limit ≥ the unit's
Amount (LCY)has approved.- The number of distinct persons who have approved is ≥
Minimum Distinct Approvers.- No barred person is among the approvers.
While any condition is unmet and unactivated levels remain → open the next level. When levels are exhausted with any condition still unmet → the unit is Blocked and raised to an Approval Administrator. It is never auto-approved.
Everything about quorum follows from this:
| Question | Answer |
|---|---|
| Is quorum per level or per path? | Per path. Levels supply approvers; the path counts distinct people across all of them. |
| Does sufficient DFA complete a path? | No. A path with an unmet quorum keeps escalating even when the first approver could authorise the full amount alone. This is the requirement "at least two approvers even when the first has enough DFA", and it needs no special handling — it is condition 3. |
| What drives escalation? | Any unmet condition: an outstanding review, a DFA shortfall, or a quorum shortfall. |
| How are distinct people counted? | As a set of user IDs. One person appearing at several levels counts once. |
| Must every approver cover the amount? | No by default — one must. Set All Approvers Must Cover DFA for the stricter reading. |
| Do reviewers count toward quorum? | No by default; configurable per path. |
| What if the quorum can never be met? | The unit blocks. The coverage report (§14.2) is designed to catch this during configuration instead. |
7.4 Worked example — straightforward approval
STANDARD path, Per Group Key by branch, Minimum Distinct Approvers = 1.
Unit STANDARD/AKL, amount 20,000. Auckland's branch manager has DFA 10,000; the regional manager has 50,000.
| Step | Level | Who | Outcome |
|---|---|---|---|
| 1 | 10 — Branch Manager | R. Chen (DFA 10,000) | Approves. DFA 10,000 < 20,000 → condition 2 unmet → open level 20. |
| 2 | 20 — Regional Manager | M. Patel (DFA 50,000) | Approves. DFA covers. Reviews: none. Distinct approvers: 2 ≥ 1. → Approved. |
7.5 Worked example — quorum overrides sufficient authority
This is the case that distinguishes this design from the old engine.
CAPEX path, Whole Request, Minimum Distinct Approvers = 2, Count Reviews Toward Quorum = No.
Unit CAPEX, amount 30,000.
| Step | Level | Who | Outcome |
|---|---|---|---|
| 1 | 10 — CapEx Reviewer (Review) | L. Novak | Signs off. Condition 1 met. Carries no authority and does not count toward quorum → conditions 2 and 3 unmet → open level 20. |
| 2 | 20 — Regional Manager | M. Patel (DFA 50,000) | Approves. DFA now covers 30,000 — condition 2 met. But distinct approvers = 1 (the reviewer does not count) < 2 → condition 3 unmet → open level 30. |
| 3 | 30 — CFO | S. Okafor | Approves. Distinct approvers = 2. All four conditions met → Approved. |
Under the old engine, step 2 would have ended the chain the moment authority was sufficient. The four-eyes requirement would have been silently unenforceable.
7.6 Worked example — one person covering two levels
A CFO covers for an absent branch manager (§8.3), then reaches their own level.
STANDARD path, Minimum Distinct Approvers = 2. Unit amount 20,000.
| Step | Level | Who | Outcome |
|---|---|---|---|
| 1 | 10 — Branch Manager | S. Okafor, acting for R. Chen | Approves. Recorded against Okafor as the person, with the acting-for context. DFA covers. Distinct approvers = 1. |
| 2 | 20 — Regional Manager | M. Patel | Approves. Distinct approvers = 2 → Approved. |
Had level 20 also resolved to Okafor, they would still have counted once, and the path would have continued to level 30 or blocked. A person cannot constitute a quorum by themselves, however many levels they occupy. This is the whole reason quorum is modelled as distinct persons across the path rather than as a count per level.
Whether Okafor may act at level 20 at all, having acted at level 10, is governed by Same Person May Approve Multiple Levels. The default is Allowed but counted once: barring it outright deadlocks small organisations, while counting once already prevents four-eyes being defeated.
8. Requester exclusion, delegation and absence
8.1 Identifying the requester
Requesters are frequently not Business Central users — they raise requisitions through the portal. Approvers always are. The case this rule exists for is an approver who raises a requisition and would otherwise be able to approve it.
Requester carries a BC User ID, resolvable from the requester's e-mail address (the Entra ID sync already matches requesters to directory users this way) and overridable by an administrator. It is blank for portal-only requesters, and the rule is then inert — someone who is not a user cannot be an approver in the first place.
The requisition header's Requested By is populated from that field, and propagates to lines.
A note for anyone reading the old code:
Requested Bywas previously never assigned by anything — it was only ever read. Any rule built on it would have been silently vacuous. It is also why "Currently With" showed blank after release or rejection.
8.2 When the requester resolves as the approver
If the path sets Requester May Approve = Not Allowed, the engine does not simply drop them and hope another candidate exists. It substitutes in a defined order:
- An eligible peer — another user resolving to the same role, with DFA ≥ the amount.
- An authorised higher role — the level's
Escalate To Role. - The next level — ordinary escalation.
- Blocked — raised to an Approval Administrator. Never auto-released.
Step 4 matters. Under the old engine, a requisition with no resolvable approver was auto-released by default. Excluding the requester could therefore have caused their own requisition to be released automatically — precisely the outcome the rule exists to prevent.
8.3 Absence cover
Absence records are date-bounded: User ID, From Date, To Date, Delegate To User ID, Reason.
The controls on them are the point of the feature, because unconstrained absence delegation is an obvious fraud route — nominate a friendly colleague, raise a requisition, approve it through them.
| Control | Rationale |
|---|---|
| Eligibility, not free choice | The delegate must hold the same role, or a role explicitly authorised to cover it (Role Cover Authorization). This is what defeats the self-selected buddy. |
| DFA floor | The delegate's authority must be ≥ the absent user's for the levels covered. |
| Never the requester | Re-checked when the approval is routed, not only when the absence is entered — an absence record long outlives any one requisition. |
| Governed maintenance | Absence records are maintainable only by Approval Administrators. Self-service absence with an unconstrained choice of delegate is the risk being mitigated. (See §17 — this is a business process decision to confirm.) |
| Immutable once relied upon | An absence that has been used for an approval cannot be silently edited; a change creates a new record. |
| Fully stamped | Every delegated approval records "X acting for Y under absence Z" in the audit log. |
| Resolution-time fallback | A delegate who has become ineligible — authority lowered, now the requester, absence expired — falls through the §8.2 ladder rather than causing a failure. |
Business Central's ad-hoc Delegate action and the bulk Delegate All Open Entries action are constrained the same way: the target must be in the eligible pool and must never be the requester.
8.4 Substitution order
Substitution is applied before the requester comparison, so the comparison is always against the person who will actually approve. Two consequences, both intended:
- A requester's substitute may approve — they are a different person, so four-eyes holds.
- Someone whose substitute is the requester is excluded.
Candidates are de-duplicated per unit. A user resolved twice — once via a role, once via an assignment — would otherwise appear to satisfy "two distinct approvers" alone.
9. Lifecycle and statuses
Line and header statuses are unchanged. New state lives on the unit and its steps:
| Object | States |
|---|---|
| Unit | Planned · Active · Approved · Rejected · Blocked · Cancelled |
| Level step | Planned · Active · Approved · Rejected · Skipped · Cancelled |
Blocked is deliberately kept off the header status, which keeps the Dataverse status option sets unchanged. It is surfaced through a Blocked Approvals list and a role-centre tile instead.
9.1 Rejection
A rejected line drops out; the rest of the requisition continues. The rejected line's remaining planned steps are cancelled, and any Whole Request unit recomputes its amount from the surviving lines when it next activates — so a requisition can fall below a threshold once a large line is rejected, and will then correctly skip a level it no longer meets.
The header remains Rejected while any line is rejected. That is the signal the requester needs. Purchase order conversion is line-driven, so approved lines on a partially rejected requisition still convert.
10. Audit trail
An append-only log entry is written for every decision, carrying:
timestamp · document and line · unit · level · role · resolved user · acting-for and absence reference · decision · amount basis · the amount and the DFA limit as at that moment · the running distinct-approver count · comment · the configuration in force.
Recording the amount and limit at decision time is what makes the trail defensible later, when configuration, role assignments or exchange rates have all moved on. "Who approved this, under what authority, against what figure" must remain answerable.
The existing Requestline Object JSON field is retained as the portal-facing summary only. It is Text[2048] and stops appending when full, which makes it unsuitable as a record of account.
11. Notifications and reminders
| Notification | Trigger |
|---|---|
| Approval request | A level activates. Existing custom HTML e-mail with portal deep links. |
| Reminder | Configurable number of days pending. |
| Escalation notice | A longer interval — to the approver's manager or an Approval Administrator. |
| Blocked | To the Approval Administrator and the requester. A blocked unit is otherwise invisible. |
| Decision | Existing requester notification on release or rejection. |
Already-actioned lines are culled from batched approver notifications, as today.
12. Resubmission and change handling
Lines are usage-restricted during approval, so changing one requires cancelling the approval and reopening.
On resubmission the affected units are re-matched and re-planned from scratch. Attributes and amounts may have changed, and continuing on a stale path is how the wrong person ends up approving something. The previous run is preserved in the audit log.
Adding a line to a requisition that holds a rejected line re-plans only the affected units.
13. Portal and Dataverse
Approval decisions can originate from the portal. The portal never writes status directly — it writes tsl_DesiredStatus, which Business Central interprets as a command:
| Desired Status | From | Action |
|---|---|---|
| Pending Approval | Open | Send for approval |
| Pending Approval | Rejected | Reopen, then send for approval |
| Open | Pending Approval | Cancel approval |
| Released | Pending Approval | Approve |
| Rejected | Pending Approval | Reject |
| Closed | any but Closed | Close, cancelling any in-flight approval |
Failures are captured in Desired Status Error and retried by a job queue entry, with two changes from current behaviour:
- Transient and permanent failures are distinguished. An incomplete Dataverse sync is transient and retries. A configuration error or a blocked unit is permanent — it stops retrying and surfaces instead of retrying indefinitely.
- Commands are idempotent. A repeated Desired Status command does not send an already-pending requisition twice.
14. Configuration governance
14.1 Change control
Paths and levels cannot be edited while the path is Enabled — the same protection the current design applies to active routings. Business Central's Change Log should be enabled on the approval configuration tables; this is a documented setup step.
14.2 Coverage report
Reports configuration that will fail at runtime:
- Roles that resolve to nobody
- Dimension values, projects or vendors with no Role Assignment, where a path will ask for one
- Paths with no levels
- Quorums that cannot be met given current role membership — a four-eyes path with only one eligible person
Run before go-live and on a schedule thereafter. As branches and projects multiply, this is the control that keeps growth from quietly breaking routing.
14.3 Simulation
Select a real requisition, or enter hypothetical attributes, and see: the matched group, the resulting path, the units with their amounts and currency basis, each level, and the resolved candidate approvers — with exclusions flagged and reasons given.
This is the highest-value tool in the design. It is where an administrator discovers, before going live, that a branch has no manager or that a four-eyes path has only one eligible approver.
15. Security and permissions
Three permission sets replace the current single all-or-nothing set:
| Set | Grants |
|---|---|
| Requester | Full access to requisitions; read-only on approval configuration |
| Approver | Read configuration; execute approve / reject / delegate |
| Administrator | Full access to configuration, absences and force approval |
Role Assignment and Absence data must be readable but not writable by approvers. An approver who can edit their own role assignment or absence record defeats every control in §8.
16. Reporting and monitoring
Pending approvals by approver and age · blocked units · cycle time by path and role · approvals by role · DFA exceptions · delegation and absence usage.
Delivered as list pages and queries over the audit log, surfaced on the existing role centre and available to Power BI.
17. Known limitations and open points
| Item | Detail |
|---|---|
| Force Approval bypasses the self-approval rule | An Approval Administrator can force-approve a requisition they raised themselves. This is existing behaviour, retained deliberately as an emergency escape, and it is logged. Organisations relying on strict segregation of duties should restrict who holds the Approval Administrator flag. |
| The submitter is not barred | Only the requester. Someone raising a requisition on another person's behalf may approve it. |
| No cross-level segregation | A person may approve at more than one level, subject to Same Person May Approve Multiple Levels. They count once toward quorum. |
| No task-level manager in BC | Job Task has no responsible-person field. Task-level resolution falls back to the project's manager. |
| No automated test coverage | Verification is a scripted manual pass. Given that these are financial controls, automated coverage is worth revisiting. |
| Who maintains absence records | Documented here as Administrator-only, which is the stronger control. To be confirmed as a business process decision — the permission model supports either. |
18. Mapping from the previous design
For anyone with a pilot tenant configured against the old model.
| Previous | Now | Notes |
|---|---|---|
| Approval Routing Header (one Active) | Approval Path, selected by Approval Group | Several paths may be active; selection is by condition and priority. |
| Approval Routing Line | Approval Path Level | Gains level type, quorum participation and an escalation role. |
| Condition Group Header / Line | Condition Set + Role Assignment | Split: matching criteria from who the approver is. The old table could only map a value to a role. |
| Approval Role (flat user list) | Approval Role with a resolution method | A role is now a question answered per line, not a list. |
| Approval Role Users | Role Assignment, or Fixed Users | Fixed lists remain right for CFO and reviewer roles. |
| DFA Aggregation Rule / Filter | Path Aggregation Basis + Group Key | These were configurable but not implemented — every comparison used the single line's amount. Aggregation now works. |
DFA Level::From Project / From Task | From Project resolution | These were declared but unimplemented, silently yielding a limit of zero. |
| Auto Release If No Workflow | Blocked state | No-approver situations block and notify rather than releasing. |
Configuration is re-entered rather than migrated; the pilot configuration is small enough that this is a mechanical job.
19. Reference — Business Central integration
Approval runs on standard Business Central objects. Retained: Approval Entry (454), the Approvals page, approval comments, delegation, Notification Entry and record restriction. Replaced: approver resolution, routing, aggregation, and the step state machine.
| BC object | Role |
|---|---|
Approval Entry (454) | The approval record. Sequence number carries the level. |
User Setup | Approver identity, Approver ID chain, substitute, Approval Administrator flag, and the extension's DFA Level field. |
Job."Project Manager" (field 1036) | Project manager derivation — see §5.3. |
| Workflow engine | Template PRQAW, category PURCHDOC, approver type Approval Routing. |
Currency Exchange Rate | LCY conversion — see §6.3. |
End of document. Review comments welcome before implementation begins.