Spaces:
Running
Running
| # Document Service - UI Quick Reference | |
| ## π Quick Start (3 Steps) | |
| ### Step 1: Initialize Upload | |
| ```javascript | |
| POST /scm/storage/upload/init | |
| Authorization: Bearer <JWT_TOKEN> | |
| Content-Type: application/json | |
| { | |
| "domain": "po", | |
| "entity_id": "PO-2024-001", | |
| "category": "invoice", | |
| "file_name": "invoice.pdf", | |
| "mime_type": "application/pdf", | |
| "file_size": 2097152, | |
| "visibility": "private" | |
| } | |
| Response: | |
| { | |
| "upload_id": "uuid", | |
| "presigned_urls": ["http://minio:9000/..."], | |
| "object_key": "tenant_001/po/PO-2024-001/invoice/invoice.pdf" | |
| } | |
| ``` | |
| ### Step 2: Upload File to MinIO (Direct) | |
| ```javascript | |
| // β οΈ NOT a backend endpoint - direct upload to MinIO | |
| // Use the presigned_urls[0] from Step 1 response | |
| PUT <presigned_urls[0]> // From Step 1 response | |
| Content-Type: application/pdf | |
| Body: <binary file data> | |
| // NO Authorization header needed (presigned URL is tenant-scoped) | |
| // Calculate checksum while uploading | |
| const fileBuffer = await file.arrayBuffer(); | |
| const checksum = await crypto.subtle.digest('SHA-256', fileBuffer); | |
| ``` | |
| **Note**: This step bypasses the backend API and uploads directly to MinIO for performance. The presigned URL is scoped to the tenant and expires in 15 minutes. | |
| ### Step 3: Complete Upload | |
| ```javascript | |
| POST /scm/storage/upload/complete | |
| Authorization: Bearer <JWT_TOKEN> | |
| Content-Type: application/json | |
| { | |
| "upload_id": "uuid from step 1", | |
| "checksum_sha256": "hex_string" | |
| } | |
| Response: | |
| { | |
| "id": "object_id", | |
| "deduplicated": false | |
| } | |
| ``` | |
| --- | |
| ## π Endpoints Reference | |
| | Method | Endpoint | Purpose | Auth Required | | |
| |--------|----------|---------|---------------| | |
| | POST | `/scm/storage/upload/init` | Initialize upload | β | | |
| | POST | `/scm/storage/upload/complete` | Finalize upload | β | | |
| | POST | `/scm/storage/download-url` | Get download URL | β | | |
| | GET | `/scm/storage/{object_id}` | Get metadata | β | | |
| | DELETE | `/scm/storage/{object_id}` | Delete object | β | | |
| --- | |
| ## π Authentication | |
| All requests must include JWT token: | |
| ```javascript | |
| headers: { | |
| "Authorization": "Bearer <JWT_TOKEN>", | |
| "Content-Type": "application/json" | |
| } | |
| ``` | |
| **JWT Payload Requirements:** | |
| - `user_id`: User identifier | |
| - `username`: Username | |
| - `role`: buyer | supplier | ops | admin | |
| - `merchant_id`: Tenant identifier | |
| --- | |
| ## π― Allowed Domains by Role | |
| | Role | PO | GRN | Inventory | Dispatch | Returns | Promotions | Profile | | |
| |------|----|----|-----------|----------|---------|-----------|---------| | |
| | buyer | β | β | β | β | β | β | β | | |
| | supplier | β | β | β | β | β | β | β | | |
| | ops | β | β | β | β | β | β | β | | |
| | admin | β | β | β | β | β | β | β | | |
| **Delete Permissions**: Only ops and admin (legal_hold can block) | |
| --- | |
| ## π¦ File Limits | |
| | Type | Max Size | MIME Types | | |
| |------|----------|-----------| | |
| | Images | 10 MB | image/jpeg, image/png, image/webp, image/gif | | |
| | Documents | 50 MB | application/pdf, application/msword, application/vnd.ms-excel, text/csv | | |
| | Exports | 50 MB | Any (temporary) | | |
| --- | |
| ## π Bucket Routing (Automatic) | |
| | Type | Bucket | Visibility | | |
| |------|--------|-----------| | |
| | Documents | `cutra-scm-documents` | Private only | | |
| | Images | `cutra-scm-images` | Private | | |
| | Images | `cutra-scm-images-public` | Public | | |
| | Exports | `cutra-scm-exports` | Temporary | | |
| --- | |
| ## β Error Codes | |
| | Code | Meaning | Solution | | |
| |------|---------|----------| | |
| | 400 | Bad Request | Check request payload, verify all required fields | | |
| | 403 | Forbidden | Check user role/domain permissions | | |
| | 404 | Not Found | Object deleted or doesn't exist | | |
| | 409 | Conflict | Checksum mismatch - file corrupted during upload | | |
| | 413 | Too Large | File exceeds size limit | | |
| | 415 | Wrong Type | MIME type not allowed | | |
| --- | |
| ## π Download File (2 Options) | |
| ### Option A: By Object ID | |
| ```javascript | |
| POST /scm/storage/download-url | |
| { "object_id": "uuid" } | |
| ``` | |
| ### Option B: By Composite Key | |
| ```javascript | |
| POST /scm/storage/download-url | |
| { | |
| "domain": "po", | |
| "entity_id": "PO-2024-001", | |
| "category": "invoice", | |
| "file_name": "invoice.pdf" | |
| } | |
| ``` | |
| **Response:** | |
| ```javascript | |
| { | |
| "url": "http://minio:9000/...", | |
| "expires_in": 900 // 15 minutes | |
| } | |
| ``` | |
| --- | |
| ## πΎ Get Metadata | |
| ```javascript | |
| GET /scm/storage/{object_id} | |
| Response: | |
| { | |
| "id": "uuid", | |
| "file_name": "invoice.pdf", | |
| "mime_type": "application/pdf", | |
| "file_size": 2097152, | |
| "visibility": "private", | |
| "domain": "po", | |
| "entity_id": "PO-2024-001", | |
| "category": "invoice", | |
| "checksum_sha256": "hex", | |
| "created_at": "2024-01-14T10:30:00Z", | |
| "legal_hold": false | |
| } | |
| ``` | |
| --- | |
| ## ποΈ Delete Object (Soft Delete) | |
| ```javascript | |
| DELETE /scm/storage/{object_id} | |
| Response: | |
| { "status": "deleted" } | |
| Notes: | |
| - Object marked as deleted (not removed) | |
| - Audit trail preserved | |
| - Returns 404 on subsequent fetches | |
| - Only ops/admin can delete | |
| - Legal hold prevents deletion | |
| ``` | |
| --- | |
| ## β»οΈ Deduplication | |
| Same file uploaded twice? | |
| - **First upload**: `{id: "uuid1", deduplicated: false}` | |
| - **Second upload**: `{id: "uuid1", deduplicated: true}` | |
| **Benefits:** | |
| - Saves storage | |
| - Single copy for identical files | |
| - Service returns existing ID | |
| --- | |
| ## π Security Features | |
| β **Tenant Isolation** | |
| - Cross-tenant access impossible | |
| - All queries filtered by merchant_id | |
| β **Presigned URLs** | |
| - 15-minute expiry (900s) | |
| - Tenant-scoped | |
| - No auth headers in URL | |
| β **RBAC** | |
| - Role-based domain access | |
| - Buyer β Supplier domains | |
| - Delete restricted to ops/admin | |
| β **Soft Delete** | |
| - Audit trail with timestamps | |
| - Legal hold enforcement | |
| - Compliance ready | |
| β **Checksum Validation** | |
| - SHA-256 verification | |
| - Detects file corruption | |
| - Prevents tampering | |
| --- | |
| ## π± React Component Usage | |
| ```javascript | |
| import DocumentUploadComponent from './DocumentUploadComponent'; | |
| <DocumentUploadComponent | |
| token={jwtToken} | |
| domain="po" | |
| entityId="PO-2024-001" | |
| category="invoice" | |
| /> | |
| ``` | |
| --- | |
| ## π Common Workflows | |
| ### Upload Invoice | |
| ```javascript | |
| POST /scm/storage/upload/init | |
| { | |
| "domain": "po", | |
| "entity_id": "PO-2024-001", | |
| "category": "invoice", | |
| "file_name": "invoice_001.pdf", | |
| "mime_type": "application/pdf", | |
| "file_size": 1048576, | |
| "visibility": "private" | |
| } | |
| ``` | |
| ### Upload Merchant Logo | |
| ```javascript | |
| POST /scm/storage/upload/init | |
| { | |
| "domain": "profile", | |
| "entity_id": "MERCHANT-001", | |
| "category": "profile", | |
| "file_name": "logo.png", | |
| "mime_type": "image/png", | |
| "file_size": 524288, | |
| "visibility": "public" | |
| } | |
| ``` | |
| ### Share Document | |
| ```javascript | |
| POST /scm/storage/download-url | |
| { "object_id": "uuid" } | |
| // Share the returned presigned URL (expires in 900s) | |
| ``` | |
| --- | |
| ## π οΈ Troubleshooting | |
| | Issue | Solution | | |
| |-------|----------| | |
| | 403 Forbidden | Check user role has access to domain | | |
| | 404 Not Found | Object deleted or wrong ID | | |
| | 409 Checksum Error | File corrupted - retry upload | | |
| | 413 Too Large | Reduce file size or check limits | | |
| | Upload stuck | Check internet connection, retry | | |
| | No download | Verify presigned URL not expired (900s max) | | |
| --- | |
| ## π Production Checklist | |
| - [ ] Use production JWT tokens | |
| - [ ] Enable HTTPS for all requests | |
| - [ ] Set up MinIO with SSL | |
| - [ ] Configure CORS for domain | |
| - [ ] Monitor upload bandwidth | |
| - [ ] Set up error logging | |
| - [ ] Test with various file types | |
| - [ ] Verify multi-tenancy isolation | |
| - [ ] Set legal hold for compliance docs | |
| - [ ] Monitor presigned URL usage | |
| --- | |
| ## π Related Files | |
| - `UI_INTEGRATION_GUIDE.md` - Detailed integration guide | |
| - `DocumentUploadComponent.jsx` - React component example | |
| - `DOCUMENT_TESTING_GUIDE.md` - API testing | |
| - `app/documents/README.md` - Backend setup | |
| --- | |
| **Last Updated**: January 14, 2026 | |
| **API Version**: 1.0.0 | |
| **Status**: Production Ready β | |