18 KiB
00 · CORE — ERP System (Phase 1: Inventory & Supply Chain)
This file is the hub. Start every task here. It defines the repository structure, the tech stack, how to stand up a runnable backend, and — most importantly — where to go next for any piece of work. Do not begin backend or frontend work without first reading the relevant section below and following the routing table in §7.
1. What this project is
A modular ERP built in phases. Phase 1 delivers the Inventory & Supply Chain subsystem: master data, procurement, goods receipt, and stock management (FIFO costing, multi-warehouse, single-tenant).
| Decision | Value |
|---|---|
| Costing method | FIFO (cost-layer tracking, per item per warehouse) |
| Warehouses | Multi-warehouse |
| Tenancy | Single-tenant |
| Approvals (PO, adjustment) | Auto-approve, config-gated off (RBAC deferred) |
| Access control | Authentication only; RBAC deferred, user identity stamped for audit |
| Vendor invoice / 3-way match | Deferred to Accounting phase (GRN retains hooks) |
Full requirements live in the backend spec (see §7). This file does not duplicate them.
Phase 2 (HRM) is now underway alongside Phase 1 — see §7 routing to 12-BACKEND-HRM.md / 13-BACKEND-HRM-API.md / 21-FRONTEND-HRM.md.
Sales has its own API reference at 14-BACKEND-SALES-API.md.
2. Repository structure
erp-monorepo/
├── README.md # Root: what this is + how to run both sides
├── .gitignore # ignore appsettings.*.json secrets, .env*, bin/, obj/, node_modules/
│
├── Backend/ # ASP.NET Core Web API (.NET 10) — see §5 to initialize
│ ├── ERPCore.sln
│ ├── PROGRESS.md # ← backend change checklist (Claude-maintained, git-shared)
│ └── ERPCore/ # single Web API project (folders, not multi-project)
│
├── Frontend/ # Next.js (App Router + TypeScript) — already initialized
│ └── PROGRESS.md # ← frontend change checklist (Claude-maintained, git-shared)
│
└── docs/ # ALL documentation
├── 00-CORE.md # ← you are here (hub)
├── 01-DOC-GUIDE.md # documentation map + tracking conventions
├── 02-SECURITY.md # accepted-risks register + per-feature security checklist
├── 10-BACKEND-PHASE1.md # backend spec: SRS + ER/entities + tech + architecture
├── 11-BACKEND-PHASE1.md # backend API reference (complete req/res)
├── 14-BACKEND-SALES-API.md # sales API reference (invoices, slips, free issues, reports)
├── 12-GENERAL-LEDGER-INTEGRATION.md # ERPCore ↔ external General Ledger service (transport only)
├── 20-FRONTEND.md # frontend user-flows + architecture rules + validation posture
└── 21-GENERAL-LEDGER-FRONTEND.md # Ledgers section: reports UI + cash/bank accounts
The Backend/ERPCore/ internal layout is created in §5.3.
3. Tech stack
Backend
| Concern | Choice |
|---|---|
| Runtime | .NET 10 (net10.0) |
| Framework | ASP.NET Core Web API (controllers) |
| ORM | EF Core 10 |
| Database | PostgreSQL (Npgsql provider) |
| Auth | JWT bearer (simple in-app login; RBAC deferred) |
| Logging | Serilog (file sink) |
| API docs | Swashbuckle / Swagger UI |
| Health | HealthChecks + EF Core DB check → GET /health |
Frontend
| Concern | Choice |
|---|---|
| Framework | Next.js (App Router) + TypeScript |
| State/forms | Plain React hooks (useState / custom hooks) |
| Validation | Dependency-free (hand-rolled) — client-side for UX only; server is authoritative |
| API access | Typed fetch client against the backend (NEXT_PUBLIC_API_BASE_URL) |
The Frontend project is already initialized. Do not scaffold it here. For all frontend architecture, flows, and validation rules, go to
20-FRONTEND.md.
Database
PostgreSQL, running on localhost for development. Secrets management (User Secrets / environment variables) is deferred; see §5.4 for the current local configuration and the note on secrets.
4. Architecture (high-level, both sides)
Backend layering — strict, one direction
Controller → Service → Repository → UnitOfWork/DbContext
(DTOs) (logic) (entities) (transaction boundary)
Rules (non-negotiable for Phase 1):
- DTOs at the controller boundary. Controllers accept and return DTOs only — never EF entities and never
DbContext. - Services hold business logic and return DTOs. Controllers stay thin (HTTP concerns only).
- Repositories own EF entities and data access. No business rules in repositories.
- UnitOfWork is the transaction boundary. Every stock-affecting operation (GRN confirm, transfer dispatch/receive, adjustment, return, count post) runs inside a single UoW transaction.
- FIFO lives in a domain service (
FifoCostingService), invoked by stock services inside the UoW transaction — not in a controller or repository. Layer consumption must lock affected rows for concurrency safety. - Errors use RFC 7807
ProblemDetails(ASP.NET Core default). Domain errors carry a stablecode. The error catalog is in11-BACKEND-PHASE1.md. - Audit actor is derived from the authenticated principal (token
sub), never from the request body.
Frontend (summary — full rules in 20-FRONTEND.md)
- Stick to the existing initialized architecture; do not restructure.
- Validate on the client for UX, but never trust it. The server re-validates everything. Business rules that depend on server state (stock availability, negative-stock block, over-receipt tolerance, FIFO sufficiency) are server-authoritative and must not be assumed valid client-side.
5. Backend initialization (runnable)
Follow in order. Commands assume repo root erp-monorepo/.
5.1 Prerequisites
- .NET 10 SDK (
dotnet --version→10.0.x) - PostgreSQL running on localhost, and a login you can use
- EF Core CLI:
dotnet tool install --global dotnet-ef(ordotnet tool update --global dotnet-ef) - Node.js (for the Frontend, handled separately)
5.2 Create solution and project
mkdir -p Backend && cd Backend
dotnet new sln -n ERPCore
dotnet new webapi -n ERPCore --use-controllers -f net10.0
dotnet sln add ERPCore/ERPCore.csproj
cd ERPCore
5.3 Create the internal folder structure
# from Backend/ERPCore/
mkdir -p Controllers Services/Interfaces Services/Stock \
Repositories/Interfaces \
Domain/Entities Domain/Enums \
Dtos/Common \
Common/Models \
Infra/UoW Infra/Persistence/Configurations Infra/Persistence/Migrations Infra/Auth Infra/Logging \
System/Errors \
HealthChecks
Target internal layout:
ERPCore/
├── ERPCore.csproj
├── Program.cs
├── appsettings.json # base (no real secrets)
├── appsettings.Development.json # local dev (localhost Postgres)
├── appsettings.Production.json # env-var driven
├── Controllers/ # HTTP; DTOs in/out
├── Services/ # business logic → DTOs
│ ├── Interfaces/
│ └── Stock/ (StockService, FifoCostingService)
├── Repositories/ # EF data access (entities)
│ └── Interfaces/
├── Domain/
│ ├── Entities/ # 38 EF entities
│ └── Enums/ # ItemType, TrackingMode, HoldStatus, Direction, *Status
├── Dtos/ # request/response DTOs (+ Common: paging)
├── Common/Models/ # PagedResult<T>, PaginationMeta, Result<T>
├── Infra/
│ ├── UoW/ # IUnitOfWork, UnitOfWork
│ ├── Persistence/ # ErpDbContext, Configurations/, Migrations/
│ ├── Auth/ # JWT setup, ICurrentUser (audit source)
│ └── Logging/ # Serilog config
├── System/Errors/ # domain exceptions, error codes, ProblemDetails middleware
└── HealthChecks/ # EF Core DB health check
5.4 Add packages
Pinned versions below are current for .NET 10 at time of writing. All Microsoft.EntityFrameworkCore.* packages must share the same version.
# from Backend/ERPCore/
dotnet add package Microsoft.EntityFrameworkCore --version 10.0.9
dotnet add package Microsoft.EntityFrameworkCore.Design --version 10.0.9
dotnet add package Microsoft.EntityFrameworkCore.Tools --version 10.0.9
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL --version 10.0.2
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 10.0.9
dotnet add package Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore --version 10.0.9
dotnet add package Serilog.AspNetCore --version 9.0.0
dotnet add package Serilog.Sinks.File --version 6.0.0
dotnet add package Swashbuckle.AspNetCore --version 10.2.3
Resulting ERPCore.csproj:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="10.0.9" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.3" />
</ItemGroup>
<ItemGroup>
<Folder Include="Common\Models\" />
<Folder Include="Infra\UoW\" />
<Folder Include="System\Errors\" />
</ItemGroup>
</Project>
Swashbuckle v10 note: v10 upgraded to
Microsoft.OpenApi2.x and emits OpenAPI 3.1.Program.csusesusing Microsoft.OpenApi;andAddSwaggerGen(...)/UseSwagger()/UseSwaggerUI().
5.5 App settings (three files)
appsettings.json (base — no real secrets; placeholder only):
{
"Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } },
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Port=5432;Database=ERPCore;Username=CHANGE_ME;Password=CHANGE_ME"
},
"Jwt": {
"Issuer": "ERPCore",
"Audience": "ERPCore.Clients",
"SigningKey": "CHANGE_ME_DEV_ONLY_32+_CHARS",
"AccessTokenMinutes": 120
},
"AllowedHosts": "*"
}
appsettings.Development.json (local dev — used by dotnet run in Development):
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Port=5432;Database=ERPCore;Username=postgres;Password=postgres"
},
"Jwt": { "SigningKey": "dev-only-signing-key-please-change-me-0123456789" }
}
appsettings.Production.json (values injected from environment variables at deploy time):
{
"ConnectionStrings": { "DefaultConnection": "" },
"Jwt": { "SigningKey": "" }
}
Secrets note (current phase): we are on localhost dev only, so the Development file holds local credentials for convenience. Before any shared/staging/production use, move secrets to User Secrets (dev) and environment variables (prod), keep
appsettings.Development.json/appsettings.Production.jsonout of git (see root.gitignore), and rotate any credential that was ever committed.
5.6 Minimal Program.cs wiring (outline)
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi;
using Serilog;
var builder = WebApplication.CreateBuilder(args);
// Serilog (file sink)
builder.Host.UseSerilog((ctx, cfg) => cfg
.ReadFrom.Configuration(ctx.Configuration)
.WriteTo.File("logs/erpcore-.log", rollingInterval: RollingInterval.Day));
builder.Services.AddControllers();
// EF Core + PostgreSQL
builder.Services.AddDbContext<ErpDbContext>(o =>
o.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
// ProblemDetails (RFC 7807)
builder.Services.AddProblemDetails();
// JWT bearer auth (RBAC deferred; identity used for audit stamp)
builder.Services.AddAuthentication(/* JwtBearer options from config */);
builder.Services.AddAuthorization();
// Health checks (EF Core DB)
builder.Services.AddHealthChecks().AddDbContextCheck<ErpDbContext>();
// Swagger
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(o => o.SwaggerDoc("v1", new() { Title = "ERPCore API", Version = "v1" }));
// DI: register UoW, repositories, services, ICurrentUser, FifoCostingService here
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseExceptionHandler(); // maps to ProblemDetails
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapHealthChecks("/health");
app.Run();
5.7 Create the database and run
# ensure the ERPCore database exists in your local Postgres, then:
dotnet ef migrations add InitialCreate -o Infra/Persistence/Migrations
dotnet ef database update
dotnet run
Verify:
- Swagger UI at
https://localhost:<port>/swagger - Health at
https://localhost:<port>/health→Healthy
Entity/DbContext modeling (the 42 entities, configurations, enums) is specified in 10-BACKEND-PHASE1.md. Do not invent the schema here — follow that file.
6. Running the frontend
The Frontend is already initialized. Point it at the backend:
cd Frontend
cp .env.local.example .env.local # set NEXT_PUBLIC_API_BASE_URL=https://localhost:<port>
npm install
npm run dev
All frontend work is governed by 20-FRONTEND.md.
7. Routing — where to go next
Every task starts here, then jumps to the right doc. Do not work from memory; open the target doc.
| If you are working on… | Go to |
|---|---|
| Requirements, business rules, entities, ER model, data types, architecture detail (Phase 1: Inventory & Supply Chain) | 10-BACKEND-PHASE1.md |
| API endpoints, request/response shapes, error catalog, enums (Phase 1) | 11-BACKEND-PHASE1.md |
| HRM requirements, business rules, entities, ER model (Phase 2) | 12-BACKEND-HRM.md |
| HRM API endpoints, request/response shapes, error catalog (Phase 2) | 13-BACKEND-HRM-API.md |
| Connecting to the external General Ledger service (proxy, config, API key) | 12-GENERAL-LEDGER-INTEGRATION.md |
| The Ledgers frontend section (reports, cash/bank accounts) | 21-GENERAL-LEDGER-FRONTEND.md |
| Sales API endpoints, request/response shapes, error catalog, enums | 14-BACKEND-SALES-API.md |
| Frontend user-flows, screen flow, architecture rules, validation posture (Phase 1) | 20-FRONTEND.md |
| HRM frontend user-flows (Phase 2) | 21-FRONTEND-HRM.md |
| Manufacturing requirements, business rules, entities, stock/costing integration, API (Phase 2) | 30-BACKEND-PHASE2.md |
| Manufacturing frontend user-flows — template canvas, run board, run execution (Phase 2) | 21-FRONTEND-PHASE2.md |
| Security risks per feature, accepted-risk register, pre-ship checklist | 02-SECURITY.md |
| Understanding the doc system, reading order, tracking conventions | 01-DOC-GUIDE.md |
| Recording backend changes made | Backend/PROGRESS.md |
| Recording frontend changes made | Frontend/PROGRESS.md |
Quick resolver:
- "Where is the model / an entity defined?" →
10-BACKEND-PHASE1.md(Phase 1) /12-BACKEND-HRM.md(HRM) — schema is authoritative there. - "What does this endpoint accept/return?" →
11-BACKEND-PHASE1.md(Phase 1) /13-BACKEND-HRM-API.md(HRM). - "What does the sales API accept/return?" →
14-BACKEND-SALES-API.md. - "How should the UI flow / what do I validate where?" →
20-FRONTEND.md(Phase 1) /21-FRONTEND-HRM.md(HRM). - "What security risks / checks apply to this feature?" →
02-SECURITY.md.
8. Progress tracking (mandatory)
Two checklists track what has actually been built, and travel with code via git:
Backend/PROGRESS.md— backend changesFrontend/PROGRESS.md— frontend changes
These are Claude-maintained: whenever a change is made on either side, the corresponding PROGRESS.md must be updated (tick items, add entries). The format and rules for these files are defined in 01-DOC-GUIDE.md §Tracking. Both files must exist before feature work begins.
Security gate: before ticking any data-mutating feature
[x], run its checklist in02-SECURITY.md(Part C) plus the foundational controls (Part B). Accepted Phase-1 exposures are recorded in02-SECURITY.mdPart A — do not "fix" them ad-hoc.
9. Documentation maintenance rule
There is one source of truth per topic. The SRS and API content live inside the docs above (not in separate scattered files). When a requirement or endpoint changes, edit it in place in 10-/11-BACKEND-PHASE1.md — do not fork copies. See 01-DOC-GUIDE.md for the full maintenance and navigation rules.
End of 00-CORE.md — the hub. Next: 01-DOC-GUIDE.md.