Refactor code structure for int in alll ids instesd defult using long

This commit is contained in:
2026-07-15 15:35:54 +05:30
parent 6c7f53350f
commit ed2ee87c68
120 changed files with 2775 additions and 12494 deletions
+6 -6
View File
@@ -50,7 +50,7 @@ HTTP ─► Controller ─► Service ─► Repository ─► UnitOfWork / ErpD
## A.4 Cross-cutting
- **Errors:** RFC 7807 `ProblemDetails` (framework default). Domain exceptions in `System/Errors` carry a stable `code`; a middleware maps them to `ProblemDetails`. Catalog in `11-BACKEND-PHASE1.md §7`.
- **Authentication:** ERPCore is a **resource server**. It validates JWTs issued by the **external AuthHex IdP** — algorithm **RS256** (asymmetric RSA), issuer `AuthHex`, audience `AuthHexClient`. AuthHex exposes **no JWKS/OIDC discovery**, so ERPCore is configured with AuthHex's **RSA public key statically** (rotation is a manual config update). Tokens live ~1000 min (prod) / 60 min (dev). A single **door authorization policy** requires an ERP `UserTypeCode`/`RoleCode` claim (AuthHex is a shared IdP, so a valid token alone is not enough); **per-endpoint RBAC stays deferred**.
- **Audit actor:** the token carries no `sub`/`nameid`; identity is AuthHex's custom **`UserId` (GUID)** claim. An `ICurrentUser` abstraction (`Infra/Auth`) resolves the acting user from a **local shadow user** — the GUID is mapped (JIT-provisioned) to a local `long` `users.user_id` that all FKs reference (see C.7). Services stamp mutations with it. **Never** trust a `createdBy` from the request body.
- **Audit actor:** the token carries no `sub`/`nameid`; identity is AuthHex's custom **`UserId` (GUID)** claim. An `ICurrentUser` abstraction (`Infra/Auth`) resolves the acting user from a **local shadow user** — the GUID is mapped (JIT-provisioned) to a local `int` `users.user_id` that all FKs reference (see C.7). Services stamp mutations with it. **Never** trust a `createdBy` from the request body.
- **Concurrency:** mutable resources carry a `RowVersion` (`[Timestamp] byte[]`), surfaced as `ETag`; `PUT`/`PATCH` require `If-Match``412` on mismatch.
- **Numbering:** document numbers come from `NumberSequence` (per doc type, per year), issued inside the same transaction as the document.
@@ -58,7 +58,7 @@ HTTP ─► Controller ─► Service ─► Repository ─► UnitOfWork / ErpD
- `ErpDbContext`: scoped (default).
- `IUnitOfWork`, repositories, services, `ICurrentUser`, `FifoCostingService`: **scoped**.
- Register in `Program.cs` (or an `AddApplication()` extension) after `AddDbContext`.
- **Auth wiring:** JWT bearer validation is built from AuthHex's **RSA public key** (config XML → `RsaSecurityKey`) with `ValidIssuer=AuthHex`, `ValidAudience=AuthHexClient`. A scoped **`IClaimsTransformation`** provisions/looks up the local shadow user (by `auth_user_id` = token `UserId` GUID) and injects the resolved local `long` id as a `ClaimTypes.NameIdentifier` (`nameid`) claim, so `ICurrentUser`/`AuditUserId` resolve a real user unchanged (falling back to the seeded system user only when unauthenticated).
- **Auth wiring:** JWT bearer validation is built from AuthHex's **RSA public key** (config XML → `RsaSecurityKey`) with `ValidIssuer=AuthHex`, `ValidAudience=AuthHexClient`. A scoped **`IClaimsTransformation`** provisions/looks up the local shadow user (by `auth_user_id` = token `UserId` GUID) and injects the resolved local `int` id as a `ClaimTypes.NameIdentifier` (`nameid`) claim, so `ICurrentUser`/`AuditUserId` resolve a real user unchanged (falling back to the seeded system user only when unauthenticated).
---
@@ -183,7 +183,7 @@ One base currency; invoicing/3-way match in Accounting (GRN carries data); users
### B.3.6 Cross-cutting (FR-X)
| ID | Requirement | Pri |
|---|---|---|
| FR-X-01 | **[Phase 1: external IdP, user identity only]** Authentication is **delegated to the external AuthHex IdP** (ERPCore validates its RS256 tokens; no local login). ERPCore **provisions a local shadow user** (`auth_user_id` GUID → local `long`) and **stamps every transaction with the acting user's identity** for audit. AuthHex now also supplies `RoleCode`/`UserTypeCode` claims, used only for the door gate; full per-endpoint RBAC (role→permission matrix) remains **deferred** — reserve role/permission structures for no-migration enablement. | M |
| FR-X-01 | **[Phase 1: external IdP, user identity only]** Authentication is **delegated to the external AuthHex IdP** (ERPCore validates its RS256 tokens; no local login). ERPCore **provisions a local shadow user** (`auth_user_id` GUID → local `int`) and **stamps every transaction with the acting user's identity** for audit. AuthHex now also supplies `RoleCode`/`UserTypeCode` claims, used only for the door gate; full per-endpoint RBAC (role→permission matrix) remains **deferred** — reserve role/permission structures for no-migration enablement. | M |
| FR-X-02 | Maintain an **immutable audit trail** for every create/update/delete and stock movement (who/when/old→new/reason). | M |
| FR-X-03 | Generate **document numbers** from configurable sequences (per type, per year), unique and gap-controlled. | M |
| FR-X-04 | Maintain configurable **reason-code** lists (adjustments, returns, count variances). | M |
@@ -242,7 +242,7 @@ Adjustment: Damage, Theft/Loss, Count Variance, Expiry Write-off, System Correct
| 7 | PO amendments | **Resolved:** Option B, edit-while-open |
| 8 | Costing method | **Resolved:** FIFO |
| 9 | Tenancy | **Resolved:** single-tenant |
| 10 | Authentication | **Resolved:** external **AuthHex** IdP (RS256; ERPCore validates only), **shadow-user** provisioning (`auth_user_id` GUID → local `long`), door-gated by ERP `UserType`/`Role`; per-endpoint RBAC deferred. *Open sub-item:* exact ERP `UserTypeCode`/`RoleCode` + RSA-key rotation process. |
| 10 | Authentication | **Resolved:** external **AuthHex** IdP (RS256; ERPCore validates only), **shadow-user** provisioning (`auth_user_id` GUID → local `int`), door-gated by ERP `UserType`/`Role`; per-endpoint RBAC deferred. *Open sub-item:* exact ERP `UserTypeCode`/`RoleCode` + RSA-key rotation process. |
---
@@ -322,7 +322,7 @@ STOCK_COUNT_LINE(count_line_id PK, count_id FK→STOCK_COUNT, item_id FK→ITEM,
## C.7 Cross-cutting
```
USER(user_id PK, username, display_name, status, auth_user_id [GUID, unique] → AuthHex identity) -- local shadow/projection of AuthHex users; user_id (long) is what all FKs reference
USER(user_id PK, username, display_name, status, auth_user_id [GUID, unique] → AuthHex identity) -- local shadow/projection of AuthHex users; user_id (int) is what all FKs reference
REASON_CODE(reason_code_id PK, code, description, context)
NUMBER_SEQUENCE(sequence_id PK, doc_type, year, last_number)
AUDIT_LOG(audit_id PK, user_id FK→USER, entity_type, entity_id, action, change_set, created_at)
@@ -342,7 +342,7 @@ ROLE_PERMISSION(role_id FK→ROLE, permission_id FK→PERMISSION)
- **Polymorphic source.** `STOCK_LEDGER.source_doc_type/source_doc_id` (and `AUDIT_LOG`, `JOURNAL_ENTRY_STUB`) reference the originating document without a hard FK per type — new transaction types (Sales, Manufacturing) write to the ledger without a schema change.
- **In-transit + cost-preserving transfer.** `STOCK_TRANSFER` holds `src`/`dest` warehouse; dispatch consumes source layers into in-transit, receive creates the destination layer at the **inherited** source cost.
- **FEFO ≠ FIFO.** FIFO governs *costing*; FEFO governs *physical picking* of perishables via `BATCH.expiry_date`.
- **External IdP + shadow user.** Authentication is delegated to **AuthHex** (RS256, issuer `AuthHex`/audience `AuthHexClient`, static public key). `USER` is a **local shadow** of AuthHex identities: `auth_user_id` (GUID from the token's `UserId` claim) is JIT-mapped to the local `long` `user_id` that every `created_by`/`requested_by`/`AUDIT_LOG.user_id`/`STOCK_LEDGER.user_id` FK references — no FK type change. A door policy admits only ERP `UserType`/`Role` holders.
- **External IdP + shadow user.** Authentication is delegated to **AuthHex** (RS256, issuer `AuthHex`/audience `AuthHexClient`, static public key). `USER` is a **local shadow** of AuthHex identities: `auth_user_id` (GUID from the token's `UserId` claim) is JIT-mapped to the local `int` `user_id` that every `created_by`/`requested_by`/`AUDIT_LOG.user_id`/`STOCK_LEDGER.user_id` FK references — no FK type change. A door policy admits only ERP `UserType`/`Role` holders.
- **Reserved RBAC.** Role/Permission/UserRole/RolePermission exist for schema-completeness only; only `USER` is live (audit stamp). AuthHex's `RoleCode`/`UserTypeCode` claims drive the door gate today; per-endpoint RBAC is future work.
- **Reorder alerts are a query**, not an entity — computed from `ITEM_REORDER` vs available. Add a table only if alert history is required.
+1 -1
View File
@@ -20,7 +20,7 @@ Authorization: Bearer <access_token>
```
- Every endpoint requires a valid **Bearer JWT**; unauthenticated → `401`. Tokens are issued by the **external AuthHex IdP** (not ERPCore) — **RS256**, issuer `AuthHex`, audience `AuthHexClient`. ERPCore validates them against AuthHex's static RSA public key (no JWKS) and admits only holders of the configured ERP `UserType`/`Role` (door policy) → otherwise `403`.
- **Per-endpoint RBAC is NOT enforced in Phase 1** (FR-X-01): any ERP-admitted user may call any endpoint.
- The **audit actor** is AuthHex's custom **`UserId` (GUID)** claim, mapped to a local shadow user (`long`). Clients never send `createdBy`; the server derives it (docs/10 A.4).
- The **audit actor** is AuthHex's custom **`UserId` (GUID)** claim, mapped to a local shadow user (`int`). Clients never send `createdBy`; the server derives it (docs/10 A.4).
### 1.3 Content type & encoding
`application/json`, UTF-8, **camelCase**. Timestamps ISO 8601 UTC (`2026-07-07T09:30:00Z`); dates `YYYY-MM-DD`. Base currency **LKR** in Phase 1.