SuperSeller3000/README.md
Simon Kuehn c98f53c6e5
Some checks are pending
CI / test (push) Waiting to run
docs: update README to reflect current state (Plan 8)
- AI backend: Mistral Cloud API (primary, not Ollama)
- Features: photo management, eBay business policies, aspect import
- Architecture section: updated source tree, Messenger transport table
- eBay setup section: step-by-step config guide
- Remove outdated Ollama switch instructions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 11:19:05 +00:00

149 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SuperSeller3000
Admin middleware for managing and selling refurbished IT hardware. Handles article ingestion via AI pipeline (photo → vision → specs → eBay texts), multi-channel publishing (eBay), and order processing with Frappe ERP invoicing.
**Live:** `https://ss3k.schaunwama.de/admin`
**Architecture reference:** `docs/architecture.md`
**Full design doc:** `docs/superpowers/specs/2026-05-13-superseller3000-design.md`
---
## Quick Start
```bash
cp .env .env.local # fill in secrets — see .env for all required vars
docker compose up -d
docker compose exec app php bin/console doctrine:migrations:migrate --env=prod
docker compose exec app php bin/console app:users:create --env=prod
docker compose exec app php bin/console app:api-keys:create --env=prod
```
Workers start automatically via Docker Compose (`worker-ai`, `worker-orders`, `worker-channel`).
---
## Tech Stack
| Layer | Technology |
|---|---|
| Language / Framework | PHP 8.4 · Symfony 8.4 |
| ORM | Doctrine ORM |
| Database | PostgreSQL 17 — schemas: `app`, `logs`, `logs_archive` |
| Queue | Symfony Messenger + Redis 7 Streams |
| AI | Mistral Cloud API — Vision: `pixtral-12b-2409`, Text: `mistral-large-latest` |
| Web Search | Tavily API |
| Channel | eBay Inventory / Account / Fulfillment / Taxonomy APIs |
| ERP | Frappe ERPNext (sales invoices, customer sync) |
| Admin | EasyAdmin 5 |
| Auth | Symfony Security + TOTP 2FA · API keys via `X-Api-Key` header |
| Proxy | Caddy 2 (Auto-HTTPS) |
---
## Running Tests
```bash
# Unit tests (fast, no external deps)
docker compose exec app php vendor/bin/phpunit tests/Unit/
# Single test file or method
docker compose exec app php vendor/bin/phpunit tests/Unit/Domain/Article/ArticleTest.php
docker compose exec app php vendor/bin/phpunit --filter testSomeMethod
# Integration tests (requires credentials in .env.local)
bin/test-integration
bin/test-integration tests/Integration/Infrastructure/Channel/Ebay/
# Static analysis (level 9, must be clean)
docker compose exec app php vendor/bin/phpstan analyse
# Code style
docker compose exec app php vendor/bin/php-cs-fixer fix --dry-run --diff
docker compose exec app php vendor/bin/php-cs-fixer fix
```
---
## Admin Panel Features
- **Articles** — list with row click → detail, inline actions (activate, re-run AI, manage photos)
- **Photo Management** — per-article page: upload, delete, set main, drag-to-reorder
- **AI Pipeline** — per-job step tracking; re-run AI from article detail; failed jobs show real error
- **Article Types** — configurable attribute schemas (name, type, unit, options, required flag)
- **eBay** — category + business policy configuration per article type (fulfillment, payment, return, location pulled live from eBay account)
- **eBay Aspect Import** — typeahead category search, imports taxonomy aspects as attribute mappings
- **Orders / Customers / Invoices** — full read/manage view
- **Prompt Templates** — DB-backed, editable in admin, `{{variable}}` substitution
- **Users** — permission checkboxes per user; API keys via console
---
## Article Ingest
### Via Admin UI
Admin → **Artikel einlesen** — select article type, condition, stock quantity, take/upload photo. Pipeline starts automatically.
### Via API
```bash
curl -X POST https://ss3k.schaunwama.de/api/pipeline/photo-upload \
-H "X-Api-Key: <key>" \
-F "articleTypeId=<uuid>" \
-F "condition=good" \
-F "stock=1" \
-F "photo=@/path/to/photo.jpg"
# Poll status
curl https://ss3k.schaunwama.de/api/pipeline/jobs/<jobId> \
-H "X-Api-Key: <key>"
```
**Pipeline A (photo):** Vision → DB model cache check → *(hit: copy texts, done)* / *(miss: Tavily search → JSON coding → Validation → Draft → eBay text)*
**Pipeline B (PXE dump):** JSON coding → Validation → Draft → eBay text
After pipeline: admin reviews draft, sets price, activates → eBay listing published automatically (with photos).
---
## eBay Setup
For each article type, configure in Admin → **eBay → Kategorie-Konfigurationen**:
1. Assign article type and eBay category ID
2. Select business policies (fulfillment, payment, return) — loaded live from your eBay account
3. Select merchant location
Import aspect mappings via Admin → **Artikeltypen** → aspect import action.
Set the public URL for photo hosting in `.env.local`:
```dotenv
APP_PUBLIC_URL=https://ss3k.schaunwama.de
```
---
## Architecture
Hexagonal (Domain / Application / Infrastructure). See `docs/architecture.md` for the full reference including all flows, entity relationships, queue details, and gotchas.
```
src/
Domain/ # Pure PHP — Article, ArticleType, Order, Customer, AIPipelineJob …
Application/ # Use cases, port interfaces (ChannelAdapterInterface, ErpAdapterInterface …)
Infrastructure/
AI/ # MistralClient, 4 AI agents (Vision, SpecsResearch, JsonCoding, EbayText)
Channel/ # EbayAdapter + 5 API clients, FrappeErpAdapter
Persistence/ # Doctrine repositories (PostgreSQL)
Messenger/ # Message classes + handlers (3 transports: ai_pipeline, orders, channel_sync)
Http/ # Symfony controllers, EasyAdmin CRUD, webhooks, public photo endpoint
```
| Transport | Retries | Used for |
|---|---|---|
| `ai_pipeline` | 3 × 2 s | All AI pipeline steps |
| `orders` | 5 × 1 s | Order processing, invoicing |
| `channel_sync` | 5 × 2 s ≤ 60 s | Publish, stock sync, deactivate, tracking |
| `failed` | — | Dead-letter; replay via `messenger:failed:retry` |