1124 lines
90 KiB
Markdown
1124 lines
90 KiB
Markdown
# PostFundManagement (PFM) Platform — Engineering & Architecture Guide (GEMINI.md)
|
||
|
||
> **Document Context**: This document serves as the primary system specification, technical architectural guide, and development roadmap for the **Post-Fund Management, Performance & Impact Management Platform (PFM)**. It is directly derived from the **PFM System Requirements Specification (SRS) v2.0** (authored by S. Phiri, 13 August 2026) and reflects the actual technical implementation and project layout in this repository.
|
||
>
|
||
> **Role for Gemini / Antigravity**: Use this file as your authoritative blueprint when extending, debugging, testing, or implementing any features, endpoints, domain models, or UI components across the PFM solution.
|
||
|
||
---
|
||
|
||
## 1. Executive Summary & Product Perspective
|
||
|
||
The **Post-Fund Management (PFM) Platform** is an enterprise-grade multi-tenant software system designed for organisations that administer and govern **Innovation, Development, NGO/NPO, Donor, Grant, Blended-Finance, and Impact-Funding** portfolios.
|
||
|
||
### 1.1 Scope Boundary
|
||
* **Upstream Boundary (Out of Scope)**: Call for applications, proposal intake, eligibility screening, technical/financial review, due diligence, and approval committee deliberation. Upstream systems hand off approved awards into PFM via REST APIs, webhooks, or batch imports.
|
||
* **Controlled Lifecycle (In Scope)**: The PFM platform lifecycle strictly begins at **APPROVED FUNDING AWARD**. Recording an approved award triggers the automated Contract/Agreement generation workflow. From that entry point, PFM governs:
|
||
$$\text{Award Intake} \longrightarrow \text{Contracting} \longrightarrow \text{Onboarding} \longrightarrow \text{Implementation / Milestones} \longrightarrow \text{Disbursement Control} \longrightarrow \text{M\&E / Risk / Compliance} \longrightarrow \text{Close-Out} \longrightarrow \text{Post-Fund Monitoring} \longrightarrow \text{Portfolio Impact Reporting}$$
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ EXTERNAL / UPSTREAM (Out of Scope) │
|
||
│ Call for Apps ─> Application ─> Assessment ─> Approval │
|
||
└────────────────────────────┬────────────────────────────┘
|
||
│ (Handover via API / Import)
|
||
════════════════════════════════▼══════════════════════════════════ SYSTEM SCOPE BEGINS
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ PFM CONTROLLED LIFECYCLE (In Scope) │
|
||
│ │
|
||
│ [M01] Award Intake & Registration │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ [M02] Contract & Agreement Management │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ [M03] Recipient Onboarding (Banking & KYC Gate) │
|
||
│ │ │
|
||
│ ├──────────────────────────┐ │
|
||
│ ▼ ▼ │
|
||
│ [M04] Project Management [M05] Budget & Finance │
|
||
│ │ │ │
|
||
│ ▼ ▼ │
|
||
│ [M07] Milestones & Evidence ──> [M06] Disbursement │
|
||
│ │ │
|
||
│ ├─────────────────────────────────────┐ │
|
||
│ ▼ ▼ │
|
||
│ [M08] M&E / MEAL Indicators [M09] Risk Mgmt │
|
||
│ [M10] Compliance & [M11] ESG [M16] Issues │
|
||
│ [M12] Innovation / TRL [M17] Site Visits│
|
||
│ [M13] Beneficiaries (POPIA) [M15] Change Ctrl│
|
||
│ │ │ │
|
||
│ └──────────────────┬──────────────────┘ │
|
||
│ ▼ │
|
||
│ [M18] Formal Project Close-Out │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ [M19] Post-Fund Monitoring (6 to 60 Months) │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ [M20 / M22] Executive Portfolio Dashboards & Reports │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Tech Stack Across Projects in the Repository
|
||
|
||
The solution is architected using **Clean Architecture** and **Domain-Driven Design (DDD)** on **.NET 10.0 (C# 13)** for the backend, paired with modern **React 19 / TypeScript / Vite** frontend applications.
|
||
|
||
```
|
||
PostFundManagement/
|
||
├── PostFundManagement.Domain/ # Core entities, enums, value objects, domain logic
|
||
├── PostFundManagement.Infrastructure/ # EF Core 10, PostgreSQL (Npgsql), Migrations, Data Protection
|
||
├── PostFundManagement.Application/ # CQRS Handlers, Mediator, DTOs, Validations, S3, Email, Quartz
|
||
├── PostFundManagement.Api/ # Minimal APIs / Carter endpoints, Auth, Swagger/Scalar, Versioning
|
||
├── PostFundManagement.Tests/ # Unit & Integration test suite (xUnit, Coverlet)
|
||
├── PostFundManagement.ConsoleUI/ # Internal Staff & Admin Web Portal (React 19 + Vite 6 + Tailwind 4)
|
||
├── PostFundManagement.PartnerUI/ # External Grantee / Partner Portal (React 19 + Vite 8 + Tailwind 4)
|
||
└── docker-compose.yml # Container orchestration for API and Frontends
|
||
```
|
||
|
||
### 2.1 Backend Projects (.NET 10.0 / C# 13)
|
||
|
||
| Project | Target Framework | Core Libraries & Packages | Architectural Purpose |
|
||
| :--- | :--- | :--- | :--- |
|
||
| **`PostFundManagement.Domain`** | `net10.0` | `Microsoft.EntityFrameworkCore` 10.0.12, `Npgsql.EntityFrameworkCore.PostgreSQL` 10.0.3, `Humanizer` 3.0.10, `Hashids.net` 1.7.0, `OpenTelemetry` 1.18.0, `Quartz` 4.1.0, `FluentResults` 4.0.0, `Mediator.Abstractions` 3.0.2, `AWSSDK.S3` 4.0.103.3, `MailKit`/`MimeKit` 4.18.0 | Contains domain entities, aggregate roots, value objects, domain enums, constant definitions, and interfaces. Clean of presentation concerns. |
|
||
| **`PostFundManagement.Infrastructure`** | `net10.0` | `Microsoft.EntityFrameworkCore` 10.0.12, `Npgsql.EntityFrameworkCore.PostgreSQL` 10.0.3, `Duende.AccessTokenManagement` 4.2.0, EF Core Tools & Design | Houses `PostFundManagementDbContext`, EF Core fluent configurations, entity-to-table mappings, PostgreSQL schema definitions, multitenancy filters, and migrations. |
|
||
| **`PostFundManagement.Application`** | `net10.0` | `Mediator.SourceGenerator` / `Mediator.Abstractions` 3.0.2, `FluentResults` 4.0.0, `Microsoft.Extensions.Caching.Hybrid` 10.10.0, `Microsoft.Extensions.Caching.StackExchangeRedis` 10.0.12, `Refit` 15.2.0, `Duende.AccessTokenManagement.OpenIdConnect` 4.2.0, `Quartz` 4.1.0, `AWSSDK.S3` | Implements CQRS command/query handlers, domain validators, S3 object storage clients (Garage S3), background jobs (Quartz), notification dispatchers, and business logic. |
|
||
| **`PostFundManagement.Api`** | `net10.0` | `Microsoft.AspNetCore.OpenApi` 10.0.12, `Scalar.AspNetCore` 2.17.4, `Asp.Versioning.Http` 10.2.3, `IdentityModel.AspNetCore` 4.3.0, `Microsoft.AspNetCore.Authentication.JwtBearer` 10.0.12, `AspNetCore.HealthChecks.NpgSql` 9.0.0, `AspNetCore.HealthChecks.UI` | Minimal API endpoint definitions divided by business slices (`Grants`, `Projects`, `Execution`, `Governance`, `Evidence`, `Communications`, `Users`). Handles routing, auth policies, error problem details, and OpenAPI metadata. |
|
||
| **`PostFundManagement.Tests`** | `net10.0` | `xunit` 2.9.3, `xunit.runner.visualstudio` 3.1.4, `Microsoft.NET.Test.Sdk` 17.14.1, `coverlet.collector` 6.0.4 | Unit and integration test coverage for domain rules, CQRS handlers, financial calculation models, and gate condition validations. |
|
||
|
||
### 2.2 Frontend Projects (React 19 / TypeScript)
|
||
|
||
#### **`PostFundManagement.ConsoleUI`** (Internal Staff / Portfolio Administration Portal)
|
||
* **Package Manager**: **Yarn** (`yarn.lock` is authoritative; `npm` must not be used)
|
||
* **Runtime & Bundler**: React 19.0.1, TypeScript 5.8.2, Vite 6.2.3
|
||
* **Styling**: Tailwind CSS v4.1.14 (`@tailwindcss/vite`, `@tailwindcss/postcss`)
|
||
* **Routing**: React Router DOM v7.18.3
|
||
* **State & Server Querying**: `@tanstack/react-query` v5.102.8, Axios 1.20.0
|
||
* **Forms & Validation**: `react-hook-form` 7.87.0, `zod` 3.25.76, `@hookform/resolvers`
|
||
* **Authentication**: `oidc-client-ts` 3.5.0 (Duende IdentityServer / STS PKCE authentication)
|
||
* **Animation & Icons**: `motion` (Framer Motion) 12.23.24, `lucide-react` 0.546.0
|
||
* **Exports & Utilities**: `jspdf` 4.2.1, `jspdf-autotable` 5.0.8, `xlsx` 0.18.5, `hashids` 2.3.0
|
||
* **AI Capabilities**: `@google/genai` 2.4.0 (for intelligent portfolio insights and drafting)
|
||
* **Target Users**: Funding Administrators, Programme Managers, Finance Officers, M&E Officers, Risk Managers, System Administrators, Executive Leadership.
|
||
|
||
#### **`PostFundManagement.PartnerUI`** (External Recipient / Grantee Portal)
|
||
* **Package Manager**: **Yarn** (`yarn.lock` is authoritative; `npm` must not be used)
|
||
* **Runtime & Bundler**: React 19.0.1, TypeScript 7.0.2, Vite 8.3.0
|
||
* **Styling & Effects**: Tailwind CSS v4.3.3, `motion` 12.23.24, `ogl` 1.0.11 (WebGL visual elements like MoltenMetal, BorderGlow, SpecularButton)
|
||
* **Routing**: React Router DOM v7.18.4
|
||
* **State & Server Querying**: `@tanstack/react-query` v5.104.0, Axios 1.20.0
|
||
* **Forms & Validation**: `react-hook-form` 7.89.0, `zod` 4.6.5
|
||
* **Authentication**: `oidc-client-ts` 3.5.0
|
||
* **Exports & Utilities**: `jspdf`, `xlsx`, `hashids`
|
||
* **Target Users**: Grantee / Partner Project Directors, Financial Controllers, Field Officers submitting evidence and disbursement drawdown requests.
|
||
|
||
### 2.3 Infrastructure, Storage & Security Topology
|
||
* **Database**: PostgreSQL 14+ (Separate connection strings for primary transactional store `pfm` and scheduler store `pfm-scheduler`).
|
||
* **Object / Evidence Store**: S3-compatible storage (e.g., Garage S3 / AWS S3) with pre-signed upload URLs and CDN acceleration.
|
||
* **Cache**: Redis (`StackExchange.Redis` + ASP.NET Core `OutputCaching` 2-minute base policy on Minimal API GET endpoints + HybridCache). Paired with TanStack React Query in `ConsoleUI` and Axios mutation versioning (`_v` parameter) to guarantee instant reads and zero stale data after mutations.
|
||
* **Authentication / STS**: Duende IdentityServer / Skoruba Administration (`https://sts.security.khongisa.co.za`).
|
||
* **Observability**: OpenTelemetry exporters streaming traces, metrics, and logs to Aspire Dashboard / OTLP collectors.
|
||
|
||
---
|
||
|
||
## 3. Master Business Rules (BR-001 to BR-010)
|
||
|
||
The system enforces 10 cardinal business rules across all modules and APIs:
|
||
|
||
| Rule ID | Rule Name | Specification & Enforcement Logic | MoSCoW |
|
||
| :--- | :--- | :--- | :--- |
|
||
| **BR-001** | **Approval is Entry Point** | No contract, project, or budget may be created without a verified, approved funding award reference. | **Must Have** |
|
||
| **BR-002** | **Contract Initiated Automatically** | Capturing and activating an approved award record automatically transitions into and initializes the Contract/Agreement workflow. | **Must Have** |
|
||
| **BR-003** | **No Uncontrolled Disbursement** | No payment or disbursement may be approved or released unless all applicable conditions precedent and milestone evidence validations are formally satisfied. | **Must Have** |
|
||
| **BR-004** | **Evidence-Based Performance** | Reported performance on milestones and indicators must be substantiated by mandatory uploaded evidence documents/files before approval. | **Must Have** |
|
||
| **BR-005** | **Single Source of Truth** | A data entity (recipient org, award amount, bank details, baseline) is captured once and referenced immutably across the full lifecycle. | **Must Have** |
|
||
| **BR-006** | **Configurable Funding Instruments** | Funding rules, milestones, payment schedules, KPIs, reporting frequencies, and risk thresholds must not be hardcoded; they vary by funding instrument type. | **Should Have** |
|
||
| **BR-007** | **Full Auditability** | Every material decision, status transition, payment approval, budget adjustment, and evidence submission writes an immutable, user-attributed log entry. | **Must Have** |
|
||
| **BR-008** | **Results Hierarchy** | Performance indicators strictly align to the causal chain: $\text{Input} \longrightarrow \text{Activity} \longrightarrow \text{Output} \longrightarrow \text{Outcome} \longrightarrow \text{Impact}$. | **Must Have** |
|
||
| **BR-009** | **Indicator Integrity** | Every measurable metric follows the evaluation sequence: $\text{Baseline} \longrightarrow \text{Target} \longrightarrow \text{Actual} \longrightarrow \text{Variance} \longrightarrow \text{Supporting Evidence}$. | **Must Have** |
|
||
| **BR-010** | **Automated Portfolio Roll-Up** | Activity and project data automatically roll up to Programme $\longrightarrow$ Portfolio $\longrightarrow$ Institution $\longrightarrow$ National/Donor reporting tiers without manual consolidation. | **Must Have** |
|
||
|
||
---
|
||
|
||
## 4. System Architecture & Data Models
|
||
|
||
### 4.1 Core Domain Entities & Relationships
|
||
|
||
```mermaid
|
||
erDiagram
|
||
Organisation ||--o{ Award : "receives"
|
||
FundingProgramme ||--o{ Award : "governs"
|
||
Award ||--|| Contract : "initiates"
|
||
Award ||--|| Project : "funds"
|
||
Project ||--o{ Budget : "allocates"
|
||
Project ||--o{ Milestone : "tracks"
|
||
Project ||--o{ Risk : "identifies"
|
||
Project ||--o{ Indicator : "measures"
|
||
Project ||--o{ BeneficiaryGroup : "reaches"
|
||
Milestone ||--o{ Disbursement : "gates"
|
||
Milestone ||--o{ Evidence : "requires"
|
||
Indicator ||--o{ Evidence : "substantiated_by"
|
||
FundingProgramme ||--o{ PortfolioSnapshot : "aggregates"
|
||
```
|
||
|
||
### 4.2 Key System Enums & Taxonomies
|
||
|
||
* **`IndustrySector`**: ICT, Agriculture, Mining, Manufacturing, Energy, Healthcare, Education, Financial Services, Tourism, Biotechnology.
|
||
* **`ProjectType`**: Research & Development (R&D), Technology Commercialisation, Infrastructure, Capacity Building, Social Impact, Community Development.
|
||
* **`EvidenceType`**: Invoice, Bank Statement, Progress Report, Geotagged Photograph, Attendance Register, Research Output, Prototype Demonstration, Certification, Patent Filing, Audited Financial Statement, Beneficiary Schedule.
|
||
* **`FundingInstrument`**:
|
||
1. *Grant*: Award $\to$ Milestone $\to$ Disbursement $\to$ Reporting.
|
||
2. *Development Finance*: Investment $\to$ Drawdown $\to$ Conditions $\to$ Repayment / Performance.
|
||
3. *Impact Investment*: Investment $\to$ KPI $\to$ Impact $\to$ Financial/Social Return.
|
||
4. *Blended Finance*: Multi-tranche (Grant + Debt + Equity + Technical Assistance).
|
||
5. *NGO Programme*: Grant $\to$ Activities $\to$ Beneficiaries $\to$ Outcomes $\to$ Impact.
|
||
6. *Innovation Funding*: Grant $\to$ TRL Progression $\to$ Prototype $\to$ Validation $\to$ Commercialisation.
|
||
|
||
---
|
||
|
||
## 5. Functional Requirements Breakdown (Modules M01 – M25)
|
||
|
||
The SRS v2.0 establishes **25 functional modules** with **304 prioritized requirements** and Given/When/Then acceptance criteria:
|
||
|
||
### **Module M01 — Award Intake & Funding Registration**
|
||
* **Purpose**: Capture and validate the official award record following external approval committee sanction.
|
||
* **Priority**: 17 Must Have
|
||
* **Key Requirements & Acceptance Criteria**:
|
||
* `FR-M01-001` to `007`: System shall capture mandatory fields: External approval reference number, funding amount ($> 0$), funding period (start & end dates), programme, instrument, recipient organisation, and project title.
|
||
* `FR-M01-016` (Duplicate Prevention): Given an award registration in progress, when the external reference number matches an existing record, then the system blocks creation and presents the existing award.
|
||
* `FR-M01-017` (Contract Initiation Trigger): When an award is successfully registered, the system automatically creates the corresponding `Contract` record in `DRAFT` status (BR-002).
|
||
* **Module Acceptance Gates**: Award cannot be activated without external reference, recipient in master data, amount $> 0$, and valid date boundaries.
|
||
|
||
### **Module M02 — Contract & Agreement Management**
|
||
* **Purpose**: Convert approved awards into legally binding, monitorable contracts with version control.
|
||
* **Workflow**: `DRAFT` $\longrightarrow$ `UNDER REVIEW` $\longrightarrow$ `EXECUTED` $\longrightarrow$ `AMENDED` (versioned) $\longrightarrow$ `EXPIRED / TERMINATED`.
|
||
* **Priority**: 22 Must Have
|
||
* **Key Requirements & Acceptance Criteria**:
|
||
* `FR-M02-001` to `012`: Support contract templates, configurable legal clauses, signing parties, effective & expiry dates, contractual obligations, payment conditions, and digital/scanned signatures.
|
||
* `FR-M02-020`: The executed agreement is locked as the authoritative legal version. Subsequent amendments (`FR-M02-021`) generate incremented versions (v1.1, v2.0) with full diff logs.
|
||
* **Module Acceptance Gates**: A contract cannot transition to `EXECUTED` unless approved award exists, authorised parties are identified, agreement version is set, required signatures are attached, and effective date exists.
|
||
|
||
### **Module M03 — Recipient Onboarding**
|
||
* **Purpose**: Verify recipient organization credentials, key personnel, KYC compliance, and banking details before funds can be released.
|
||
* **Priority**: 13 Must Have
|
||
* **Key Requirements & Acceptance Criteria**:
|
||
* `FR-M03-001` to `006`: Establish organization profile, key personnel, verified banking details, tax/statutory compliance documents, project team assignments, and configurable onboarding checklists.
|
||
* `FR-M03-012` (First Disbursement Gate): Onboarding verification is a mandatory prerequisite for the first disbursement. If banking details are unverified or compliance docs are missing, disbursement approvals are strictly blocked (AT-03).
|
||
* `FR-M03-013`: Recipient profiles and verified banking details can be reused across subsequent awards (BR-005).
|
||
|
||
### **Module M04 — Project & Programme Management**
|
||
* **Purpose**: Plan, schedule, and track implementation at the work breakdown structure (WBS), activity, and task levels.
|
||
* **Workflow**: `SET-UP` $\longrightarrow$ `READY FOR IMPLEMENTATION` $\longrightarrow$ `ACTIVE` $\longrightarrow$ `AT RISK` $\longrightarrow$ `SUSPENDED` $\longrightarrow$ `COMPLETED` $\longrightarrow$ `CLOSED` $\longrightarrow$ `POST-FUND` $\longrightarrow$ `ARCHIVED`.
|
||
* **Priority**: 21 Must Have
|
||
* **Key Requirements & Acceptance Criteria**:
|
||
* `FR-M04-001` to `014`: Work breakdown structure, activities, tasks, responsible assignees, activity dependencies, deliverable links, and timelines.
|
||
* `FR-M04-015`: Automatically monitor planned vs actual completion. Highlight delayed activities (`FR-M04-017`).
|
||
* `FR-M04-018`: Project progress percentage is calculated automatically from verified milestone completions.
|
||
* `FR-M04-021`: Project status and metrics automatically roll up to programme level (BR-010).
|
||
|
||
### **Module M05 — Budget & Financial Management**
|
||
* **Purpose**: Maintain the approved project budget, commitments, actual expenditure, variance, and leveraged co-funding.
|
||
* **Priority**: 18 Must Have, 1 Should Have
|
||
* **Core Financial Calculations**:
|
||
$$\text{Budget Utilisation (\%)} = \frac{\text{Actual Expenditure}}{\text{Approved Budget}} \times 100$$
|
||
$$\text{Variance} = \text{Approved Budget} - (\text{Actual Expenditure} + \text{Committed Expenditure})$$
|
||
$$\text{Leverage Ratio} = \frac{\text{Additional External Capital Leveraged}}{\text{Total PFM Funding Award}}$$
|
||
* **Key Requirements & Acceptance Criteria**:
|
||
* `FR-M05-001` to `013`: Maintain multi-year budgets, budget categories, commitments, forecasts, and expenditures.
|
||
* `FR-M05-011` / `AT-04`: If cumulative commitments plus expenditure exceed an approved budget line, the system blocks the transaction and demands an approved budget reallocation/revision.
|
||
* `FR-M05-017` & `018`: Track third-party co-funding and private sector co-investment leverage.
|
||
|
||
### **Module M06 — Disbursement Management**
|
||
* **Purpose**: Control the release of grant tranches against verified milestones and satisfied conditions precedent.
|
||
* **Priority**: 14 Must Have, 1 Should Have
|
||
* **Key Requirements & Acceptance Criteria**:
|
||
* `FR-M06-001` to `007`: Multi-tranche disbursement schedules, milestone linkage, conditions precedent validation, payment request capture, multi-stage approval workflow, payment reference recording, and bank reconciliation.
|
||
* `FR-M06-008` & `012` / `AT-05`: Payment release requires: (1) Linked milestone is in `VERIFIED` status, (2) Conditions precedent satisfied, (3) Recipient onboarding complete. Duplicate payment approval attempts on the same tranche are blocked.
|
||
|
||
### **Module M07 — Milestone & Deliverable Management**
|
||
* **Purpose**: Track project milestones and deliverable submissions through rigorous evidence-based verification.
|
||
* **Workflow**: `PLANNED` $\longrightarrow$ `IN PROGRESS` $\longrightarrow$ `SUBMITTED` $\longrightarrow$ `UNDER REVIEW` $\longrightarrow$ `(REWORK REQUIRED | VERIFIED)` $\longrightarrow$ `PAID` $\longrightarrow$ `CLOSED`.
|
||
* **Priority**: 10 Must Have
|
||
* **Key Requirements & Acceptance Criteria**:
|
||
* `FR-M07-001` to `005`: Define contractual milestones, associate deliverables, define mandatory evidence types, monitor due dates, and allow grantees to submit evidence.
|
||
* `FR-M07-006` & `007`: Authorised internal reviewers verify or reject submissions (with mandatory rework reasoning).
|
||
* `FR-M07-009`: Overdue milestones are automatically flagged and escalated to Programme Managers.
|
||
|
||
### **Module M08 — Monitoring, Evaluation, Accountability & Learning (M&E / MEAL)**
|
||
* **Purpose**: Track the results chain from input to impact against baseline and target indicators.
|
||
* **Priority**: 20 Must Have, 2 Should Have
|
||
* **Indicator RAG Status Thresholds**:
|
||
* 🟢 **Green (On Track)**: $\ge 90\%$ of target
|
||
* 🟡 **Amber (At Risk)**: $70\% - 89\%$ of target
|
||
* 🔴 **Red (Off Track)**: $< 70\%$ of target
|
||
* **Key Requirements & Acceptance Criteria**:
|
||
* `FR-M08-001` & `002`: Support Theory of Change (ToC) and Logical Framework (Logframe) models.
|
||
* `FR-M08-003` to `018`: Baseline, target, actual recording with mandatory evidence attachment (`FR-M08-016` / `AT-06`).
|
||
* `FR-M08-021`: Clearly segregate output indicators (operational delivery) from outcome indicators (behavioural/economic changes).
|
||
|
||
### **Module M09 — Risk Management**
|
||
* **Purpose**: Identify, assess, mitigate, and escalate risks across projects and portfolios.
|
||
* **Risk Categories**: Strategic, Financial, Operational, Technical, Commercial, Governance, Regulatory, Reputational, Cybersecurity, Fraud, ESG, Sustainability.
|
||
* **Priority**: 8 Must Have, 2 Should Have
|
||
* **Key Requirements & Acceptance Criteria**:
|
||
* `FR-M09-001` to `005`: Project risk registers, risk owners, Likelihood (1-5) and Impact (1-5) scoring, mitigation actions, and automated **Residual Risk** calculation.
|
||
* `FR-M09-006` / `AT-07`: High-rated risks with overdue mitigations automatically escalate to the Risk Dashboard (`D05`) and Programme Manager alerts.
|
||
* `FR-M09-008`: Dynamic portfolio-wide risk heatmaps.
|
||
|
||
### **Module M10 — Compliance Management**
|
||
* **Purpose**: Manage statutory, contractual, financial, tax, POPIA/data protection, and safeguarding obligations.
|
||
* **Priority**: 8 Should Have
|
||
* **Key Requirements**: Obligation tracking, automatic due date reminders, overdue escalation (`AT-08`), and compliance exception registers.
|
||
|
||
### **Module M11 — ESG & Sustainability**
|
||
* **Purpose**: Track Environmental, Social, and Governance metrics alongside post-grant organizational viability.
|
||
* **Priority**: 8 Should Have
|
||
* **Key Requirements**: Environmental indicators (carbon offset, waste, energy), Social indicators (diversity, community development), Governance ratings, and project sustainability assessments.
|
||
|
||
### **Module M12 — Innovation & Technology (NSI Differentiator)**
|
||
* **Purpose**: Specialized tracking for South African National System of Innovation (NSI) and technology grants.
|
||
* **Priority**: 9 Should Have, 4 Could Have
|
||
* **Key Requirements**:
|
||
* Innovation Passport creation (`FR-M12-001`).
|
||
* **Technology Readiness Level (TRL 1–9)** progression tracking (Starting TRL vs Current TRL).
|
||
* Tracking prototypes, IP assets/patents (`FR-M12-006`), validation tests, technology stalls (`FR-M12-012`), commercialisation status, licensing agreements, and follow-on venture capital.
|
||
|
||
### **Module M13 — Beneficiary & Stakeholder Management**
|
||
* **Purpose**: Register and report on direct/indirect beneficiaries for social and community development grants.
|
||
* **Priority**: 9 Could Have
|
||
* **Key Requirements**: Demographic categorization (gender, youth, disability, geography), outcome linkages, duplicate detection, and **POPIA-compliant field-level data protection and anonymization** (`FR-M13-007`).
|
||
|
||
### **Module M14 — Evidence & Document Management**
|
||
* **Purpose**: Secure repository for all supporting files, verifications, audit sheets, and legal documents.
|
||
* **Priority**: 9 Must Have
|
||
* **Key Requirements**:
|
||
* File uploads against milestones, indicators, and change requests.
|
||
* Metadata tracking: file versioning, hash verification, MIME type restrictions, and maximum size limits.
|
||
* Storage integration via pre-signed S3 URLs.
|
||
* Immutable audit trail for all document uploads, reviews, approvals, and deletions (`FR-M14-007`).
|
||
|
||
### **Module M15 — Change Control & Variation Management**
|
||
* **Purpose**: Governed process for adjusting project scope, budget reallocations, milestones, or timeline extensions.
|
||
* **Priority**: 8 Should Have
|
||
* **Key Requirements**:
|
||
* Formal Change Requests with impact assessments and justifications (`FR-M15-001` to `005`).
|
||
* Approval workflow. When approved, baseline updates are recorded while preserving original approved baselines for audit history (`FR-M15-008` / `AT-09`).
|
||
|
||
### **Module M16 — Issue, Exception & Escalation**
|
||
* **Purpose**: Log, triage, assign, and resolve operational blockers during project execution.
|
||
* **Priority**: 7 Should Have
|
||
* **Key Requirements**: Issue ticketing, severity grading, owner assignment, automated escalation, linking issues to risks and milestones, and in-app requests for PM commentary.
|
||
|
||
### **Module M17 — Site / Field Monitoring**
|
||
* **Purpose**: In-field inspection and audit visits with mobile-friendly offline support.
|
||
* **Priority**: 7 Should Have, 1 Could Have
|
||
* **Key Requirements**: Visit scheduling, digital standardized checklists, offline observation caching and sync (`NFR-010`), geotagged photo capture, and automated site visit report generation.
|
||
|
||
### **Module M18 — Close-Out Management**
|
||
* **Purpose**: Formal project decommission upon settling all contractual, financial, and reporting obligations.
|
||
* **Priority**: 9 Should Have
|
||
* **Key Requirements**: Final technical/financial report verification, asset reconciliation, lessons learned capture, formal management closure approval, and automated handover into Post-Fund Monitoring (`FR-M18-009` / `AT-10`).
|
||
|
||
### **Module M19 — Post-Fund Monitoring (Fundamental Value Driver)**
|
||
* **Purpose**: Track long-term economic sustainability, organizational survival, and lasting impact after grant funding has concluded.
|
||
* **Standard Follow-Up Intervals**: **6 months, 12 months, 24 months, 36 months, and 60 months**.
|
||
* **Priority**: 10 Should Have, 2 Could Have
|
||
* **Key Metrics Tracked**: Enterprise survival status, recurring revenue, net new jobs sustained, additional investment/capital raised, ongoing technology adoption, and commercial scale.
|
||
|
||
### **Module M20 — Portfolio Management**
|
||
* **Purpose**: Executive cross-project aggregation and strategic portfolio analytics.
|
||
* **Priority**: 15 Must Have
|
||
* **Key Visualizations & Analytics**: Funding by sector, geography (provinces/municipalities), organisation type, TRL stage, and funding programme. Real-time lists of high-risk and underperforming projects.
|
||
|
||
### **Module M21 — Knowledge & Learning**
|
||
* **Purpose**: Institutional memory capturing what works and what fails to improve future programme design.
|
||
* **Priority**: 8 Could Have
|
||
* **Key Requirements**: Lessons learned catalog, failure taxonomy classification, case study generation, and evaluation recommendations.
|
||
|
||
### **Module M22 — Reporting & Business Intelligence**
|
||
* **Purpose**: High-fidelity dashboards and role-tailored reporting exports (PDF, Excel).
|
||
* **Priority**: 12 Must Have, 1 Should Have
|
||
* **Key Deliverable**: **Executive Portfolio Dashboard (D01)** rendering 6 headline KPIs:
|
||
1. Total Portfolio Value ($ZAR$)
|
||
2. Active Awards Count
|
||
3. Total Disbursed Capital ($ZAR$)
|
||
4. Overall % On Track
|
||
5. Count of Projects At Risk
|
||
6. Total Beneficiaries Reached
|
||
* Accompanying panels: Funding by Sector donut chart, Portfolio Risk Heatmap, Funding-to-Impact trendline, and an actionable "Projects Requiring Attention" table.
|
||
|
||
### **Module M23 — Administration & Configuration**
|
||
* **Purpose**: System setup, multi-tenant provisioning, role definitions, and taxonomy governance.
|
||
* **Priority**: 3 Should Have, 8 Could Have
|
||
* **Key Requirements**: Tenant isolation, user onboarding, role-permission matrix configuration (Create, Read, Update, Approve, Verify, Export - CRUAVX), and funding instrument parameterization.
|
||
|
||
### **Module M24 — Integration & API Management**
|
||
* **Purpose**: Secure interfaces for upstream intake, payment gateways, and external registries.
|
||
* **Priority**: 2 Should Have, 3 Could Have, 3 Won't Have (Deep ERP, GIS map layer, and external research DBs deferred).
|
||
* **Key Requirements**: REST APIs, inbound webhook handlers, API transaction logging, and retry resilience (Polly).
|
||
|
||
### **Module M25 — Audit & Assurance**
|
||
* **Purpose**: Full traceability and compliance verification for internal and external auditors.
|
||
* **Priority**: 9 Should Have
|
||
* **Key Requirements**: Queryable immutable audit log (`AT-12`), history of who approved every payment, contract version history, and full audit trails tracing any portfolio metric back to source evidence.
|
||
|
||
---
|
||
|
||
## 6. Non-Functional Requirements (NFR-001 – NFR-010)
|
||
|
||
| ID | Category | Requirement & Acceptance Target | MoSCoW |
|
||
| :--- | :--- | :--- | :--- |
|
||
| **NFR-001** | **Performance (Pages)** | Standard transactional pages render within $\le 3$ seconds at the 95th percentile under normal load. | **Must Have** |
|
||
| **NFR-002** | **Performance (Dashboards)**| Standard dashboards render in $\le 5$ seconds; heavy reports process asynchronously with user notifications. | **Must Have** |
|
||
| **NFR-003** | **Availability** | Production uptime $\ge 99.5\%$, with higher availability targets for disbursement/payment services. | **Must Have** |
|
||
| **NFR-004** | **Security & Access** | Multi-factor authentication (MFA), TLS 1.3 in transit, AES-256 at rest, RBAC/ABAC enforcement, secure API tokens. | **Must Have** |
|
||
| **NFR-005** | **Scalability** | Capable of supporting thousands of organisations, tens of thousands of projects, and millions of evidence records. | **Should Have** |
|
||
| **NFR-006** | **Disaster Recovery** | Recovery Point Objective ($\text{RPO}$) $\le 1$ hour; Recovery Time Objective ($\text{RTO}$) $\le 4$ hours. | **Should Have** |
|
||
| **NFR-007** | **Multi-Tenancy Isolation** | Logical tenant isolation enforced across all database queries. Data from Tenant A never leaks to Tenant B. | **Must Have** |
|
||
| **NFR-008** | **Audit Trail Integrity** | Tamper-evident, timestamped, append-only audit trail for all state changes and financial approvals. | **Must Have** |
|
||
| **NFR-009** | **Configurability** | Business rules, payment triggers, and KPI libraries configurable by admins without code changes or redeployments. | **Should Have** |
|
||
| **NFR-010** | **Mobile Offline Tolerance** | Site visit and field data collection works seamlessly without connectivity, synchronizing once back online. | **Should Have** |
|
||
|
||
---
|
||
|
||
## 7. Cross-Module Acceptance Test Plan (AT-01 – AT-14)
|
||
|
||
These 14 end-to-end integration test scenarios must be verified before production readiness:
|
||
|
||
1. **AT-01 (Award to Contract)**: Valid award creates award record, rejects duplicate reference numbers, and auto-initiates Contract workflow. Attempts to disburse against an award without an executed contract are blocked.
|
||
2. **AT-02 (Contract Execution)**: Executing a contract with all signatures locks it as authoritative and activates downstream budget and milestone modules. Missing signatures prevent transition to `EXECUTED`.
|
||
3. **AT-03 (Onboarding Gate)**: Attempting to process a first disbursement when recipient banking details are unverified blocks the payment until onboarding is complete.
|
||
4. **AT-04 (Budget Control)**: Attempting to commit expenditure that exceeds the approved budget line is blocked until an approved budget revision exists.
|
||
5. **AT-05 (Milestone-Linked Disbursement)**: Payment requests are only eligible for approval when the linked milestone is in `VERIFIED` status. Duplicate approvals on the same tranche are blocked.
|
||
6. **AT-06 (Evidence & Indicators)**: Submitting an indicator actual value without required supporting evidence is blocked. Recording actual values automatically computes variance and RAG status.
|
||
7. **AT-07 (Risk Escalation)**: A High-rated risk with an overdue mitigation date is automatically escalated to the Risk Dashboard and PM alert queue.
|
||
8. **AT-08 (Compliance Tracking)**: A compliance obligation passing its due date without submission automatically marks overdue and triggers escalation.
|
||
9. **AT-09 (Change Control)**: A budget reallocation exceeding thresholds updates the approved baseline only after formal approval, preserving original baselines.
|
||
10. **AT-10 (Close-Out & Handover)**: Project closure is blocked if unresolved risks or missing reports remain. Upon closure confirmation, the project is enrolled in Post-Fund Monitoring (6/12/24/36/60-month schedule).
|
||
11. **AT-11 (Portfolio Roll-Up)**: Indicator actuals verified at project level immediately aggregate into programme and portfolio totals.
|
||
12. **AT-12 (Audit Trail Integrity)**: Any approval, payment, or budget edit creates an immutable, user-attributed audit entry that cannot be altered even by admins.
|
||
13. **AT-13 (Role-Based Access)**: Users without payment-approval roles attempting to execute an approval are denied and the attempt is logged.
|
||
14. **AT-14 (Multi-Tenancy Isolation)**: Users querying awards, projects, or budgets only receive data matching their authenticated `TenantId`.
|
||
|
||
---
|
||
|
||
## 8. Current Implementation Status & Gap Analysis
|
||
|
||
### 8.1 Completed Components (Existing in Codebase)
|
||
* **Domain Layer**:
|
||
* Core entities defined: `Award`, `Contract`, `Project`, `Programme`, `Portfolio`, `Budget`, `Milestone`, `Activity`, `Indicator`, `Outcome`, `Beneficiary`, `Disbursement`, `Risk`, `Issue`, `ComplianceItem`, `SiteVisit`, `ChangeRequest`, `Evidence`, `AuditLog`, `Tenant`, `User`, `Rule`, `Instrument`.
|
||
* Enums for sectors, project types, evidence types, and notification channels.
|
||
* **Infrastructure Layer**:
|
||
* PostgreSQL EF Core DbContext with model configurations and initial migrations.
|
||
* AWS S3 SDK integration for document uploads.
|
||
* **API Endpoints**:
|
||
* Endpoints grouped by slices: Grants (`CreateAward`, `CreateContract`, `ExecuteContract`, `CreateBudget`), Projects (`CreateProject`, `CreateMilestone`, `CreateActivity`, `UpdateProjectStatus`), Execution (`CreateDisbursement`, `ApproveDisbursement`, `CreateRisk`), Evidence (`CreateEvidence`, `CreateIndicator`), Governance (`CreateTenant`, `CreateOrganisation`).
|
||
* **UI Skeletons**:
|
||
* `ConsoleUI`: Routing and layout for Dashboard, Projects, Grants, Beneficiaries, Governance, Monitoring, Settings, SiteVisits.
|
||
* `PartnerUI`: Grantees landing, basic project card display, and evidence upload modal shell.
|
||
|
||
### 8.2 Remaining Work & Implementation Priorities
|
||
|
||
To bring the project to full compliance with SRS v2.0, the following implementation tasks are prioritized:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ RELEASE 1: MVP CORE THREAD (Priority 1) │
|
||
├─────────────────────────────────────────────────────────────────────────────┤
|
||
│ 1. Complete M01 Award Intake validation (duplicate ref check, auto-contract) │
|
||
│ 2. Enforce M02 Contract locks & signature verification gates │
|
||
│ 3. Build M03 Recipient Onboarding gate blocking 1st disbursement (AT-03) │
|
||
│ 4. Implement M05 Budget overrun prevention (AT-04) & formula calculations │
|
||
│ 5. Implement M06 Disbursement conditions precedent & milestone gating(AT-05)│
|
||
│ 6. Complete M07/M14 Milestone evidence review queue & verification logic │
|
||
│ 7. Complete M08 Indicator RAG threshold engine (Green/Amber/Red) (AT-06) │
|
||
│ 8. Connect M20/M22 Executive Portfolio Dashboard D01 with live KPIs │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ RELEASE 2: GOVERNANCE & CONTROL (Priority 2) │
|
||
├─────────────────────────────────────────────────────────────────────────────┤
|
||
│ 1. M09 Risk likelihood/impact matrix, residual risk, auto-escalation (AT-07)│
|
||
│ 2. M10 Compliance tracking & automated due date escalation (AT-08) │
|
||
│ 3. M15 Change request lifecycle & baseline history preservation (AT-09) │
|
||
│ 4. M16 Operational issue tracker with PM commentary requests │
|
||
│ 5. M17 Mobile/offline site visit inspection checklists (NFR-010) │
|
||
│ 6. M18 Formal project close-out checklist gate (AT-10) │
|
||
│ 7. M19 Post-Fund Monitoring schedule generator (6/12/24/36/60 mo intervals) │
|
||
│ 8. M25 Audit log viewer with end-to-end evidence traceability (AT-12) │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ RELEASE 3: DIFFERENTIATORS & SPECIALIZED DOMAINS (Priority 3) │
|
||
├─────────────────────────────────────────────────────────────────────────────┤
|
||
│ 1. M12 Innovation Passport & TRL (1–9) tracker (prototypes, patents) │
|
||
│ 2. M13 Beneficiary outcomes with POPIA-compliant personal data masking │
|
||
│ 3. M11 ESG indicators and post-funding sustainability scores │
|
||
│ 4. M21 Knowledge repository, failure categorization & case studies │
|
||
│ 5. M23 Dynamic funding instrument configurator (Grant, DFI, Blended, NSI) │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Developer Guidelines & Coding Conventions
|
||
|
||
When adding or modifying code in this codebase, strictly follow these standards:
|
||
|
||
1. **Architecture & CQRS**:
|
||
* API endpoints in `PostFundManagement.Api/Endpoints/` must remain lightweight. They parse HTTP requests, dispatch commands/queries via `IMediator`, and map `Result<T>` from `FluentResults` into appropriate HTTP responses (`Results.Ok`, `Results.BadRequest`, `Results.Problem`).
|
||
* Business logic belongs in `PostFundManagement.Application` command and query handlers.
|
||
* Entities in `PostFundManagement.Domain` protect their invariants. Avoid public setters where domain methods encapsulate state transitions (e.g. `contract.Execute(...)`, `disbursement.Approve(...)`).
|
||
2. **Multi-Tenancy & Data Isolation**:
|
||
* Every query must respect the ambient `TenantId`. Never execute queries or mutations without verifying tenant boundaries (`NFR-007`, `AT-14`).
|
||
3. **Auditability (BR-007, AT-12)**:
|
||
* Any change modifying financial amounts, approvals, contractual states, risk ratings, or project statuses must generate an `AuditLog` entry detailing `UserId`, `Timestamp`, `Action`, `PreviousValue`, and `NewValue`.
|
||
4. **Data Protection (POPIA)**:
|
||
* Recipient banking details (`AccountNumber`, `BranchCode`) and beneficiary personal information must be encrypted or masked at rest and restricted via field-level authorization.
|
||
5. **Frontend State & API Sync**:
|
||
* Use TanStack Query hooks for server caching, optimistic updates, and cache invalidation.
|
||
* Forms must use `react-hook-form` with `zod` schemas enforcing mandatory fields at input time.
|
||
6. **Frontend Package Management (Yarn Mandatory)**:
|
||
* **Always use `yarn` instead of `npm`** across all frontend projects (`PostFundManagement.ConsoleUI` and `PostFundManagement.PartnerUI`).
|
||
* Never run `npm install`, `npm run`, or create/modify `package-lock.json`.
|
||
* Standard commands:
|
||
* `yarn install` or `yarn` to install dependencies.
|
||
* `yarn add <pkg>` / `yarn add -D <pkg>` to add dependencies.
|
||
* `yarn build` to trigger production builds.
|
||
* `yarn dev` to launch local development servers.
|
||
* `yarn test` for executing frontend tests.
|
||
|
||
---
|
||
|
||
## 10. Role and Claim Architecture (RBAC & Permissions)
|
||
|
||
The platform implements a fine-grained, claim-based authorization model across both the backend APIs and frontend web applications. This model is partially implemented in `PostFundManagement.ConsoleUI/src/auth/rbac.ts` and governs screen access, action buttons, status changes, and endpoint execution.
|
||
|
||
### 10.1 Role Claims Inventory
|
||
|
||
The system defines 15 distinct role claims corresponding to operational domains and feature slices:
|
||
|
||
| Claim Key | Label | Scope & Description |
|
||
| :--- | :--- | :--- |
|
||
| `dashboard` | Executive Dashboard | Strategic macro metrics, portfolio KPIs, and ISO 31000 health views |
|
||
| `governance` | Governance & Structure | Holding entities, investment portfolios, programmes, and statutory frameworks |
|
||
| `projects_milestones` | Projects & Milestones | Work breakdown structure (WBS), scheduled milestones, activities, and progress |
|
||
| `grant_financials` | Grant & Financials | Funding awards, budget line allocations, tranches, and disbursements |
|
||
| `evidence_risks` | M&E, Evidence & Risks | Evidence verification queue, indicator actuals, and ISO 31000 risk registers |
|
||
| `beneficiaries` | Beneficiaries | Target demographics, POPIA compliance tracking, and direct/indirect outcomes |
|
||
| `change_management` | Change Management | Contractual change requests, budget reallocations, timeline variations, and issues |
|
||
| `site_visits` | Site Visits | Physical field inspection reports, checklist findings, and geotagged evidence |
|
||
| `settings_invites` | Settings Invites | Onboarding invitations, applicant verification links, and notifications |
|
||
| `settings_users` | Settings Users | User account provisioning, role assignments, claims, and lockout management |
|
||
| `settings_auditlog` | Settings Audit Log | Immutable system-wide activity ledger and audit trail entries |
|
||
| `settings_notifications` | Settings Notifications | SMTP engines, notification dispatch logs, and delivery channels |
|
||
| `tenant_self_service` | Tenant Self-Service | Tenant-level profile parameters, local branding, and sub-user management |
|
||
| `tenant_management` | Tenant Management | System-level multi-tenant directory, partition allocation, and cross-tenant isolation |
|
||
| `unrestricted` | Unrestricted Access | System-wide root administration; bypasses granular claim checks |
|
||
|
||
### 10.2 Claim Action Values & Permitted Capabilities
|
||
|
||
Each claim assigned to a user or role can be granted specific permission levels. The permitted action assignments are:
|
||
|
||
* **`view`**: Can view records based on role (read-only access to entities, grids, and details).
|
||
* **`add`**: Can add/create records or submit feedback; can change workflow statuses *other than approve*. (Includes `view` capability).
|
||
* **`update`**: Can use **approve** status changes in dropdowns and action bars. (Includes `view` and `add` capabilities).
|
||
* **`export`**: Can click export buttons (e.g. Spreadsheet/Excel `.xlsx`, PDF reports). (Includes `view` capability).
|
||
* **`all`**: Can perform all operations (`view`, `add`, `update`, `export`). Full capability within the claim domain.
|
||
|
||
### 10.3 Default Role Claim Map
|
||
|
||
The following baseline mappings define the claims assigned to system roles:
|
||
|
||
| Role | Assigned Claims | Permitted Scope Summary |
|
||
| :--- | :--- | :--- |
|
||
| **`Admin`** | `unrestricted` | Full system-wide control across all modules, configuration, and tenant partitions. |
|
||
| **`Executive`** | `dashboard`, `governance`, `projects_milestones`, `grant_financials`, `evidence_risks`, `tenant_self_service` | Strategic oversight, macro KPIs, portfolio health, financials, and high-level project monitoring. |
|
||
| **`Supervisor`** | `dashboard`, `projects_milestones`, `grant_financials`, `evidence_risks`, `beneficiaries`, `change_management`, `site_visits`, `settings_invites`, `settings_users` | Operational review, verification of milestones/evidence, site inspections, user/invite administration, and change review. |
|
||
| **`Manager`** | `dashboard`, `projects_milestones`, `grant_financials`, `evidence_risks`, `beneficiaries`, `change_management`, `site_visits`, `settings_invites`, `settings_users`, `settings_auditlog`, `settings_notifications`, `tenant_self_service` | Comprehensive operational management, including audit logs, notifications configuration, self-service tenancy, and full project/grant controls. |
|
||
| **`Contributor`** | `projects_milestones`, `grant_financials`, `evidence_risks`, `beneficiaries`, `change_management`, `site_visits`, `settings_invites` | Working-level execution: creating activities, submitting milestone evidence, capturing risks, drafting change requests, and logging site observations. |
|
||
|
||
### 10.4 Restriction Invariant & Enforcement Rules
|
||
|
||
* **Explicit Restriction Rule**: **If a role claim is not present, the user is fully restricted from performing any actions associated with that claim.**
|
||
* **Partner Role Isolation**: The external `partner` (grantee/recipient) role is strictly confined to `PostFundManagement.PartnerUI`. Any attempt by a `partner` user to authenticate or access `PostFundManagement.ConsoleUI` routes is completely blocked.
|
||
* **Granular Action Checking in UI**:
|
||
* Pages and routes are protected via `<ClaimProtectedRoute claim="..." action="..." />`.
|
||
* Components query capabilities using `const { canView, canAdd, canApprove, canExport } = useRbac()`.
|
||
* Approval buttons and status transitions requiring `update` are disabled or hidden if the user's claim assignment does not permit `update` or `all`.
|
||
* Export buttons (`.xlsx`, `.pdf`) are disabled or hidden if the user's claim assignment does not permit `export` or `all`.
|
||
* **Backend API Enforcement**:
|
||
* Endpoints inspect JWT claims (`role_claim:<claim_name>` or array claims) to enforce authorization before executing command/query handlers.
|
||
* Violations immediately return `403 Forbidden` and log an audit security event (`NFR-004`, `AT-13`).
|
||
|
||
### 10.5 Maker-Checker Segregation of Duties Pattern
|
||
|
||
In addition to RBAC role and claim constraints, the platform enforces the **Maker-Checker Pattern** across all operational lifecycle modules:
|
||
|
||
* **Separation of Duty Rule**: **The user who creates or drafts a record (the "Maker") is strictly barred from verifying or approving that record (the "Checker"), regardless of their role or administrative privileges.**
|
||
* **Attribution**: Every domain entity immutably captures the creator's user identifier in `createdBy` (Guid/Subject ID resolved from `context.GetUserId()`).
|
||
* **Evaluation Formula**:
|
||
$$\text{Can Approve} = \text{Has 'update' claim permission} \land (\text{Authenticated User ID} \neq \text{Record createdBy})$$
|
||
* **Client UI Enforcement**:
|
||
* Components use `const { canApproveRecord, isMaker, getApprovalRestrictionReason } = useRbac()`.
|
||
* Status transition dropdowns disable the `Approved` / `Active` option when `!canApproveRecord(claim, record.createdBy)` with contextual indicator `(Maker-Checker: Cannot approve own record)`.
|
||
* Fast-track approval buttons (budget lines, disbursement tranches, evidence submissions, change requests) are hidden or marked with a Maker-Checker warning badge when inspected by the creator.
|
||
|
||
---
|
||
|
||
## 11. Functional API Surface & Endpoint Catalog
|
||
|
||
The backend exposes a fully functional RESTful API hosted at `https://api.ifri.khongisa.co.za/` (OpenAPI v1 specification available at `/openapi/v1.json`). All resource routes follow the versioned prefix `/v1/api/`.
|
||
|
||
### 11.1 Governance Endpoints (24 Endpoints)
|
||
Governs institutional hierarchies, multi-tenant boundaries, investment portfolios, statutory programmes, and portfolio dashboard metrics.
|
||
|
||
* **Executive Dashboard & Valuation**:
|
||
* `GET /v1/api/governance/dashboard` (`dashboard`): Returns executive macro metrics, ISO 31000 portfolio health status, and top-level KPI aggregations.
|
||
* `GET /v1/api/governance/portfolios/{portfolioId}/value` (`getporfoliovalue`): Aggregates total approved budget and disbursed values across all projects in a portfolio.
|
||
* `GET /v1/api/governance/programmes/{programmeId}/value` (`getprogrammevalue`): Calculates total project budget values under a specific programme.
|
||
* **Funding Instruments (BR-006)**:
|
||
* `POST /v1/api/governance/instruments` (`createinstrument`): Registers a new funding instrument (Grant, DFI, Impact Investment, Blended Finance, NGO, Innovation).
|
||
* `PUT /v1/api/governance/instruments` (`updateinstrument`): Modifies instrument parameters and rules.
|
||
* `GET /v1/api/governance/instruments/{programmeId}` (`getinstruments`): Lists all funding instruments configured for a programme.
|
||
* **Organisations & Recipient Master Data**:
|
||
* `POST /v1/api/governance/organisations` (`createorganisation`): Creates a new recipient or partner organisation profile.
|
||
* `GET /v1/api/governance/organisations` (`getorganisations`): Paginated directory of registered organisations.
|
||
* `GET /v1/api/governance/organisations/{organisationId}` (`getorganisation`): Retrieves organisation details, verification state, and banking references.
|
||
* `PUT /v1/api/governance/organisations` (`updateorganisation`): Updates organisation master data.
|
||
* **Portfolios & Programmes**:
|
||
* `POST /v1/api/governance/organisations/portfolio` (`createportfolio`): Creates an investment portfolio linked to an organisation.
|
||
* `GET /v1/api/governance/organisations/{organisationId}/portfolio` (`getorganisationportfolios`): Lists portfolios for an organisation.
|
||
* `POST /v1/api/governance/organisations/portfolios` (`getportfolios`): Retrieves all active investment portfolios.
|
||
* `GET /v1/api/governance/portfolios/{portfolioId}` (`getportfolio`): Retrieves portfolio details and linked programmes.
|
||
* `PUT /v1/api/governance/organisations/portfolio` (`updateportfolio`): Updates portfolio properties.
|
||
* `GET /v1/api/governance/portfolios/{portfolioId}/programmes` (`getportfolioprogrammes`): Retrieves programmes belonging to a portfolio.
|
||
* `GET /v1/api/governance/programmes/{portfolioId}` (`getprogrammes`): Lists all programmes linked to a portfolio.
|
||
* **Multi-Tenancy Partitioning (NFR-007, AT-14)**:
|
||
* `POST /v1/api/governance/tenants` (`createtenant`): Registers a new system tenant (e.g. government department, DFI).
|
||
* `GET /v1/api/governance/tenants` (`gettenantsretrieval`): Lists all system tenants.
|
||
* `GET /v1/api/governance/tenants/{tenantId}` (`gettenants`): Retrieves a single tenant profile.
|
||
* `PUT /v1/api/governance/tenants` (`updatetenant`): Modifies tenant parameters and subscription status.
|
||
* `GET /v1/api/governance/tenants/{tenantIdHash}/organisations` (`gettenantorganisationsretrieval`): Returns all organisations owned and isolated within a specific tenant partition.
|
||
* **Budgets**:
|
||
* `PUT /v1/api/grants/budgets` (`updatebudget`): Updates budget allocations and approvals.
|
||
|
||
### 11.2 Grants, Awards & Contracting Endpoints (14 Endpoints)
|
||
Enforces the mandatory controlled lifecycle starting at approved award (BR-001, BR-002).
|
||
|
||
* **Award Intake (M01)**:
|
||
* `POST /v1/api/grants/awards` (`createaward`): Registers an approved funding award with external reference, amount, period, recipient, and objectives. Automatically triggers initial draft contract creation (BR-002, AT-01).
|
||
* `GET /v1/api/grants/awards` (`getawards`): Lists all registered awards.
|
||
* `GET /v1/api/grants/awards/{awardId}` (`getaward`): Retrieves detailed award metadata and downstream project links.
|
||
* **Budget Management (M05)**:
|
||
* `POST /v1/api/grants/budgets` (`createbudget`): Allocates approved budget lines against an award.
|
||
* `GET /v1/api/grants/budgets` (`getbudgets`): Lists all project and award budgets.
|
||
* `GET /v1/api/grants/budgets/{budgetId}` (`getbudget`): Retrieves specific budget line allocations, commitments, and actual expenditures.
|
||
* **Contract & Agreement Governance (M02)**:
|
||
* `POST /v1/api/grants/contracts` (`createcontract`): Creates a contract record with configurable clauses and obligations.
|
||
* `GET /v1/api/grants/contracts` (`getcontracts`): Lists contracts across the portfolio.
|
||
* `GET /v1/api/grants/contracts/{contractId}` (`getcontract`): Retrieves contract metadata, versioning, and execution status.
|
||
* `PUT /v1/api/grants/contracts` (`updatecontract`): Updates draft contract terms prior to execution.
|
||
* `POST /v1/api/grants/contracts/{contractId}` (`uploadcontract`): Securely uploads signed agreement artifacts to S3 object storage.
|
||
* `GET /v1/api/grants/contracts/{contractId}/securelink` (`getcontracturl`): Generates a time-limited pre-signed download URL (default 15 minutes) for the executed contract document.
|
||
* `POST /v1/api/grants/contracts/execute` (`executecontract`): Formally executes agreement with digital signatures, locking version 1.0 as the authoritative legal baseline (AT-02).
|
||
* **Evidence URLs**:
|
||
* `GET /v1/api/evidence/{evidenceId}/securelink` (`getevidenceurl`): Generates pre-signed temporary S3 download link for verified evidence files.
|
||
|
||
### 11.3 Projects & Operational Delivery Endpoints (43 Endpoints)
|
||
Controls the work breakdown structure (WBS), milestones, field monitoring, and project governance (M04, M07, M10, M13, M15, M16, M17).
|
||
|
||
* **Project Management (M04)**:
|
||
* `POST /v1/api/projects` (`createproject`): Creates a new project under an approved award and programme.
|
||
* `GET /v1/api/projects` (`getprojects`): Lists all active and historical projects.
|
||
* `GET /v1/api/projects/{projectId}` (`getproject`): Detailed view of project WBS, timeline, status, and financials.
|
||
* `PUT /v1/api/projects` (`updateproject`): Updates project details.
|
||
* `PUT /v1/api/projects/status` (`updateprojectstatus`): Updates project status across lifecycle states (`ACTIVE`, `AT RISK`, `COMPLETED`, `CLOSED`).
|
||
* `POST /v1/api/projects/programme` (`createprogramme`), `PUT /v1/api/projects/programme` (`updateprogramme`), `GET /v1/api/projects/programme/{programmeId}` (`getprogramme`), `GET /v1/api/projects/programme/{page}/{pageSize}` (`getprogrammesprojects`), `GET /v1/api/projects/{programmeId}/programme` (`getprogrammeprojects`).
|
||
* **Activities & WBS (M04)**:
|
||
* `POST /v1/api/projects/activities` (`createactivity`): Adds an activity linked to a project milestone.
|
||
* `GET /v1/api/projects/activities/{activityId}` (`getactivity`): Retrieves specific activity details.
|
||
* `GET /v1/api/projects/{projectId}/activities` (`getactivities`): Lists all WBS activities for a project.
|
||
* **Milestone & Deliverables (M07)**:
|
||
* `GET /v1/api/projects/{projectId}/milestones` (`getprojectmilestones`): Lists contractual milestones for a project.
|
||
* `GET /v1/api/projects/milestones/{milestoneId}` (`getmilestone`): Retrieves milestone requirements, due dates, and evidence attachments.
|
||
* `GET /v1/api/projects/milestones/{page}/{pageSize}` (`getmilestones`): Paginated milestone repository.
|
||
* `PUT /v1/api/projects/milestone` (`updatemilestone`): Updates milestone dates, statuses, and verification decisions.
|
||
* **Beneficiaries & Social Outcomes (M13)**:
|
||
* `POST /v1/api/projects/beneficiaries` (`createbeneficiary`): Registers beneficiary group or target demographic.
|
||
* `GET /v1/api/projects/beneficiaries/{beneficiaryId}` (`getbeneficiaries`): Retrieves beneficiary details.
|
||
* `GET /v1/api/projects/{projectId}/beneficiaries` (`getbeneficiariesretrieval`): Lists beneficiaries for a project.
|
||
* `PUT /v1/api/projects/beneficiaries` (`updatebeneficiary`): Updates beneficiary records.
|
||
* `POST /v1/api/projects/beneficiaris/outcomes` (`createoutcome`): Links an achieved outcome to a beneficiary group.
|
||
* `GET /v1/api/projects/outcomes/{outcomeId}` (`getoutcome`): Retrieves outcome details.
|
||
* `GET /v1/api/projects/outcomes/{beneficiatyId}` (`getoutcomes`): Retrieves outcomes for a beneficiary.
|
||
* `PUT /v1/api/projects/outcomes` (`updateoutcome`): Updates outcome status.
|
||
* **Compliance Management (M10)**:
|
||
* `POST /v1/api/projects/complianceitems` (`createcomplianceitem`): Logs a statutory, financial, or contractual compliance obligation.
|
||
* `GET /v1/api/projects/complianceitems/{complianceItemId}` (`getcomplianceitem`): Retrieves compliance item status.
|
||
* `GET /v1/api/projects/complianceitems/{projectId}` (`getcomplianceitems`), `GET /v1/api/projects/{projectId}/complianceitems`: Lists project compliance tasks.
|
||
* `PUT /v1/api/projects/complianceitems` (`updatecomplianceitem`): Updates compliance satisfaction and attached evidence.
|
||
* **Risk & Issue Governance (M09, M16)**:
|
||
* `GET /v1/api/projects/{projectId}/risks` (`getprojectrisks`): Lists risks registered against a project.
|
||
* `POST /v1/api/projects/issues` (`createissue`): Logs an operational issue or escalation blocker.
|
||
* `GET /v1/api/projects/issues/{issueId}` (`getissue`): Retrieves issue details and resolution history.
|
||
* `GET /v1/api/projects/{projectId}/issues` (`getissues`): Lists all issues for a project.
|
||
* `PUT /v1/api/projects/issues` (`updateissue`): Updates issue severity, resolution, or escalation owner.
|
||
* **Change Control & Variations (M15, AT-09)**:
|
||
* `POST /v1/api/projects/changerequests` (`createchangerequest`): Submits a formal change request (scope, budget, timeline).
|
||
* `GET /v1/api/projects/changerequests/{changeRequestId}` (`getchangerequest`): Retrieves change impact evaluation.
|
||
* `GET /v1/api/projects/{projectId}/changerequests` (`getchangerequests`): Lists project change requests.
|
||
* `PUT /v1/api/projects/changerequests` (`updatechangerequest`): Records review, approval, or rejection of change request.
|
||
* **Site Visits & Field Inspections (M17)**:
|
||
* `POST /v1/api/projects/sitevisits` (`createsitevisit`): Records scheduled or completed field monitoring visit.
|
||
* `GET /v1/api/projects/sitevisits/{siteVisitId}` (`getsitevisit`): Retrieves site visit checklist, inspector observations, and findings.
|
||
* `GET /v1/api/projects/{projectId}/sitevisits` (`getsitevisits`): Lists site visits conducted on a project.
|
||
* `PUT /v1/api/projects/sitevisits` (`updatesitevisit`): Updates inspection findings and corrective actions.
|
||
* **Indicators Linkage (M08)**:
|
||
* `GET /v1/api/projects/indicators/project/{projectId}` (`getprojectindicators`): Lists all M&E indicators tracked for a project.
|
||
|
||
### 11.4 Evidence & M&E Endpoints (11 Endpoints)
|
||
Handles evidence verification and quantitative results measurement (M08, M14).
|
||
|
||
* **Supporting Evidence Repository (M14)**:
|
||
* `PUT /v1/api/evidence/file` (`createevidence`): Multipart/stream file upload directly to S3 storage, registering SHA-256 hashes, MIME types, and entity linkages.
|
||
* `PUT /v1/api/evidence` (`updateevidence`): Updates evidence metadata, verification status, and review notes.
|
||
* `GET /v1/api/evidence/{evidenceId}` (`getevidence`): Retrieves evidence record details and download references.
|
||
* `GET /v1/api/evidence/files/{page}/{pageSize}` (`getevidencefiles`): Paged catalogue of all evidence assets across the system.
|
||
* `GET /v1/api/evidence/milestone/{milestoneId}` (`getmilestoneevidence`): Retrieves all supporting files attached to a specific milestone.
|
||
* `GET /v1/api/evidence/{projectId}` (`getprojectevidence`): Retrieves all evidence files registered for a project.
|
||
* **M&E Indicators (M08)**:
|
||
* `POST /v1/api/evidence/indicators` (`createindicator`): Defines an indicator with baseline, target, unit of measure, and results level.
|
||
* `GET /v1/api/evidence/indicators/{page}/{pageSize}` (`getindicators`): Lists all indicators across programmes.
|
||
* `GET /v1/api/evidence/indicators/{indicatorId}` (`getindicator`): Detailed view of an indicator's history.
|
||
* `POST /v1/api/evidence/indicators/{indicatorId}/actuals/{actualAmount}` (`recordindicatoramount`): Records verified actual results; automatically evaluates variance and RAG status (Green $\ge 90\%$, Amber $70\text{--}89\%$, Red $< 70\%$).
|
||
* **Milestone Creation**:
|
||
* `POST /v1/api/evidence/milestones` (`createmilestonerequest`): Creates milestone requirements linked to deliverables and evidence rules.
|
||
|
||
### 11.5 Execution & Financial Control Endpoints (13 Endpoints)
|
||
Strict disbursement release controls and risk matrices (M06, M09).
|
||
|
||
* **Disbursement Tranche Releases (M06, BR-003, AT-05)**:
|
||
* `POST /v1/api/execution/disbursements` (`createdisbursement`): Creates a planned disbursement tranche linked to a milestone.
|
||
* `GET /v1/api/execution/disbursements` (`getdisbursements`): Lists all disbursement schedules across awards.
|
||
* `GET /v1/api/execution/disbursements/{disbursementId}` (`getdisbursement`): Retrieves tranche amount, bank details, and conditions precedent status.
|
||
* `PUT /v1/api/execution/disbursements` (`updatedisbursement`): Modifies payment schedule details.
|
||
* `POST /v1/api/execution/disbursements/{disbursementId}/approve` (`approvedisbursement`): Authorises tranche release. Strictly requires verified milestone evidence and completed onboarding; blocks duplicate payment approvals.
|
||
* **Risk Register (M09, AT-07)**:
|
||
* `POST /v1/api/execution/risks` (`createrisk`): Registers a project risk with likelihood, impact, and mitigation plan.
|
||
* `GET /v1/api/execution/risks` (`getrisks`): Lists risks across the portfolio.
|
||
* `GET /v1/api/execution/risks/{riskId}` (`getrisk`): Detailed risk profile including residual risk score.
|
||
* `PUT /v1/api/execution/risks` (`updaterisk`): Updates risk status, mitigation progress, or escalates high risks.
|
||
* **Programme Rules & Constraints (M04, M23)**:
|
||
* `POST /v1/api/execution/rules` (`createrule`): Configures an operational rule or threshold for a programme.
|
||
* `PUT /v1/api/execution/rules` (`updaterule`): Amends a rule with auto-incrementing version control.
|
||
* `GET /v1/api/execution/rules/{ruleId}` (`getrule`): Retrieves rule definition.
|
||
* `POST /v1/api/execution/programmes/rules` (`getrules`): Lists all active rules for a programme.
|
||
|
||
### 11.6 Communications & Notifications Endpoints (6 Endpoints)
|
||
* **Onboarding & Invitations (M03)**:
|
||
* `POST /v1/api/communications/invites` (`sendinvite`): Dispatches onboarding invitation via Email, SMS, or WhatsApp.
|
||
* `GET /v1/api/communications/invites/{page}/{pageSize}` (`getinvites`): Lists sent invitations.
|
||
* `GET /v1/api/communications/invites/{applicantUserId}` (`getapplicantinvites`): Lists invitations sent to an applicant.
|
||
* `GET /v1/api/communications/invites/applicants/{applicantUserId}` (`getapplicantinvite`): Retrieves single invitation details.
|
||
* `GET /v1/api/communications/applicants/{applicantUserId}/all` (`getiapplicantdata`): Aggregates all onboarding communications and status for an applicant.
|
||
* **Notification Logging**:
|
||
* `GET /v1/api/communications/notifications/{organisationId}` (`getnotifications`): Retrieves notification dispatch and delivery logs.
|
||
|
||
### 11.7 User Administration & Identity Endpoints (15 Endpoints)
|
||
Directly integrated with the hosted Skoruba Duende security system.
|
||
|
||
* **User Directory & Synchronization**:
|
||
* `POST /v1/api/users/{page}/{pageSize}` (`getusers`): Paged, searchable directory of system users with filtering by status and email.
|
||
* `GET /v1/api/users/{userId}` (`getuser`): Retrieves user metadata, assigned roles, and tenant associations.
|
||
* `GET /v1/api/users/{userId}/status/{status}` (`updateuserstatus`): Updates user activity status (Active, Inactive, Suspended).
|
||
* `POST /v1/api/integration/sync` (`syncuser`): Triggers background synchronization of users and organisations between STS and local DB.
|
||
* **User Provisioning & Session Control**:
|
||
* `POST /v1/api/users/{tenantId}/{idNumber}` (`createuser`): Provisions a new user account in both Skoruba IdentityServer and the local database.
|
||
* `PUT /v1/api/users/{userId}/session/lockout/{disable}` (`setuserlockout`): Immediately locks or unlocks user account access in the identity store.
|
||
* `PUT /v1/api/users/{userId}/session/logout` (`loguserout`): Invalidates active user security tokens and ends active sessions.
|
||
* **Role & Claim Assignments (RBAC)**:
|
||
* `GET /v1/api/users/{userId}/roles` (`getuserroles`): Retrieves roles assigned to a user.
|
||
* `PATCH /v1/api/users/{userId}/roles/{roleId}` (`adduserrole`): Assigns a system role to a user.
|
||
* `DELETE /v1/api/users/{userId}/roles/{roleId}` (`deleteuserrole`): Revokes a system role from a user.
|
||
* `GET /v1/api/users/system/roles` (`getsystemroles`): Discovers all roles configured in the security authority.
|
||
* `GET /v1/api/users/system/roles/{roleId}/claims` (`getroleclaims`): Discovers claims associated with a role.
|
||
* `GET /v1/api/users/{userId}/claims` (`getuserclaims`): Retrieves direct claims assigned to a user.
|
||
* `PATCH /v1/api/users/user/claims` (`adduserclaim`): Attaches an explicit claim value to a user.
|
||
* `DELETE /v1/api/users/{targetUserId}/claims/{claimId}` (`deleteuserclaim`): Removes a claim from a user.
|
||
|
||
---
|
||
|
||
## 12. Standardized API Response Handling & ProblemDetails Contract
|
||
|
||
### 12.1 ProblemDetails Error Protocol
|
||
When an endpoint experiences a validation failure, business rule violation, or internal error, it returns a standard RFC 7807 / ASP.NET Core `ProblemDetails` object:
|
||
|
||
```json
|
||
{
|
||
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
|
||
"title": "Error",
|
||
"status": 400,
|
||
"detail": "Approved funding amount must be greater than zero,Funding start date must precede end date"
|
||
}
|
||
```
|
||
|
||
### 12.2 Critical Client-Side Parsing Rule
|
||
In `PostFundManagement.Application/Extensions/General.cs`, error lists from `FluentResults` are joined into the `Detail` property using a comma separator:
|
||
```csharp
|
||
public static ProblemDetails ToProblems(this IReadOnlyList<IError> errors, int statusCode = StatusCodes.Status400BadRequest) => new()
|
||
{
|
||
Detail = string.Join(',', errors.Select(e => e.Message)),
|
||
Status = statusCode,
|
||
Title = "Error",
|
||
};
|
||
```
|
||
|
||
> [!IMPORTANT]
|
||
> **Crucial Rule for Frontends (`PartnerUI` and `ConsoleUI`)**:
|
||
> The `detail` property contains the **actual human-readable error messages**.
|
||
> Frontends must **never** display raw generic error titles. Instead, frontends must:
|
||
> 1. Inspect `error.response.data.detail`.
|
||
> 2. Split the string by `,` (comma) and trim whitespace from each item.
|
||
> 3. Display each item as an individual bullet point or toast message.
|
||
>
|
||
> This behavior is standardized in `PostFundManagement.PartnerUI/src/services/api/errorHelper.ts` (`extractApiErrors` and `formatApiErrorMessage`) and must be maintained across all form handlers.
|
||
|
||
---
|
||
|
||
## 13. Hosted Skoruba Duende Security System Integration
|
||
|
||
The platform relies on a dedicated, hosted **Skoruba Duende IdentityServer** deployment for authentication, machine-to-machine trust, and identity governance.
|
||
|
||
### 13.1 Infrastructure & Authority Configuration
|
||
* **STS Authority Endpoint**: `https://sts.security.khongisa.co.za`
|
||
* **Skoruba Administration API**: `https://api.security.khongisa.co.za/` (Connection string `SkorubaApi`)
|
||
* **API Audience**: `pfm` (configured under `SecuritySettings:Audience`)
|
||
|
||
### 13.2 API Security Architecture
|
||
1. **Interactive JWT Token Validation**:
|
||
* Configured via `AddApiSecurity` in `PostFundManagement.Application/Extensions/Security.cs`.
|
||
* Validates tokens against `https://sts.security.khongisa.co.za` using asymmetric key validation.
|
||
* `options.MapInboundClaims = false`: Preserves original claim types (`role`, `role_claim:*`, `sub`, `email`) without automatic legacy mapping.
|
||
* Enforces `Constants.MainScope` (`pfm` scope) on all protected endpoints.
|
||
2. **Machine-to-Machine Administration (Client Credentials)**:
|
||
* Configured via `AddSkorubaApiService` using `ClientCredentialsTokenHandler`.
|
||
* Automatically requests and caches client credentials tokens from `https://sts.security.khongisa.co.za/connect/token`.
|
||
* Communicates with `https://api.security.khongisa.co.za/` via Refit / typed HttpClient to manage users, roles, and claims programmatically.
|
||
* Outbound calls are guarded by Polly exponential retry policies (`options.Retry.MaxRetryAttempts = 3`).
|
||
3. **Session Revocation & Lockout**:
|
||
* `PUT /v1/api/users/{userId}/session/logout` and `/lockout/{disable}` invoke `ISkorubaUserService` to trigger immediate session invalidation in Duende STS, ensuring instant token revocation.
|
||
|
||
---
|
||
|
||
## 14. PartnerUI Completion Roadmap
|
||
|
||
The client has confirmed that the backend implementation conforms with the SRS v2.0 requirements. In order to complete **`PostFundManagement.PartnerUI`** (the external grantee/recipient portal), the following UI features must be finalized:
|
||
|
||
1. **Recipient Onboarding Flow (Module M03)**:
|
||
* Interactive wizard for grantee onboarding: organisation profile verification, key personnel registration, and banking details submission.
|
||
* Tax clearance and statutory document uploads with real-time status feedback.
|
||
2. **Milestone Evidence Submission Wizard (Modules M07 / M14)**:
|
||
* Interactive deliverable tracker matching wireframe Figure 10 in the SRS.
|
||
* File upload modal with pre-validation (allowed extensions: PDF, JPG, DOCX, XLSX; max size: 25MB).
|
||
* Reviewer status feedback display (Submitted $\to$ Under Review $\to$ Verified or Rework Required with reviewer commentary).
|
||
3. **Disbursement Tranche Drawdown Requests (Module M06)**:
|
||
* Partner drawdown dashboard displaying eligible, submitted, and disbursed funding tranches.
|
||
* Drawdown request form with automatic conditions precedent checklist verification.
|
||
4. **M&E Indicator Actuals Reporting (Module M08)**:
|
||
* Form enabling partners to report periodic actuals against contractual indicator targets.
|
||
* Mandatory evidence attachment validation prior to submitting actual metric values.
|
||
5. **Post-Fund Monitoring Reporting (Module M19)**:
|
||
* Longitudinal survey forms triggering at 6, 12, 24, 36, and 60 months post project close-out.
|
||
* Tracking enterprise survival, annual turnover, net jobs maintained, and ongoing commercialisation.
|
||
6. **Consistent Error & Notification Handling**:
|
||
* Integrate `extractApiErrors()` and `formatApiErrorMessage()` from `errorHelper.ts` across all PartnerUI mutation forms and modals.
|
||
|
||
---
|
||
|
||
## 15. Hashids Architecture & Frontend Conformance Insights
|
||
|
||
The platform uses **Hashids** to obfuscate internal sequential database primary keys (`int64` / `long` and `int32`), mitigating entity enumeration, sequential parameter tampering, and unauthorized data scraping.
|
||
|
||
### 15.1 Backend Hashids Topology & Error Behavior
|
||
|
||
* **Implementation**: Managed by `HashService` (`PostFundManagement.Application/Services/Shared/HashService.cs`) using the `Hashids.net` (v1.7.0) library.
|
||
* **Shared Configuration (`HasherSettings`)**:
|
||
* **Salt**: Sourced from configuration (`HasherSettings:Salt` or `HASHER_SALT`), defaulting to:
|
||
`"TGlnaHQgQ2hhcm1zLCBNaWRyYW5kQm9va3MgbWFrZSBsb3RzIG9mIG1vbmV5IGFuZCBhcmUgYSBzdWNjZXNzZnVsIHZpcmFsIHN0b3J5IGluIFNvdXRoIEFmcmljYQ=="`
|
||
* **Min Hash Length**: `HasherSettings:MinHashLength` or `HASHER_MIN_HASH_LENGTH` (default: `11`).
|
||
* **Core Service Methods**:
|
||
* `HashEncodeLongId(long id)` / `HashEncodeIntId(int id)`: Encodes integers into obfuscated HashID strings (e.g. `1` $\to$ `kY5aB9m...`).
|
||
* `DecodeLongIdHash(string hash)` / `DecodeIntIdHash(string hash)`: Decodes HashID strings back into database IDs.
|
||
* `HashEncodeHex(string hex)` / `DecodeHexHash(string hex)`: Encodes and decodes arbitrary alphanumeric strings (such as South African 13-digit National ID numbers and passport numbers) via hexadecimal conversions.
|
||
* **Security & Failure Contract (`IdDecodeError` $\to$ 403 Forbidden)**:
|
||
* When an endpoint receives an invalid, corrupted, or tampered HashID, `DecodeLongIdHash` or `DecodeHexHash` fails and returns an `IdDecodeError`.
|
||
* Across Minimal API endpoints (e.g. `GetProjectEndpoint`, `GetTenantOrganisationsEndpoint`, `CreateUserEndpoint`), `IdDecodeError` is mapped explicitly:
|
||
```csharp
|
||
var r when r.HasError<IdDecodeError>() => Results.Forbid()
|
||
```
|
||
* As a result, passing an unhashed raw numeric ID (e.g. `/v1/api/projects/1`) or an invalid hash immediately yields **HTTP 403 Forbidden**, preventing route scanning and guessing attacks.
|
||
|
||
### 15.2 Path Parameters vs. Request Body JSON Payloads (The Core Rule)
|
||
|
||
A critical distinction governs the communication between frontends and the API:
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────────────────────────────────┐
|
||
│ URL Path Parameters: HASHID STRINGS ONLY │
|
||
│ E.g. GET /v1/api/projects/{projectId} ──> expects "kY5aB9m..." (HashID) │
|
||
│ E.g. GET /v1/api/governance/tenants/{tenantIdHash}/organisations ──> expects HashID │
|
||
│ E.g. POST /v1/api/users/{tenantId}/{idNumber} ──> expects HashID + Hex HashID │
|
||
└────────────────────────────────────────────────────────────────────────────────────────┘
|
||
vs.
|
||
┌────────────────────────────────────────────────────────────────────────────────────────┐
|
||
│ Request Body JSON Payloads: NATIVE NUMERIC INTEGERS ONLY │
|
||
│ E.g. POST /v1/api/projects ──> { "programmeId": 12, "name": "..." } (int64 integer) │
|
||
│ E.g. PUT /v1/api/projects/status ──> { "projectId": 45, "status": 2 } (int64 integer) │
|
||
│ OpenAPI Schema: { "type": "integer", "format": "int64", "pattern": "^-?(0|[1-9]\\d*)$"} │
|
||
└────────────────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
> [!WARNING]
|
||
> **Payload Failure Trap**:
|
||
> If an alphanumeric HashID string is passed inside a JSON request body where an integer is expected, ASP.NET Core deserialization and OpenAPI validation will reject the request with **HTTP 400 Bad Request**.
|
||
> Conversely, if a raw integer is passed in a URL route parameter where a HashID is expected, `DecodeLongIdHash` will fail to decode, returning **HTTP 403 Forbidden**.
|
||
|
||
### 15.3 How ConsoleUI Successfully Conforms
|
||
|
||
`PostFundManagement.ConsoleUI` achieves seamless, defect-free conformance through an isolated, two-pronged abstraction layer:
|
||
|
||
#### 1. Instant Client-Side Deterministic Hasher (`src/utils/hashids.ts` & `src/services/toolsService.ts`)
|
||
* ConsoleUI bundles the JavaScript `hashids` library initialized with the exact same `salt` and `minHashLength` as the backend.
|
||
* All encoding and decoding occurs **synchronously and client-side with zero network round-trips**.
|
||
* `isHashId(str)` guards against accidental double-encoding if an identifier has already been hashed.
|
||
* Specialized encoders:
|
||
* `encodeEntityId(id)` / `toolsService.resolveProjectId(id)`: Encodes database numeric IDs for URL route parameters.
|
||
* `encodeIdNumberHex(idNumber)`: Encodes South African national ID numbers (13 digits), passports, and alphanumeric keys into hex-encoded Hashids for the `/api/users/{tenantId}/{idNumber}` provisioning endpoint.
|
||
|
||
#### 2. Request Body Sanitization via `toIntegerId()` in `src/services/api.ts`
|
||
* In `api.ts`, outgoing API calls systematically decouple route parameters from JSON payload bodies:
|
||
* **Route Parameters**: Routed via `resolveProjectId(projectId)` or `encodeEntityId(id)` to produce valid HashIDs.
|
||
* **Payload Bodies**: Routed via `toIntegerId(payload.id)` (`toolsService.decodeToNumberSync(id)`).
|
||
```typescript
|
||
export const toIntegerId = (id: number | string | null | undefined): number => {
|
||
return toolsService.decodeToNumberSync(id);
|
||
};
|
||
```
|
||
* `toIntegerId` detects whether the passed ID is a HashID, a numeric string, or a number, and safely decodes it into a canonical integer number before Axios serializes the JSON request body.
|
||
|
||
#### 3. Component Decoupling
|
||
* UI components (`ProjectsPage.tsx`, `GrantsPage.tsx`, `SettingsPage.tsx`, `CreateUserModal.tsx`) do not perform manual hash transformations or math.
|
||
* They simply pass identifiers into `api.ts` methods, which transparently route HashIDs to URLs and integer numbers to request bodies.
|
||
|
||
### 15.4 Conformance Guidelines for PartnerUI Completion
|
||
|
||
When implementing remaining features in `PostFundManagement.PartnerUI`, follow the proven ConsoleUI pattern:
|
||
1. **Route Parameters**: Use `hashParam(id)` (already established in `PartnerUI/src/services/api/endpoints.ts`) for all URL paths.
|
||
2. **Payload Bodies**: Always decode incoming IDs to integer numbers when constructing POST/PUT request bodies for Milestones, Evidences, Drawdown requests, and Indicator actuals.
|
||
3. **Hex ID Encoding**: When capturing user national IDs or identification numbers, apply `encodeIdNumberHex()` before invoking user endpoints.
|
||
4. **403 Forbidden Detection**: Treat HTTP 403 on parameterized GET routes as a potential `IdDecodeError` caused by an invalid or unhashed route parameter.
|
||
|
||
---
|
||
|
||
## 16. .NET Project Architecture, Coding Style & Interceptor-Based Governance
|
||
|
||
The backend is built with **.NET 10.0 (C# 13)** using Domain-Driven Design (DDD), Clean Architecture, and Aspect-Oriented Programming (AOP).
|
||
|
||
### 16.1 Layered Architecture & Project Responsibilities
|
||
|
||
```
|
||
PostFundManagement.Api
|
||
├── Endpoints/ # Minimal API slices implementing IEndpoint
|
||
├── Filters/ # Endpoint filters (ActiveUserFilter, UserRoleFilter)
|
||
└── Program.cs # Middleware pipeline, DI composition, OpenAPI transformer
|
||
│
|
||
▼ (References)
|
||
PostFundManagement.Application
|
||
├── Services/ # Domain services grouped by slice (Projects, Grants, etc.)
|
||
│ ├── [Slice]Service.cs # Actual business logic
|
||
│ └── [Slice]CacheService.cs # HybridCache decorator
|
||
├── Interceptors/ # Castle DynamicProxy AOP (LoggingInterceptor, AuditInterceptor)
|
||
├── Extensions/ # Service registration & DI wiring (Services.cs, Security.cs)
|
||
└── Abstractions/ # Service interfaces (IProjectService, IGrantsService)
|
||
│
|
||
├─────────────────────────────┐
|
||
▼ ▼
|
||
PostFundManagement.Infrastructure PostFundManagement.Domain
|
||
├── Database/ ├── Entities/ # Aggregate roots & entities
|
||
│ ├── ApplicationDbContext.cs ├── Enums/ # System taxonomies
|
||
│ └── Configurations/ ├── Errors/ # Domain errors (IError)
|
||
├── Security/ ├── Attributes/ # [Log], [Audit], [ApiVersionTarget]
|
||
└── Migrations/ └── Abstractions/ # IEntity, IAggregateRoot, IService
|
||
```
|
||
|
||
### 16.2 C# 13 & .NET 10 Coding Conventions
|
||
|
||
1. **Primary Constructors & Modern Types**:
|
||
* Services and endpoints use primary constructors:
|
||
```csharp
|
||
public sealed class ProjectService(
|
||
IDbContextFactory<ApplicationDbContext> contextFactory,
|
||
HashService hashService,
|
||
ITenantContextService tenantContext) : IProjectService
|
||
```
|
||
* Extensive use of expression-bodied members (`=>`) for concise method definitions.
|
||
* Nullable reference types (`<Nullable>enable</Nullable>`) strictly enforced.
|
||
2. **Functional Result Pattern (`FluentResults`)**:
|
||
* Domain and Application services **never throw exceptions for predictable business validation failures**.
|
||
* Methods return `Task<Result>` or `Task<Result<T>>`.
|
||
* Standard error objects in `PostFundManagement.Domain.Errors`:
|
||
* `ValidationError`: Mandatory field missing, format invalid, or constraint violation.
|
||
* `NotFoundError`: Entity ID not found in database.
|
||
* `UpdateError`: Mutation failed or produced zero affected rows.
|
||
* `CreateError`: Entity creation failed during persistence.
|
||
* `IdDecodeError`: HashID token corrupted, invalid salt, or tampering detected.
|
||
3. **Unified Exception-to-Result Wrapping**:
|
||
* Service methods wrap their implementation inside `Result.Try`:
|
||
```csharp
|
||
public async Task<Result<Project>> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||
await Result.Try((Func<Task<Result<Project>>>)(async () =>
|
||
{
|
||
// Execution logic...
|
||
}));
|
||
```
|
||
* Catches unhandled exceptions and converts them safely into `Result.Fail` without crashing the thread or leaking raw stack traces.
|
||
4. **Pattern-Matching Endpoint Handlers**:
|
||
* Minimal API endpoints map `Result<T>` directly to HTTP status codes via C# switch expressions:
|
||
```csharp
|
||
await service.UpdateProjectStatusAsync(...) switch
|
||
{
|
||
{ IsSuccess: true } => Results.Ok(),
|
||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
|
||
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
|
||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||
}
|
||
```
|
||
5. **High-Concurrency DbContext Management**:
|
||
* Services inject `IDbContextFactory<ApplicationDbContext>` rather than scoped `ApplicationDbContext`.
|
||
* Creates short-lived contexts using `await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);`.
|
||
* Avoids thread concurrency issues in parallel mediator requests and background Quartz jobs.
|
||
6. **Query & Mutation Optimizations**:
|
||
* Queries use `.AsNoTracking()` to eliminate EF Core tracking overhead.
|
||
* Targeted updates leverage `.ExecuteUpdateAsync(...)` for in-database bulk updates without loading full entities into memory.
|
||
7. **Tiered Hybrid Caching**:
|
||
* Implements `Microsoft.Extensions.Caching.Hybrid` alongside StackExchange Redis.
|
||
* Local L1 memory cache: 30-minute expiration.
|
||
* Distributed L2 Redis cache: 2-minute expiration (`InstanceName = "PFM-API_"`).
|
||
|
||
---
|
||
|
||
### 16.3 Logging Strategy Using `LoggingInterceptor` & `[Log]`
|
||
|
||
The application uses Aspect-Oriented Programming (AOP) via **Castle DynamicProxy** and `Castle.Core.AsyncInterceptor` to decouple logging from core domain logic.
|
||
|
||
#### How `LoggingInterceptor` Works
|
||
* **Source**: [`PostFundManagement.Application/Interceptors/LoggingInterceptor.cs`](file:///c:/Users/Admin/source/repos/PostFundManagement/PostFundManagement.Application/Interceptors/LoggingInterceptor.cs).
|
||
* **Trigger**: Decorated with `[Log]` attribute on service interface methods.
|
||
* **Execution Flow**:
|
||
1. Intercepts both synchronous (`InterceptSynchronous`) and asynchronous (`InterceptAsynchronous<TResult>`) calls.
|
||
2. Awaits the target method execution to retrieve the `resultObj`.
|
||
3. Checks if `resultObj is IResultBase result && result.IsFailed`.
|
||
4. If failed:
|
||
* Discovers target method information.
|
||
* Sanitizes and serializes invocation arguments via `BuildPayloadSummary(parameters, args)`:
|
||
* Form files are summarized: `[File] Name='...', Size=... bytes`.
|
||
* Streams are summarized: `[Stream] Length=... bytes`.
|
||
* Cancellation tokens are excluded.
|
||
* DTOs/Requests are serialized to JSON.
|
||
* Extracts error reasons and error type:
|
||
```csharp
|
||
var errorType = result.Reasons?.FirstOrDefault()?.GetType().Name.Replace("Error", string.Empty);
|
||
var errorsString = string.Join("; ", errorMessages);
|
||
```
|
||
* Emits a structured log via `ILogger`:
|
||
```csharp
|
||
logger.LogError("Method {MethodName} failed. Errors: [{ErrorType}] {Errors}. Parameters: {@Parameters}",
|
||
method.Name, errorType, errorsString, parameters);
|
||
```
|
||
5. The original `resultObj` is returned to the caller without alteration.
|
||
|
||
---
|
||
|
||
### 16.4 Audit Strategy Using `AuditInterceptor`, `[Audit]` & `AuditService`
|
||
|
||
The audit trail is a strict regulatory requirement (`BR-007`, `NFR-008`, `AT-12`). Rather than writing manual audit code in every handler, the platform automates audit logging via `AuditInterceptor`.
|
||
|
||
#### How `AuditInterceptor` Works
|
||
* **Source**: [`PostFundManagement.Application/Interceptors/AuditInterceptor.cs`](file:///c:/Users/Admin/source/repos/PostFundManagement/PostFundManagement.Application/Interceptors/AuditInterceptor.cs).
|
||
* **Trigger**: Decorated with `[Audit]` attribute on service interface methods.
|
||
* **Execution Flow**:
|
||
1. Intercepts asynchronous execution: `InterceptAsynchronous<TResult>(IInvocation invocation)`.
|
||
2. Inspects `resultObj`: Auditing executes **only when `result.IsSuccess` is true**.
|
||
3. Automatically resolves audit attributes from runtime reflection:
|
||
* **Action**: Strips `Async` suffix from method name (`CreateProjectAsync` $\to$ `CreateProject`).
|
||
* **EntityName**: Inferred from return value generic type (`Result<Project>` $\to$ `Project`) or request argument name (`CreateProjectRequest` $\to$ `Project`).
|
||
* **EntityId**: Inspects the returned object's `Value.Id` property or checks invocation arguments for an `Id`/`id` property or primitive key.
|
||
* **Changes**: Serializes parameters into a formatted payload summary (`BuildPayloadSummary`).
|
||
* **PerformedBy**: Extracts the authenticated user's `Guid` from `IHttpContextAccessor.HttpContext.User` claims (`sub` or `NameIdentifier`).
|
||
4. Asynchronously invokes `AuditService.LogActionAsync(...)`:
|
||
```csharp
|
||
await auditService.LogActionAsync(
|
||
entityId: entityId,
|
||
entityName: entityName,
|
||
action: action,
|
||
changes: changes,
|
||
performedBy: performedBy).ConfigureAwait(false);
|
||
```
|
||
5. Persists an immutable entry to `context.AuditLogs` with UTC timestamp and ambient `TenantId`.
|
||
6. **Resilience**: The entire audit block is wrapped in a non-throwing catch block so audit database write issues never fail a successful business transaction.
|
||
|
||
---
|
||
|
||
### 16.5 AOP Service Registration Pipeline
|
||
|
||
All application services are registered through a unified decorator-and-proxy composition in [`PostFundManagement.Application/Extensions/ServiceExtensionTools.cs`](file:///c:/Users/Admin/source/repos/PostFundManagement/PostFundManagement.Application/Extensions/ServiceExtensionTools.cs) and [`Services.cs`](file:///c:/Users/Admin/source/repos/PostFundManagement/PostFundManagement.Application/Extensions/Services.cs):
|
||
|
||
```csharp
|
||
services.AddLoggedAuditedCachedServiceScoped<IProjectService, ProjectService, ProjectCacheService>();
|
||
services.AddLoggedAuditedCachedServiceScoped<IGrantsService, GrantsService, GrantsCacheService>();
|
||
services.AddLoggedAuditedCachedServiceScoped<IExecutionService, ExecutionService, ExecutionCacheService>();
|
||
services.AddLoggedAuditedCachedServiceScoped<IEvidenceService, EvidenceService, EvidenceCacheService>();
|
||
services.AddLoggedAuditedCachedServiceScoped<IGovernanceService, GovernanceService, GovernanceCacheService>();
|
||
services.AddLoggedAuditedCachedServiceScoped<ICommunicationsService, CommunicationsService, CommunicationsCacheService>();
|
||
services.AddLoggedAuditedCachedServiceScoped<IUserService, UserService, UserCacheService>();
|
||
```
|
||
|
||
#### Execution Pipeline Order
|
||
|
||
```
|
||
Caller (e.g. Minimal API Endpoint)
|
||
│
|
||
▼
|
||
1. Castle DynamicProxy Interface Proxy
|
||
│
|
||
├─► [LoggingInterceptor]: Intercepts call; logs structured error if Result.IsFailed
|
||
│
|
||
├─► [AuditInterceptor]: Intercepts call; records immutable AuditLog if Result.IsSuccess
|
||
│
|
||
▼
|
||
2. [Slice]CacheService (Cache Decorator)
|
||
│ Checks HybridCache (L1 Memory / L2 Redis); returns cached response on read hits
|
||
│
|
||
▼ (Cache Miss / Mutation)
|
||
3. [Slice]Service (Actual Implementation)
|
||
│ Executes business invariants, EF Core queries, and state persistence
|
||
│
|
||
▼
|
||
Database (PostgreSQL / Npgsql)
|
||
```
|
||
|
||
This composition ensures:
|
||
* **Zero Boilerplate**: Domain services do not write logging or auditing code.
|
||
* **Separation of Concerns**: Caching, logging, auditing, and business rules remain in isolated, testable classes.
|
||
* **Guaranteed Auditability**: Any method tagged with `[Audit]` automatically satisfies `BR-007` and `AT-12`.
|
||
|
||
|
||
|
||
|
||
|