|
1 | | -# ScrapFlow SA – Enterprise Scrapyard Management System |
| 1 | +<div align="center"> |
2 | 2 |
|
3 | | -[](https://dotnet.microsoft.com/en-us/apps/aspnet) |
| 3 | +# ScrapFlow SA |
| 4 | + |
| 5 | +### The complete scrap metal management platform for South African scrapyards |
| 6 | +### SAPS/ITAC compliant · Real-time · Multi-site · Production-ready |
| 7 | + |
| 8 | +[](https://dotnet.microsoft.com/) |
4 | 9 | [](https://react.dev/) |
5 | 10 | [](https://www.postgresql.org/) |
6 | | -[](https://tailwindcss.com/) |
| 11 | +[](https://tailwindcss.com/) |
7 | 12 | [](https://www.docker.com/) |
| 13 | +[](https://scrap-flow-xi.vercel.app/) |
| 14 | +[](LICENSE) |
| 15 | + |
| 16 | +<br/> |
| 17 | + |
| 18 | +[Live Demo](https://scrap-flow-xi.vercel.app/) · [API Docs](https://scrapflow-api.onrender.com/swagger) · [Report Bug](https://github.com/dev-k99/ScrapFlow/issues) |
| 19 | + |
| 20 | +</div> |
| 21 | + |
| 22 | +--- |
8 | 23 |
|
9 | | -**ScrapFlow SA** is a full-stack, production-ready solution built specifically for the South African scrap metal industry. It modernizes the legacy "pen-and-paper" metal trade with a high-performance, **SAPS/ITAC-compliant** digital platform. |
| 24 | +> [!NOTE] |
| 25 | +> **Try the live demo instantly — no sign-up needed.** Use any of the test accounts below: |
| 26 | +> |
| 27 | +> | Role | Email | Password | |
| 28 | +> |------|-------|----------| |
| 29 | +> | **Owner** *(full access)* | `owner@scrapflow.co.za` | `ScrapFlow@2026!` | |
| 30 | +> | Manager | `manager@scrapflow.co.za` | `ScrapFlow@2026!` | |
| 31 | +> | Scale Operator | `scale@scrapflow.co.za` | `ScrapFlow@2026!` | |
| 32 | +> | Grader | `grader@scrapflow.co.za` | `ScrapFlow@2026!` | |
| 33 | +> | Accountant | `accounts@scrapflow.co.za` | `ScrapFlow@2026!` | |
| 34 | +> |
| 35 | +> **Frontend**: https://scrap-flow-xi.vercel.app/ | **API**: https://scrapflow-api.onrender.com |
10 | 36 |
|
11 | 37 | --- |
12 | 38 |
|
13 | | -## 📽️ The Problem vs. The Solution |
| 39 | +## The Problem vs. The Solution |
14 | 40 |
|
15 | | -### The Problem |
| 41 | +South African scrapyards face three existential threats from legacy pen-and-paper operations: |
16 | 42 |
|
17 | | -Legacy scrapyards in South Africa face three existential threats: |
| 43 | +| Pain Point | Before ScrapFlow | After ScrapFlow | |
| 44 | +|------------|-----------------|-----------------| |
| 45 | +| **Compliance Chaos** | Paper registers fail SAPS audits → fines & license revocation | Compliance baked into code — ticket cannot complete without EFT ref + ID photos | |
| 46 | +| **Operational Blindness** | No real-time tonnage or profit visibility | Live dashboard with inventory value, revenue, and ticket stats | |
| 47 | +| **Cash Ban Burden** | Manual EFT tracking is error-prone and un-auditable | Integrated payment workflow with verified references and 5-year audit trail | |
18 | 48 |
|
19 | | -1. **Compliance Chaos**: The _South African Second-Hand Goods Act_ and the recent **2024/2025 Metal Cash Ban** require strict record-keeping. Yards using paper registers often fail SAPS audits, leading to heavy fines or license revocation. |
20 | | -2. **Operational Blindness**: Without real-time inventory tracking, owners are unaware of their exact tonnage on hand or their true profit margins after price fluctuations. |
21 | | -3. **The Electronic Payment Gap**: Switching from cash to electronic payments (EFT) is a huge administrative burden without an integrated workflow to track proof-of-payments. |
| 49 | +> The **South African Second-Hand Goods Act** and the **2024/2025 Metal Cash Ban** require strict digital record-keeping. ScrapFlow acts as a *Digital Auditor* — hard-blocking non-compliant actions at the service layer. |
22 | 50 |
|
23 | | -### The ScrapFlow Solution |
| 51 | +--- |
24 | 52 |
|
25 | | -ScrapFlow digitizes the entire lifecycle of a scrapyard transaction, ensuring that **compliance is baked into the code, not an afterthought.** By hard-blocking non-compliant actions (like missing ID photos or lacking EFT references), it acts as a "Digital Auditor" for the yard. |
| 53 | +## Features |
| 54 | + |
| 55 | +| Category | Feature | Detail | |
| 56 | +|----------|---------|--------| |
| 57 | +| **Compliance** | Cash-ban enforcement | Blocks ticket completion without a verified EFT reference | |
| 58 | +| **Compliance** | Mandatory photo capture | 3 required photos: Seller ID, Load, Proof-of-Payment | |
| 59 | +| **Compliance** | 5-year audit trail | Rolling registers stored per SAPS/ITAC requirements | |
| 60 | +| **Operations** | 6-step inbound ticket | Arrive → Gross Weigh → Grade → Tare → Pay → Complete | |
| 61 | +| **Operations** | Outbound tickets | Full customer invoice workflow with lot selection | |
| 62 | +| **Inventory** | Real-time lot tracking | Automatic lot creation on ticket completion | |
| 63 | +| **Inventory** | Adjust & write-off | Manager-approved lot adjustments with reason logging | |
| 64 | +| **Real-time** | SignalR inventory hub | Live updates pushed to all connected clients instantly | |
| 65 | +| **Automation** | Webhook system | Fires on ticket/inventory events → Zapier, n8n, Make | |
| 66 | +| **UX** | Offline-first PWA | IndexedDB drafts survive connectivity loss | |
| 67 | +| **UX** | Weighbridge integration | Web Serial API direct industrial scale communication | |
| 68 | +| **Admin** | 5-role RBAC | Owner / Manager / ScaleOp / Grader / Accountant | |
| 69 | +| **Admin** | Multi-site support | Manage multiple scrapyards from one account | |
| 70 | +| **Reports** | Revenue & tonnage reports | Filterable by site, date range, and material grade | |
26 | 71 |
|
27 | 72 | --- |
28 | 73 |
|
29 | | -## 🌟 Key Features |
| 74 | +## Tech Stack |
30 | 75 |
|
31 | | -### ⚖️ 1. Compliance-First Engine |
| 76 | +| Backend | Frontend | |
| 77 | +|---------|----------| |
| 78 | +| ASP.NET Core 8 (Clean Architecture) | React 18 (JSX + Vite) | |
| 79 | +| Entity Framework Core 8 | Tailwind CSS + Glassmorphism UI | |
| 80 | +| PostgreSQL (Supabase) | Zustand state management | |
| 81 | +| SignalR (real-time hubs) | @microsoft/signalr client | |
| 82 | +| JWT authentication (12h tokens) | Axios + React Query patterns | |
| 83 | +| HMAC-SHA256 webhook signing | Web Serial API (weighbridge) | |
| 84 | +| Serilog structured logging | IndexedDB offline drafts | |
| 85 | +| Docker + docker-compose | Deployed on Vercel | |
32 | 86 |
|
33 | | -- **Automatic Cash-Ban Enforcement**: The `TicketService` strictly prevents completion without valid EFT references. |
34 | | -- **Mandatory ID/Photo Capture**: Integrated Media API captures 3 mandatory photos (Seller, Load, ID) before a ticket can be finalized. |
35 | | -- **Audit-Ready Registers**: Rolling registers stored for 5 years as required by the Second-Hand Goods Act. |
| 87 | +--- |
36 | 88 |
|
37 | | -### 🍎 2. Premium Apple-Style UX |
| 89 | +## Architecture |
38 | 90 |
|
39 | | -- **Aesthetic Engineering**: A minimalist, high-contrast UI designed for the harsh lighting conditions of a scrapyard. Built with glassmorphism and Tailwind's emerald palette. |
40 | | -- **Weighbridge Integration**: Direct communication with industrial scales via the **Web Serial API**. |
| 91 | +```mermaid |
| 92 | +graph TD |
| 93 | + Client["React Client\n(Vercel)"] |
| 94 | + API["ScrapFlow.API\n(Render)"] |
| 95 | + APP["ScrapFlow.Application\nDTOs · Interfaces · Services"] |
| 96 | + DOMAIN["ScrapFlow.Domain\nEntities · Enums · Business Rules"] |
| 97 | + INFRA["ScrapFlow.Infrastructure\nEF Core · Migrations · Services"] |
| 98 | + DB[("PostgreSQL\n(Supabase)")] |
| 99 | + SIG["SignalR Hubs\nInventory · Tickets"] |
| 100 | + HOOK["Webhook Service\nHMAC-SHA256 signing"] |
| 101 | + N8N["n8n / Zapier\nAutomation workflows"] |
| 102 | +
|
| 103 | + Client -- "REST + SignalR" --> API |
| 104 | + API --> APP |
| 105 | + APP --> DOMAIN |
| 106 | + INFRA --> APP |
| 107 | + INFRA --> DOMAIN |
| 108 | + INFRA --> DB |
| 109 | + API --> SIG |
| 110 | + API --> HOOK |
| 111 | + HOOK -- "HTTP POST" --> N8N |
| 112 | +``` |
41 | 113 |
|
42 | 114 | --- |
43 | 115 |
|
44 | | -## 🛠️ Engineering Challenges & Solves |
| 116 | +## Deployment |
45 | 117 |
|
46 | | -Building a system for a highly regulated environment revealed several technical hurdles: |
| 118 | +| Layer | Platform | Notes | |
| 119 | +|-------|----------|-------| |
| 120 | +| **Frontend** | [Vercel](https://vercel.com/) | Auto-deploys on push to `main` | |
| 121 | +| **Backend API** | [Render](https://render.com/) | Dockerized ASP.NET Core 8 | |
| 122 | +| **Database** | [Supabase](https://supabase.com/) | Managed PostgreSQL | |
| 123 | +| **Automation** | n8n (Docker) + Zapier | 4 pre-built workflow templates | |
47 | 124 |
|
48 | | -### 🔄 1. The Circular Dependency Trap |
| 125 | +--- |
49 | 126 |
|
50 | | -**Problem**: During the split between `Application` and `Infrastructure` layers, I encountered a circular dependency where services needed the `DbContext`, but the `DbContext` needed service-related logic. |
51 | | -**Solution**: I moved high-level business services (like `TicketService`) into the **Infrastructure layer**. By implementing interfaces defined in the `Application` layer, I maintained Clean Architecture principles while resolving the cyclic reference. |
| 127 | +## Engineering Challenges & Solves |
52 | 128 |
|
53 | | -### 💉 2. Dependency Injection & Service Scoping |
| 129 | +### 1. Circular Dependency in Clean Architecture |
| 130 | +**Problem**: During the Application/Infrastructure split, services needed `DbContext` but `DbContext` needed service logic — a circular reference. |
| 131 | +**Solution**: Moved `TicketService` into the Infrastructure layer, implementing interfaces defined in Application. Maintained Clean Architecture principles while resolving the cyclic reference. |
54 | 132 |
|
55 | | -**Problem**: Standalone services (like the `NotificationService` for WhatsApp) initially threw runtime errors due to missing `ILogger` implementations and improper DI registrations. |
56 | | -**Solution**: I standardized DI registrations in `Program.cs` and utilized generic `ILogger<T>` patterns. In unit tests, I used **Moq** to provide verified mock loggers, ensuring the business logic could be tested in isolation from external logging providers. |
| 133 | +### 2. Dependency Injection & Service Scoping |
| 134 | +**Problem**: Standalone services threw runtime errors due to missing `ILogger` implementations and improper DI registrations. |
| 135 | +**Solution**: Standardized all DI registrations in `Program.cs` using generic `ILogger<T>`. In unit tests, used **Moq** to provide verified mock loggers — isolating business logic from external providers. |
57 | 136 |
|
58 | | -### 🧪 3. In-Memory Database Versioning |
| 137 | +### 3. In-Memory Database Versioning |
| 138 | +**Problem**: The xUnit test suite failed because `InMemoryDatabase v10` was incompatible with the .NET 8 target. |
| 139 | +**Solution**: Manual downgrade to `Microsoft.EntityFrameworkCore.InMemory v8.0.0`, aligning test infrastructure with the core runtime. |
59 | 140 |
|
60 | | -**Problem**: When setting up the xUnit test suite, the latest `InMemoryDatabase` version (v10) was incompatible with the project's .NET 8 target. |
61 | | -**Solution**: I performed a manual downgrade to **Microsoft.EntityFrameworkCore.InMemory v8.0.0**, aligning the test infrastructure with the core runtime and ensuring stable "database-in-memory" testing for compliance rules. |
| 141 | +### 4. Non-Blocking Webhook Dispatch |
| 142 | +**Problem**: Firing webhooks synchronously would add latency to every ticket completion HTTP response. |
| 143 | +**Solution**: Implemented fire-and-forget using `_ = Task.Run(() => _webhookService.FireAsync(...))` — the HTTP response returns immediately while the webhook dispatches in the background. HMAC-SHA256 signature header ensures receiver authenticity. |
62 | 144 |
|
63 | 145 | --- |
64 | 146 |
|
65 | | -## 🧠 What I Learnt |
| 147 | +## What I Learnt |
66 | 148 |
|
67 | | -1. **Industry-Specific Architecture**: Learnt how to translate legal requirements (SAPS/ITAC) into strict software "guards" and validation logic. |
68 | | -2. **Browser-Hardware Interfacing**: Explored the power of the **Web Serial API** to bridge the gap between industrial hardware (scales) and modern web browsers. |
69 | | -3. **Offline-First Resilience**: Implemented PWA strategies (Service Workers + IndexedDB) to ensure data integrity even when South African yard connectivity is unstable. |
70 | | -4. **Clean Architecture Discipline**: Deepened my understanding of how to maintain strict separation of concerns even when complex infrastructure dependencies arise. |
| 149 | +1. **Industry-Specific Architecture** — Translating legal requirements (SAPS/ITAC) into strict software guards and validation logic at the service layer. |
| 150 | +2. **Browser-Hardware Interfacing** — Using the **Web Serial API** to bridge industrial weighbridge hardware and modern web browsers without drivers. |
| 151 | +3. **Offline-First Resilience** — PWA strategies with Service Workers + IndexedDB to handle South African connectivity instability. |
| 152 | +4. **Clean Architecture Discipline** — Maintaining strict separation of concerns even when complex infrastructure dependencies arise in real-world projects. |
| 153 | +5. **Real-time Systems** — Building SignalR hubs with group-based broadcasting (per-site) and stable client reconnection handling. |
71 | 154 |
|
72 | 155 | --- |
73 | 156 |
|
74 | | -## 🏗️ Technical Architecture |
| 157 | +## Getting Started (Local Dev) |
75 | 158 |
|
76 | | -```mermaid |
77 | | -graph TD |
78 | | - API[ScrapFlow.API] --> APP[ScrapFlow.Application] |
79 | | - INFRA[ScrapFlow.Infrastructure] --> APP |
80 | | - INFRA --> DOMAIN[ScrapFlow.Domain] |
81 | | - APP --> DOMAIN |
| 159 | +### Prerequisites |
| 160 | +- .NET 8 SDK |
| 161 | +- Node.js 18+ |
| 162 | +- Docker Desktop |
82 | 163 |
|
83 | | - subgraph "Infrastructure" |
84 | | - DB[(PostgreSQL)] |
85 | | - SignalR[SignalR Hubs] |
86 | | - Comp[Compliance Engine] |
87 | | - Notif[Notification Service] |
88 | | - end |
| 164 | +### 1. Clone & configure |
| 165 | +```bash |
| 166 | +git clone https://github.com/dev-k99/ScrapFlow.git |
| 167 | +cd ScrapFlow |
| 168 | +cp src/ScrapFlow.API/appsettings.example.json src/ScrapFlow.API/appsettings.json |
| 169 | +# Edit appsettings.json — add your PostgreSQL connection string and JWT secret |
89 | 170 | ``` |
90 | 171 |
|
91 | | ---- |
| 172 | +### 2. Docker quick start |
| 173 | +```bash |
| 174 | +docker compose up --build |
| 175 | +# API: http://localhost:5010 |
| 176 | +# n8n: http://localhost:5678 |
| 177 | +``` |
| 178 | + |
| 179 | +### 3. Or run individually |
| 180 | +```bash |
| 181 | +# Backend |
| 182 | +cd src/ScrapFlow.API && dotnet run |
92 | 183 |
|
93 | | -## 🚀 Getting Started |
| 184 | +# Frontend |
| 185 | +cd scrapflow-client && npm install && npm run dev |
| 186 | +# → http://localhost:5173 |
| 187 | +``` |
94 | 188 |
|
95 | | -1. **Docker Quick Start**: `docker-compose up --build` |
96 | | -2. **Backend**: `cd src/ScrapFlow.API && dotnet run` |
97 | | -3. **Frontend**: `cd scrapflow-client && npm run dev` |
98 | | -4. **Tests**: `dotnet test` |
| 189 | +### 4. Seed data |
| 190 | +On first run, the API automatically seeds: |
| 191 | +- 5 user accounts (Owner, Manager, ScaleOp, Grader, Accountant) |
| 192 | +- 2 sites (Germiston Main Yard, Durban North Depot) |
| 193 | +- 20 material grades (ferrous + non-ferrous) with realistic ZAR prices |
| 194 | +- 10 suppliers + 3 customers + 5 sample completed tickets |
99 | 195 |
|
100 | 196 | --- |
101 | 197 |
|
102 | | -_Designed & Engineered for South African Scrapyards._ |
| 198 | +## API Reference |
| 199 | + |
| 200 | +| Resource | Method | Endpoint | Auth | |
| 201 | +|----------|--------|----------|------| |
| 202 | +| Auth | POST | `/api/auth/login` | Public | |
| 203 | +| Auth | POST | `/api/auth/register` | Public | |
| 204 | +| Dashboard | GET | `/api/dashboard?siteId=` | Any role | |
| 205 | +| Inbound Tickets | POST | `/api/tickets/inbound` | ScaleOp+ | |
| 206 | +| Inbound Tickets | PUT | `/api/tickets/inbound/{id}/gross-weight` | ScaleOp+ | |
| 207 | +| Inbound Tickets | PUT | `/api/tickets/inbound/{id}/grading` | Grader+ | |
| 208 | +| Inbound Tickets | PUT | `/api/tickets/inbound/{id}/payment` | Accountant+ | |
| 209 | +| Inbound Tickets | PUT | `/api/tickets/inbound/{id}/complete` | Manager+ | |
| 210 | +| Outbound Tickets | GET/POST | `/api/tickets/outbound` | ScaleOp+ | |
| 211 | +| Inventory | GET | `/api/inventory?siteId=&status=` | Any role | |
| 212 | +| Inventory | PUT | `/api/inventory/{id}/adjust` | Manager+ | |
| 213 | +| Inventory | PUT | `/api/inventory/{id}/write-off` | Manager+ | |
| 214 | +| Materials | GET | `/api/materials` | Any role | |
| 215 | +| Suppliers | GET/POST | `/api/suppliers` | ScaleOp+ | |
| 216 | +| Customers | GET/POST | `/api/customers` | ScaleOp+ | |
| 217 | +| Reports | GET | `/api/reports` | Manager+ | |
| 218 | +| Webhooks | GET/POST/DELETE | `/api/webhooks` | Owner only | |
| 219 | +| Sites | GET/POST | `/api/sites` | Owner (POST) | |
| 220 | +| Audit Log | GET | `/api/auditlogs` | Manager+ | |
| 221 | +| Health | GET | `/health` | Public | |
| 222 | + |
| 223 | +> Full interactive docs: **https://scrapflow-api.onrender.com/swagger** |
| 224 | +
|
| 225 | +--- |
| 226 | + |
| 227 | +<div align="center"> |
| 228 | + |
| 229 | +**Designed & Engineered for South African Scrapyards** |
| 230 | + |
| 231 | +*Built by [Kwanele Ntshangase](https://github.com/dev-k99) · [Live Demo](https://scrap-flow-xi.vercel.app/) · [View Source](https://github.com/dev-k99/ScrapFlow)* |
| 232 | + |
| 233 | +</div> |
0 commit comments