Discoveries — Paper Surplus Marketplace
Last Updated: 2026-02-27
Architecture Decisions
Django 6.0.2 (not 5.x)
Using Django 6.0.2 which was recently released. DRF 3.16.1 is compatible. All dependencies confirmed working in venv.
UUID Primary Keys Everywhere
All models inherit from TimestampedModel which uses UUID4 primary keys. This is intentional for:
- Avoiding sequential ID enumeration attacks
- Easier data merging if multi-tenant is added later
- Better for distributed systems
23 TextChoices Enums in common/enums.py
All domain enums are centralized in common/enums.py rather than per-app. This prevents circular imports and makes the domain vocabulary discoverable in one place. Enums cover: PaperType (11 types), QualityGrade, Incoterm (7 types), OnboardingStatus, FiberType, ProductForm, FluteType, Region (7 regions), CompanyType, CreditStatus, NewsletterFrequency, SurplusSource (8 types), ContainerType, SurplusItemStatus (7 states), MatchType, MatchStatus, ContainerProposalStatus, IngestionStatus, NewsletterStatus, NewsletterTrigger, PaymentStatus, PaymentMethod, ShippingStatus, VisibilityRuleType, VisibilityScope.
Split Settings Pattern
Settings are split into config/settings/ with base.py (shared), dev.py, testing.py, prod.py. The DJANGO_SETTINGS_MODULE defaults to config.settings.dev.
9 Feature-Based Apps
Apps map to business domains:
accounts— CustomUser, authenticationmills— Mill entitiesbuyers— Buyer entities, BuyerSpecsurplus— SurplusItem, Product, VisibilityRulematching— MatchResultcontainers— ContainerProposalnewsletters— Newsletter generation and deliveryingestion— Excel/email parsing, IngestionBatch, ParserConfigtransactions— Transaction records, payments, shipping
Service Layer Pattern
Business logic lives in apps/<app>/services.py, not in views or models. Views call services, services orchestrate model operations. This keeps views thin and business logic testable.
Backend Integration Notes
Database
- PostgreSQL on localhost, DB:
marketplace, User:marketplace - uuid-ossp extension enabled
- Redis DB 1 reserved for caching/Celery
morichal_sourceDB contains legacy MorichalAI data (read-only)
Entity Model Count
13 models across 9 apps — all implemented and migrated:
- CustomUser (accounts)
- Mill (mills)
- Buyer (buyers)
- BuyerSpec (buyers)
- Product (surplus)
- SurplusItem (surplus)
- VisibilityRule (surplus)
- MatchResult (matching)
- ContainerProposal (containers)
- Newsletter (newsletters)
- IngestionBatch (ingestion)
- ParserConfig (ingestion)
- Transaction (transactions)
SurplusItem State Machine
Status transitions follow strict rules:
- pre_production → available
- available → exclusive, expired, withdrawn
- exclusive → sold, available (on expiry/decline)
- Invalid transitions raise errors
MorichalAI Data Migration
Import Architecture
- Service at
backend/common/services/morichal_import/with 11 modules - Uses psycopg 3.3.3 (NOT psycopg2) for direct source DB access
- Management command:
python manage.py import_morichal - Supports --dry-run, --phases (selective), --clear-existing, --report-file
Import Results
- 111 mills, 162 buyers, 486 products, 170 buyer specs, 12 freight rates, 773 transactions (1,714 total)
- All records tagged with
[Imported from MorichalAI ...]in notes field - Idempotent via get_or_create — safe to rerun
Key Decisions
- Legacy data mapped to new enum values (e.g., old paper types → PaperType choices)
- Freight rates stored as FreightRate records linked to origin/destination regions
- Transaction history preserved for derive_specs/derive_products commands to work on
Frontend Notes
Container Visualization Architecture (2026-02-27)
Three container components at different scales:
container-mini.component.ts— Card-size (ViewBox 120x52). Simplified 3D shape, no ridges/door lines. Shows up to 2 containers + overflow count. Used in browse grid cards.container-svg.component.ts— Full detail (ViewBox 400x200). 3D isometric with corrugation ridges, door lines, fill animation. HasshowLegendinput for grid usage. Used when a single large container visualization is needed.reel-container-widget.component.ts— Interactive calculator widget. Replaced the old static Container Fit card on product detail pages. Includes reel-based container packing logic.
All use CSS variables (--surface-sunken, --border-strong, --accent-400, --surface-overlay, --surface-raised, --text-primary, --text-secondary) for light/dark theme support.
Surplus Card CTA Pattern
Cards now include a "Request Quote" button that shows a toast notification. Uses event.preventDefault() + event.stopPropagation() to prevent the card's routerLink from firing when button is clicked.
Transaction UI Pattern
- Transaction detail uses a status timeline component showing progression through states
- Action buttons (confirm, ship, complete) are conditionally rendered based on current status and user role
- Angular Material dialogs for confirmation flows
Navigation
- Role-based routing: admin sees all modules, mills see surplus/transactions, buyers see matching/transactions
- Lazy-loaded feature modules for performance
- JWT token stored in localStorage, interceptor adds Authorization header
Paper Industry Domain Notes
GSM Ranges
- Paper industry standard: 13-500 GSM (grams per square meter)
- Typical surplus: 60-350 GSM
- Validators enforce 13-500 range
Container Specs
- 20ft: ~21,770 kg max payload
- 40ft: ~26,780 kg max payload
- 40ft HC: ~26,480 kg max payload
- Container assembly uses greedy bin-packing algorithm
Incoterms
7 supported: EXW, FCA, FOB, CFR, CIF, DAP, DDP (subset of ICC Incoterms 2020)