diff --git a/docs/development/synology-github-repo.md b/docs/development/synology-github-repo.md index 58509e1e..b2e00918 100644 --- a/docs/development/synology-github-repo.md +++ b/docs/development/synology-github-repo.md @@ -55,13 +55,15 @@ Git is an open-source, distributed version control system that helps you manage ``` Adding new repository on NAS DS223J -> ssh boulaht1@DS223J -> sudo -i -> cd /volume1/git-repos/ -> git --bare init my-repo.git -> chown -R git-boulaht1:users my-repo.git -> cd my-repo.git/ - + ```bash + > ssh boulaht1@DS223J + > sudo -i + > cd /volume1/git-repos/ + > git --bare init my-repo.git + > chown -R git-boulaht1:users my-repo.git + > cd my-repo.git/ + ``` + --- ### ✅ **Steps to Push Local Git Repo to Synology NAS** diff --git a/docs/implementation/oms-feature-plan.md b/docs/implementation/oms-feature-plan.md new file mode 100644 index 00000000..2534c676 --- /dev/null +++ b/docs/implementation/oms-feature-plan.md @@ -0,0 +1,662 @@ +# OMS Feature Implementation Plan + +## Overview + +Transform Wizamart into a **"Lightweight OMS for Letzshop Sellers"** by building the missing features that justify the tier pricing structure. + +**Goal:** Ship Essential tier quickly, then build Professional differentiators, then Business features. + +## Design Decisions (Confirmed) + +| Decision | Choice | +|----------|--------| +| Phase 1 scope | Invoicing + Tier Limits together | +| PDF library | WeasyPrint (HTML/CSS to PDF) | +| Invoice style | Simple & Clean (minimal design) | + +--- + +## Current State Summary + +### Already Production-Ready +- Multi-tenant architecture (Company → Vendor hierarchy) +- Letzshop order sync, confirmation, tracking +- Inventory management with locations and reservations +- Unified Order model (direct + marketplace) +- Customer model with pre-calculated stats (total_orders, total_spent) +- Team management + RBAC +- CSV export patterns (products) + +### Needs to be Built +| Feature | Tier Impact | Priority | +|---------|-------------|----------| +| Basic LU Invoice (PDF) | Essential | P0 | +| Tier limits enforcement | Essential | P0 | +| Vendor VAT Settings | Professional | P1 | +| EU VAT Invoice | Professional | P1 | +| Incoming Stock / PO | Professional | P1 | +| Customer CSV Export | Professional | P1 | +| Multi-vendor view | Business | P2 | +| Accounting export | Business | P2 | + +--- + +## Phase 1: Essential Tier (Target: 1 week) + +**Goal:** Launch Essential (€49) with basic invoicing and tier enforcement. + +### Step 1.1: Vendor Invoice Settings (1 day) + +**Create model for vendor billing details:** + +``` +models/database/vendor_invoice_settings.py +``` + +Fields: +- `vendor_id` (FK, unique - one-to-one) +- `company_name` (legal name for invoices) +- `company_address`, `company_city`, `company_postal_code`, `company_country` +- `vat_number` (e.g., "LU12345678") +- `invoice_prefix` (default "INV") +- `invoice_next_number` (auto-increment) +- `payment_terms` (optional text) +- `bank_details` (optional IBAN etc.) +- `footer_text` (optional) + +**Pattern to follow:** `models/database/letzshop.py` (VendorLetzshopCredentials) + +**Files to create/modify:** +- `models/database/vendor_invoice_settings.py` (new) +- `models/database/__init__.py` (add import) +- `models/database/vendor.py` (add relationship) +- `models/schema/invoice.py` (new - Pydantic schemas) +- `alembic/versions/xxx_add_vendor_invoice_settings.py` (migration) + +--- + +### Step 1.2: Basic Invoice Model (0.5 day) + +**Create invoice storage:** + +``` +models/database/invoice.py +``` + +Fields: +- `id`, `vendor_id` (FK) +- `order_id` (FK, nullable - for manual invoices later) +- `invoice_number` (unique per vendor) +- `invoice_date` +- `seller_details` (JSONB snapshot) +- `buyer_details` (JSONB snapshot) +- `line_items` (JSONB snapshot) +- `subtotal_cents`, `vat_rate`, `vat_amount_cents`, `total_cents` +- `currency` (default EUR) +- `status` (draft, issued, paid, cancelled) +- `pdf_generated_at`, `pdf_path` (optional) + +**Files to create/modify:** +- `models/database/invoice.py` (new) +- `models/database/__init__.py` (add import) +- `alembic/versions/xxx_add_invoices_table.py` (migration) + +--- + +### Step 1.3: Invoice Service - Basic LU Only (1 day) + +**Create service for invoice generation:** + +``` +app/services/invoice_service.py +``` + +Methods: +- `create_invoice_from_order(order_id, vendor_id)` - Generate invoice from order +- `get_invoice(invoice_id, vendor_id)` - Retrieve invoice +- `list_invoices(vendor_id, skip, limit)` - List vendor invoices +- `_generate_invoice_number(settings)` - Auto-increment number +- `_snapshot_seller(settings)` - Capture vendor details +- `_snapshot_buyer(order)` - Capture customer details +- `_calculate_totals(order)` - Calculate with LU VAT (17%) + +**For Essential tier:** Fixed 17% Luxembourg VAT only. EU VAT comes in Professional. + +**Files to create:** +- `app/services/invoice_service.py` (new) + +--- + +### Step 1.4: PDF Generation (1.5 days) + +**Add WeasyPrint dependency and create PDF service:** + +``` +app/services/invoice_pdf_service.py +``` + +Methods: +- `generate_pdf(invoice)` - Returns PDF bytes +- `_render_html(invoice)` - Jinja2 template rendering + +**Template:** +``` +app/templates/invoices/invoice.html +``` + +Simple, clean invoice layout: +- Seller details (top left) +- Buyer details (top right) +- Invoice number + date +- Line items table +- Totals with VAT breakdown +- Footer (payment terms, bank details) + +**Files to create/modify:** +- `requirements.txt` (add weasyprint) +- `app/services/invoice_pdf_service.py` (new) +- `app/templates/invoices/invoice.html` (new) +- `app/templates/invoices/invoice.css` (new, optional) + +--- + +### Step 1.5: Invoice API Endpoints (0.5 day) + +**Create vendor invoice endpoints:** + +``` +app/api/v1/vendor/invoices.py +``` + +Endpoints: +- `POST /orders/{order_id}/invoice` - Generate invoice for order +- `GET /invoices` - List invoices +- `GET /invoices/{invoice_id}` - Get invoice details +- `GET /invoices/{invoice_id}/pdf` - Download PDF + +**Files to create/modify:** +- `app/api/v1/vendor/invoices.py` (new) +- `app/api/v1/vendor/__init__.py` (add router) + +--- + +### Step 1.6: Invoice Settings UI (0.5 day) + +**Add invoice settings to vendor settings page:** + +Modify existing vendor settings template to add "Invoice Settings" section: +- Company name, address fields +- VAT number +- Invoice prefix +- Payment terms +- Bank details + +**Files to modify:** +- `app/templates/vendor/settings.html` (add section) +- `static/vendor/js/settings.js` (add handlers) +- `app/api/v1/vendor/settings.py` (add endpoints if needed) + +--- + +### Step 1.7: Order Detail - Invoice Button (0.5 day) + +**Add "Generate Invoice" / "Download Invoice" button to order detail:** + +- If no invoice: Show "Generate Invoice" button +- If invoice exists: Show "Download Invoice" link + +**Files to modify:** +- `app/templates/vendor/order-detail.html` (add button) +- `static/vendor/js/order-detail.js` (add handler) + +--- + +### Step 1.8: Tier Limits Enforcement (1 day) + +**Create tier/subscription model:** + +``` +models/database/vendor_subscription.py +``` + +Fields: +- `vendor_id` (FK, unique) +- `tier` (essential, professional, business) +- `orders_this_month` (counter, reset monthly) +- `period_start`, `period_end` +- `is_active` + +**Create limits service:** + +``` +app/services/tier_limits_service.py +``` + +Methods: +- `check_order_limit(vendor_id)` - Returns (allowed: bool, remaining: int) +- `increment_order_count(vendor_id)` - Called when order synced +- `get_tier_limits(tier)` - Returns limit config +- `reset_monthly_counters()` - Cron job + +**Tier limits:** +| Tier | Orders/month | Products | +|------|--------------|----------| +| Essential | 100 | 200 | +| Professional | 500 | Unlimited | +| Business | Unlimited | Unlimited | + +**Integration points:** +- `order_service.py` - Check limit before creating order +- Letzshop sync - Check limit before importing + +**Files to create/modify:** +- `models/database/vendor_subscription.py` (new) +- `app/services/tier_limits_service.py` (new) +- `app/services/order_service.py` (add limit check) +- `app/services/letzshop/order_service.py` (add limit check) + +--- + +## Phase 2: Professional Tier (Target: 2 weeks) + +**Goal:** Build the differentiating features that justify €99/month. + +### Step 2.1: EU VAT Rates Table (0.5 day) + +**Create VAT rates reference table:** + +``` +models/database/eu_vat_rates.py +``` + +Fields: +- `country_code` (LU, DE, FR, etc.) +- `country_name` +- `standard_rate` (decimal) +- `reduced_rate_1`, `reduced_rate_2` (optional) +- `effective_from`, `effective_until` + +**Seed with current EU rates (27 countries).** + +**Files to create:** +- `models/database/eu_vat_rates.py` (new) +- `alembic/versions/xxx_add_eu_vat_rates.py` (migration + seed) + +--- + +### Step 2.2: Enhanced Vendor VAT Settings (0.5 day) + +**Add OSS fields to VendorInvoiceSettings:** + +- `is_oss_registered` (boolean) +- `oss_registration_country` (if different from company country) + +**Files to modify:** +- `models/database/vendor_invoice_settings.py` (add fields) +- `alembic/versions/xxx_add_oss_fields.py` (migration) + +--- + +### Step 2.3: VAT Service (1 day) + +**Create VAT calculation service:** + +``` +app/services/vat_service.py +``` + +Methods: +- `get_vat_rate(country_code, as_of_date)` - Lookup rate +- `determine_vat_regime(seller_country, buyer_country, buyer_vat_number, is_oss)` - Returns (regime, rate) +- `validate_vat_number(vat_number)` - Format check (VIES integration later) + +**VAT Decision Logic:** +1. B2B with valid VAT number → Reverse charge (0%) +2. Domestic sale → Domestic VAT +3. Cross-border + OSS registered → Destination VAT +4. Cross-border + under threshold → Origin VAT + +**Files to create:** +- `app/services/vat_service.py` (new) + +--- + +### Step 2.4: Enhanced Invoice Service (1 day) + +**Upgrade invoice service for EU VAT:** + +- Add `vat_regime` field to invoice (domestic, oss, reverse_charge, origin) +- Add `destination_country` field +- Use VATService to calculate correct rate +- Update invoice template for regime-specific text + +**Files to modify:** +- `models/database/invoice.py` (add fields) +- `app/services/invoice_service.py` (use VATService) +- `app/templates/invoices/invoice.html` (add regime text) +- `alembic/versions/xxx_add_vat_regime_to_invoices.py` + +--- + +### Step 2.5: Purchase Order Model (1 day) + +**Create purchase order tracking:** + +``` +models/database/purchase_order.py +``` + +**PurchaseOrder:** +- `id`, `vendor_id` (FK) +- `po_number` (auto-generated) +- `supplier_name` (free text for now) +- `status` (draft, ordered, partial, received, cancelled) +- `order_date`, `expected_date` +- `notes` + +**PurchaseOrderItem:** +- `purchase_order_id` (FK) +- `product_id` (FK) +- `quantity_ordered` +- `quantity_received` +- `unit_cost_cents` (optional) + +**Files to create:** +- `models/database/purchase_order.py` (new) +- `models/database/__init__.py` (add import) +- `models/schema/purchase_order.py` (new) +- `alembic/versions/xxx_add_purchase_orders.py` + +--- + +### Step 2.6: Purchase Order Service (1 day) + +**Create PO management service:** + +``` +app/services/purchase_order_service.py +``` + +Methods: +- `create_purchase_order(vendor_id, data)` - Create PO +- `add_item(po_id, product_id, quantity)` - Add line item +- `receive_items(po_id, items)` - Mark items received, update inventory +- `get_incoming_stock(vendor_id)` - Summary of pending stock +- `list_purchase_orders(vendor_id, status, skip, limit)` + +**Integration:** When items received → call `inventory_service.adjust_inventory()` + +**Files to create:** +- `app/services/purchase_order_service.py` (new) + +--- + +### Step 2.7: Purchase Order UI (1.5 days) + +**Create PO management page:** + +``` +app/templates/vendor/purchase-orders.html +``` + +Features: +- List POs with status +- Create new PO (select products, quantities, expected date) +- Receive items (partial or full) +- View incoming stock summary + +**Inventory page enhancement:** +- Show "On Order" column in inventory list +- Query: SUM of quantity_ordered - quantity_received for pending POs + +**Files to create/modify:** +- `app/templates/vendor/purchase-orders.html` (new) +- `static/vendor/js/purchase-orders.js` (new) +- `app/api/v1/vendor/purchase_orders.py` (new endpoints) +- `app/routes/vendor_pages.py` (add route) +- `app/templates/vendor/partials/sidebar.html` (add menu item) +- `app/templates/vendor/inventory.html` (add On Order column) + +--- + +### Step 2.8: Customer Export Service (1 day) + +**Create customer export functionality:** + +``` +app/services/customer_export_service.py +``` + +Methods: +- `export_customers_csv(vendor_id, filters)` - Returns CSV string + +**CSV Columns:** +- email, first_name, last_name, phone +- customer_number +- total_orders, total_spent, avg_order_value +- first_order_date, last_order_date +- preferred_language +- marketing_consent +- tags (if we add tagging) + +**Files to create:** +- `app/services/customer_export_service.py` (new) + +--- + +### Step 2.9: Customer Export API + UI (0.5 day) + +**Add export endpoint:** + +``` +GET /api/v1/vendor/customers/export?format=csv +``` + +**Add export button to customers page:** +- "Export to CSV" button +- Downloads file directly + +**Files to modify:** +- `app/api/v1/vendor/customers.py` (add export endpoint) +- `app/templates/vendor/customers.html` (add button) + +--- + +## Phase 3: Business Tier (Target: 1-2 weeks) + +**Goal:** Build features for teams and high-volume operations. + +### Step 3.1: Multi-Vendor Consolidated View (2 days) + +**For companies with multiple Letzshop accounts:** + +**New page:** +``` +app/templates/vendor/multi-vendor-dashboard.html +``` + +Features: +- See all vendor accounts under same company +- Consolidated order count, revenue +- Switch between vendor contexts +- Unified reporting + +**Requires:** Company-level authentication context (already exists via Company → Vendor relationship) + +**Files to create/modify:** +- `app/templates/vendor/multi-vendor-dashboard.html` (new) +- `app/services/multi_vendor_service.py` (new) +- `app/api/v1/vendor/multi_vendor.py` (new) + +--- + +### Step 3.2: Accounting Export (1 day) + +**Export invoices in accounting-friendly formats:** + +``` +app/services/accounting_export_service.py +``` + +Methods: +- `export_invoices_csv(vendor_id, date_from, date_to)` - Simple CSV +- `export_invoices_xml(vendor_id, date_from, date_to)` - For accounting software + +**CSV format for accountants:** +- invoice_number, invoice_date +- customer_name, customer_vat +- subtotal, vat_rate, vat_amount, total +- currency, status + +**Files to create:** +- `app/services/accounting_export_service.py` (new) +- `app/api/v1/vendor/accounting.py` (new endpoints) + +--- + +### Step 3.3: API Access Documentation (1 day) + +**If not already documented, create API documentation page:** + +- Document existing vendor API endpoints +- Add rate limiting for API tier +- Generate API keys for vendors + +**Files to create/modify:** +- `docs/api/vendor-api.md` (documentation) +- `app/services/api_key_service.py` (if needed) + +--- + +## Implementation Order Summary + +### Week 1: Essential Tier +| Day | Task | Deliverable | +|-----|------|-------------| +| 1 | Step 1.1 | Vendor Invoice Settings model | +| 1 | Step 1.2 | Invoice model | +| 2 | Step 1.3 | Invoice Service (LU only) | +| 3-4 | Step 1.4 | PDF Generation | +| 4 | Step 1.5 | Invoice API | +| 5 | Step 1.6 | Invoice Settings UI | +| 5 | Step 1.7 | Order Detail button | + +### Week 2: Tier Limits + EU VAT Start +| Day | Task | Deliverable | +|-----|------|-------------| +| 1 | Step 1.8 | Tier limits enforcement | +| 2 | Step 2.1 | EU VAT rates table | +| 2 | Step 2.2 | OSS fields | +| 3 | Step 2.3 | VAT Service | +| 4 | Step 2.4 | Enhanced Invoice Service | +| 5 | Testing | End-to-end invoice testing | + +### Week 3: Purchase Orders + Customer Export +| Day | Task | Deliverable | +|-----|------|-------------| +| 1 | Step 2.5 | Purchase Order model | +| 2 | Step 2.6 | Purchase Order service | +| 3-4 | Step 2.7 | Purchase Order UI | +| 5 | Step 2.8-2.9 | Customer Export | + +### Week 4: Business Tier +| Day | Task | Deliverable | +|-----|------|-------------| +| 1-2 | Step 3.1 | Multi-vendor view | +| 3 | Step 3.2 | Accounting export | +| 4 | Step 3.3 | API documentation | +| 5 | Testing + Polish | Full testing | + +--- + +## Key Files Reference + +### Models to Create +- `models/database/vendor_invoice_settings.py` +- `models/database/invoice.py` +- `models/database/eu_vat_rates.py` +- `models/database/vendor_subscription.py` +- `models/database/purchase_order.py` + +### Services to Create +- `app/services/invoice_service.py` +- `app/services/invoice_pdf_service.py` +- `app/services/vat_service.py` +- `app/services/tier_limits_service.py` +- `app/services/purchase_order_service.py` +- `app/services/customer_export_service.py` +- `app/services/accounting_export_service.py` + +### Templates to Create +- `app/templates/invoices/invoice.html` +- `app/templates/vendor/purchase-orders.html` + +### Existing Files to Modify +- `models/database/__init__.py` +- `models/database/vendor.py` +- `app/services/order_service.py` +- `app/templates/vendor/settings.html` +- `app/templates/vendor/order-detail.html` +- `app/templates/vendor/inventory.html` +- `app/templates/vendor/customers.html` +- `requirements.txt` + +--- + +## Dependencies to Add + +``` +# requirements.txt +weasyprint>=60.0 +``` + +**Note:** WeasyPrint requires system dependencies: +- `libpango-1.0-0` +- `libpangocairo-1.0-0` +- `libgdk-pixbuf2.0-0` + +Add to Dockerfile if deploying via Docker. + +--- + +## Testing Strategy + +### Unit Tests +- `tests/unit/services/test_invoice_service.py` +- `tests/unit/services/test_vat_service.py` +- `tests/unit/services/test_tier_limits_service.py` +- `tests/unit/services/test_purchase_order_service.py` + +### Integration Tests +- `tests/integration/api/v1/vendor/test_invoices.py` +- `tests/integration/api/v1/vendor/test_purchase_orders.py` + +### Manual Testing +- Generate invoice for LU customer +- Generate invoice for DE customer (OSS) +- Generate invoice for B2B with VAT number (reverse charge) +- Create PO, receive items, verify inventory update +- Export customers CSV, import to Mailchimp + +--- + +## Success Criteria + +### Essential Tier Ready When: +- [ ] Can generate PDF invoice from order (LU VAT) +- [ ] Invoice settings page works +- [ ] Order detail shows invoice button +- [ ] Tier limits enforced on order sync + +### Professional Tier Ready When: +- [ ] EU VAT calculated correctly by destination +- [ ] OSS regime supported +- [ ] Reverse charge for B2B supported +- [ ] Purchase orders can be created and received +- [ ] Incoming stock shows in inventory +- [ ] Customer export to CSV works + +### Business Tier Ready When: +- [ ] Multi-vendor dashboard works +- [ ] Accounting export works +- [ ] API access documented diff --git a/docs/implementation/vat-invoice-feature.md b/docs/implementation/vat-invoice-feature.md new file mode 100644 index 00000000..5b6531ad --- /dev/null +++ b/docs/implementation/vat-invoice-feature.md @@ -0,0 +1,734 @@ +# VAT Invoice Feature - Technical Specification + +## Overview + +Generate compliant PDF invoices with correct VAT calculation based on destination country, handling EU cross-border VAT rules including OSS (One-Stop-Shop) regime. + +--- + +## EU VAT Rules Summary + +### Standard VAT Rates by Country (2024) + +| Country | Code | Standard Rate | Reduced | +|---------|------|---------------|---------| +| Luxembourg | LU | 17% | 8%, 3% | +| Germany | DE | 19% | 7% | +| France | FR | 20% | 10%, 5.5% | +| Belgium | BE | 21% | 12%, 6% | +| Netherlands | NL | 21% | 9% | +| Austria | AT | 20% | 13%, 10% | +| Italy | IT | 22% | 10%, 5%, 4% | +| Spain | ES | 21% | 10%, 4% | +| Portugal | PT | 23% | 13%, 6% | +| Ireland | IE | 23% | 13.5%, 9% | +| Poland | PL | 23% | 8%, 5% | +| Czech Republic | CZ | 21% | 15%, 10% | +| ... | ... | ... | ... | + +### When to Apply Which VAT + +``` +┌─────────────────────────────────────────────────────────────┐ +│ VAT DECISION TREE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Is buyer a business with valid VAT number? │ +│ ├── YES → Reverse charge (0% VAT, buyer accounts for it) │ +│ └── NO → Continue... │ +│ │ +│ Is destination same country as seller? │ +│ ├── YES → Apply domestic VAT (Luxembourg = 17%) │ +│ └── NO → Continue... │ +│ │ +│ Is seller registered for OSS? │ +│ ├── YES → Apply destination country VAT rate │ +│ └── NO → Continue... │ +│ │ +│ Has seller exceeded €10,000 EU threshold? │ +│ ├── YES → Must register OSS, apply destination VAT │ +│ └── NO → Apply origin country VAT (Luxembourg = 17%) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Data Model + +### New Tables + +```sql +-- VAT configuration per vendor +CREATE TABLE vendor_vat_settings ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + vendor_id UUID NOT NULL REFERENCES vendors(id), + + -- Company details for invoices + company_name VARCHAR(255) NOT NULL, + company_address TEXT NOT NULL, + company_city VARCHAR(100) NOT NULL, + company_postal_code VARCHAR(20) NOT NULL, + company_country VARCHAR(2) NOT NULL DEFAULT 'LU', + vat_number VARCHAR(50), -- e.g., "LU12345678" + + -- VAT regime + is_vat_registered BOOLEAN DEFAULT TRUE, + is_oss_registered BOOLEAN DEFAULT FALSE, -- One-Stop-Shop + + -- Invoice numbering + invoice_prefix VARCHAR(20) DEFAULT 'INV', + invoice_next_number INTEGER DEFAULT 1, + + -- Optional + payment_terms TEXT, -- e.g., "Due upon receipt" + bank_details TEXT, -- IBAN, etc. + footer_text TEXT, -- Legal text, thank you message + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- EU VAT rates reference table +CREATE TABLE eu_vat_rates ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + country_code VARCHAR(2) NOT NULL, -- ISO 3166-1 alpha-2 + country_name VARCHAR(100) NOT NULL, + standard_rate DECIMAL(5,2) NOT NULL, -- e.g., 17.00 + reduced_rate_1 DECIMAL(5,2), + reduced_rate_2 DECIMAL(5,2), + super_reduced_rate DECIMAL(5,2), + effective_from DATE NOT NULL, + effective_until DATE, -- NULL = current + + UNIQUE(country_code, effective_from) +); + +-- Generated invoices +CREATE TABLE invoices ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + vendor_id UUID NOT NULL REFERENCES vendors(id), + order_id UUID REFERENCES orders(id), -- Can be NULL for manual invoices + + -- Invoice identity + invoice_number VARCHAR(50) NOT NULL, -- e.g., "INV-2024-0042" + invoice_date DATE NOT NULL DEFAULT CURRENT_DATE, + + -- Parties + seller_details JSONB NOT NULL, -- Snapshot of vendor at invoice time + buyer_details JSONB NOT NULL, -- Snapshot of customer at invoice time + + -- VAT calculation details + destination_country VARCHAR(2) NOT NULL, + vat_regime VARCHAR(20) NOT NULL, -- 'domestic', 'oss', 'reverse_charge', 'origin' + vat_rate DECIMAL(5,2) NOT NULL, + + -- Amounts + subtotal_net DECIMAL(12,2) NOT NULL, -- Before VAT + vat_amount DECIMAL(12,2) NOT NULL, + total_gross DECIMAL(12,2) NOT NULL, -- After VAT + currency VARCHAR(3) DEFAULT 'EUR', + + -- Line items snapshot + line_items JSONB NOT NULL, + + -- PDF storage + pdf_path VARCHAR(500), -- Path to generated PDF + pdf_generated_at TIMESTAMP, + + -- Status + status VARCHAR(20) DEFAULT 'draft', -- draft, issued, paid, cancelled + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + UNIQUE(vendor_id, invoice_number) +); +``` + +### Line Items JSONB Structure + +```json +{ + "items": [ + { + "description": "Product Name", + "sku": "ABC123", + "quantity": 2, + "unit_price_net": 25.00, + "vat_rate": 17.00, + "vat_amount": 8.50, + "line_total_net": 50.00, + "line_total_gross": 58.50 + } + ] +} +``` + +--- + +## Service Layer + +### VATService + +```python +# app/services/vat_service.py + +from decimal import Decimal +from datetime import date +from typing import Optional + +class VATService: + """Handles VAT calculation logic for EU cross-border sales.""" + + # Fallback rates if DB lookup fails + DEFAULT_RATES = { + 'LU': Decimal('17.00'), + 'DE': Decimal('19.00'), + 'FR': Decimal('20.00'), + 'BE': Decimal('21.00'), + 'NL': Decimal('21.00'), + 'AT': Decimal('20.00'), + 'IT': Decimal('22.00'), + 'ES': Decimal('21.00'), + # ... etc + } + + def __init__(self, db_session): + self.db = db_session + + def get_vat_rate(self, country_code: str, as_of: date = None) -> Decimal: + """Get current VAT rate for a country.""" + as_of = as_of or date.today() + + # Try DB first + rate = self.db.query(EUVATRate).filter( + EUVATRate.country_code == country_code, + EUVATRate.effective_from <= as_of, + (EUVATRate.effective_until.is_(None) | (EUVATRate.effective_until >= as_of)) + ).first() + + if rate: + return rate.standard_rate + + # Fallback + return self.DEFAULT_RATES.get(country_code, Decimal('0.00')) + + def determine_vat_regime( + self, + seller_country: str, + buyer_country: str, + buyer_vat_number: Optional[str], + seller_is_oss: bool, + seller_exceeded_threshold: bool = False + ) -> tuple[str, Decimal]: + """ + Determine which VAT regime applies and the rate. + + Returns: (regime_name, vat_rate) + """ + # B2B with valid VAT number = reverse charge + if buyer_vat_number and self._validate_vat_number(buyer_vat_number): + return ('reverse_charge', Decimal('0.00')) + + # Domestic sale + if seller_country == buyer_country: + return ('domestic', self.get_vat_rate(seller_country)) + + # Cross-border B2C + if seller_is_oss: + # OSS: destination country VAT + return ('oss', self.get_vat_rate(buyer_country)) + + if seller_exceeded_threshold: + # Should be OSS but isn't - use destination anyway (compliance issue) + return ('oss_required', self.get_vat_rate(buyer_country)) + + # Under threshold: origin country VAT + return ('origin', self.get_vat_rate(seller_country)) + + def _validate_vat_number(self, vat_number: str) -> bool: + """ + Validate EU VAT number format. + For production: integrate with VIES API. + """ + if not vat_number or len(vat_number) < 4: + return False + + # Basic format check: 2-letter country + numbers + country = vat_number[:2].upper() + return country in self.DEFAULT_RATES + + def calculate_invoice_totals( + self, + line_items: list[dict], + vat_rate: Decimal + ) -> dict: + """Calculate invoice totals with VAT.""" + subtotal_net = Decimal('0.00') + + for item in line_items: + quantity = Decimal(str(item['quantity'])) + unit_price = Decimal(str(item['unit_price_net'])) + line_net = quantity * unit_price + + item['line_total_net'] = float(line_net) + item['vat_rate'] = float(vat_rate) + item['vat_amount'] = float(line_net * vat_rate / 100) + item['line_total_gross'] = float(line_net + line_net * vat_rate / 100) + + subtotal_net += line_net + + vat_amount = subtotal_net * vat_rate / 100 + total_gross = subtotal_net + vat_amount + + return { + 'subtotal_net': float(subtotal_net), + 'vat_rate': float(vat_rate), + 'vat_amount': float(vat_amount), + 'total_gross': float(total_gross), + 'line_items': line_items + } +``` + +### InvoiceService + +```python +# app/services/invoice_service.py + +class InvoiceService: + """Generate and manage invoices.""" + + def __init__(self, db_session, vat_service: VATService): + self.db = db_session + self.vat = vat_service + + def create_invoice_from_order( + self, + order_id: UUID, + vendor_id: UUID + ) -> Invoice: + """Generate invoice from an existing order.""" + order = self.db.query(Order).get(order_id) + vat_settings = self.db.query(VendorVATSettings).filter_by( + vendor_id=vendor_id + ).first() + + if not vat_settings: + raise ValueError("Vendor VAT settings not configured") + + # Determine VAT regime + regime, rate = self.vat.determine_vat_regime( + seller_country=vat_settings.company_country, + buyer_country=order.shipping_country, + buyer_vat_number=order.customer_vat_number, + seller_is_oss=vat_settings.is_oss_registered + ) + + # Prepare line items + line_items = [ + { + 'description': item.product_name, + 'sku': item.sku, + 'quantity': item.quantity, + 'unit_price_net': float(item.unit_price) + } + for item in order.items + ] + + # Calculate totals + totals = self.vat.calculate_invoice_totals(line_items, rate) + + # Generate invoice number + invoice_number = self._generate_invoice_number(vat_settings) + + # Create invoice + invoice = Invoice( + vendor_id=vendor_id, + order_id=order_id, + invoice_number=invoice_number, + invoice_date=date.today(), + seller_details=self._snapshot_seller(vat_settings), + buyer_details=self._snapshot_buyer(order), + destination_country=order.shipping_country, + vat_regime=regime, + vat_rate=rate, + subtotal_net=totals['subtotal_net'], + vat_amount=totals['vat_amount'], + total_gross=totals['total_gross'], + line_items={'items': totals['line_items']}, + status='issued' + ) + + self.db.add(invoice) + self.db.commit() + + return invoice + + def _generate_invoice_number(self, settings: VendorVATSettings) -> str: + """Generate next invoice number and increment counter.""" + year = date.today().year + number = settings.invoice_next_number + + invoice_number = f"{settings.invoice_prefix}-{year}-{number:04d}" + + settings.invoice_next_number += 1 + self.db.commit() + + return invoice_number + + def _snapshot_seller(self, settings: VendorVATSettings) -> dict: + """Capture seller details at invoice time.""" + return { + 'company_name': settings.company_name, + 'address': settings.company_address, + 'city': settings.company_city, + 'postal_code': settings.company_postal_code, + 'country': settings.company_country, + 'vat_number': settings.vat_number + } + + def _snapshot_buyer(self, order: Order) -> dict: + """Capture buyer details at invoice time.""" + return { + 'name': f"{order.shipping_first_name} {order.shipping_last_name}", + 'company': order.shipping_company, + 'address': order.shipping_address, + 'city': order.shipping_city, + 'postal_code': order.shipping_postal_code, + 'country': order.shipping_country, + 'vat_number': order.customer_vat_number + } +``` + +--- + +## PDF Generation + +### Using WeasyPrint + +```python +# app/services/invoice_pdf_service.py + +from weasyprint import HTML, CSS +from jinja2 import Environment, FileSystemLoader + +class InvoicePDFService: + """Generate PDF invoices.""" + + def __init__(self, template_dir: str = 'app/templates/invoices'): + self.env = Environment(loader=FileSystemLoader(template_dir)) + + def generate_pdf(self, invoice: Invoice) -> bytes: + """Generate PDF bytes from invoice.""" + template = self.env.get_template('invoice.html') + + html_content = template.render( + invoice=invoice, + seller=invoice.seller_details, + buyer=invoice.buyer_details, + items=invoice.line_items['items'], + vat_label=self._get_vat_label(invoice.vat_regime) + ) + + pdf_bytes = HTML(string=html_content).write_pdf( + stylesheets=[CSS(filename='app/static/css/invoice.css')] + ) + + return pdf_bytes + + def _get_vat_label(self, regime: str) -> str: + """Human-readable VAT regime label.""" + labels = { + 'domestic': 'TVA Luxembourg', + 'oss': 'TVA (OSS - pays de destination)', + 'reverse_charge': 'Autoliquidation (Reverse Charge)', + 'origin': 'TVA pays d\'origine' + } + return labels.get(regime, 'TVA') +``` + +### Invoice HTML Template + +```html + + + + + + + + +
+
FACTURE
+
+ {{ invoice.invoice_number }}
+ Date: {{ invoice.invoice_date.strftime('%d/%m/%Y') }} +
+
+ +
+
+
De:
+ {{ seller.company_name }}
+ {{ seller.address }}
+ {{ seller.postal_code }} {{ seller.city }}
+ {{ seller.country }}
+ {% if seller.vat_number %}TVA: {{ seller.vat_number }}{% endif %} +
+
+
Facturé à:
+ {{ buyer.name }}
+ {% if buyer.company %}{{ buyer.company }}
{% endif %} + {{ buyer.address }}
+ {{ buyer.postal_code }} {{ buyer.city }}
+ {{ buyer.country }}
+ {% if buyer.vat_number %}TVA: {{ buyer.vat_number }}{% endif %} +
+
+ + {% if invoice.order_id %} +

Référence commande: {{ invoice.order_id }}

+ {% endif %} + + + + + + + + + + + + {% for item in items %} + + + + + + + {% endfor %} + +
DescriptionQtéPrix unit. HTTotal HT
{{ item.description }}{% if item.sku %} ({{ item.sku }}){% endif %}{{ item.quantity }}€{{ "%.2f"|format(item.unit_price_net) }}€{{ "%.2f"|format(item.line_total_net) }}
+ + + + + + + + + + + + + + +
Sous-total HT:€{{ "%.2f"|format(invoice.subtotal_net) }}
{{ vat_label }} ({{ invoice.vat_rate }}%):€{{ "%.2f"|format(invoice.vat_amount) }}
TOTAL TTC:€{{ "%.2f"|format(invoice.total_gross) }}
+ + {% if invoice.vat_regime == 'reverse_charge' %} +
+ Autoliquidation de la TVA
+ En application de l'article 196 de la directive 2006/112/CE, la TVA est due par le preneur. +
+ {% elif invoice.vat_regime == 'oss' %} +
+ Régime OSS (One-Stop-Shop)
+ TVA calculée selon le taux du pays de destination ({{ invoice.destination_country }}). +
+ {% endif %} + + + + +``` + +--- + +## API Endpoints + +```python +# app/api/v1/vendor/invoices.py + +@router.post("/orders/{order_id}/invoice") +async def create_invoice_from_order( + order_id: UUID, + vendor: Vendor = Depends(get_current_vendor), + db: Session = Depends(get_db) +): + """Generate invoice for an order.""" + service = InvoiceService(db, VATService(db)) + invoice = service.create_invoice_from_order(order_id, vendor.id) + return InvoiceResponse.from_orm(invoice) + +@router.get("/invoices/{invoice_id}/pdf") +async def download_invoice_pdf( + invoice_id: UUID, + vendor: Vendor = Depends(get_current_vendor), + db: Session = Depends(get_db) +): + """Download invoice as PDF.""" + invoice = db.query(Invoice).filter( + Invoice.id == invoice_id, + Invoice.vendor_id == vendor.id + ).first() + + if not invoice: + raise HTTPException(404, "Invoice not found") + + pdf_service = InvoicePDFService() + pdf_bytes = pdf_service.generate_pdf(invoice) + + return Response( + content=pdf_bytes, + media_type="application/pdf", + headers={ + "Content-Disposition": f"attachment; filename={invoice.invoice_number}.pdf" + } + ) + +@router.get("/invoices") +async def list_invoices( + vendor: Vendor = Depends(get_current_vendor), + db: Session = Depends(get_db), + skip: int = 0, + limit: int = 50 +): + """List all invoices for vendor.""" + invoices = db.query(Invoice).filter( + Invoice.vendor_id == vendor.id + ).order_by(Invoice.invoice_date.desc()).offset(skip).limit(limit).all() + + return [InvoiceResponse.from_orm(inv) for inv in invoices] +``` + +--- + +## UI Integration + +### Order Detail - Invoice Button + +```html + +{% if not order.invoice %} + +{% else %} + + + Download Invoice ({{ order.invoice.invoice_number }}) + +{% endif %} +``` + +### Vendor Settings - VAT Configuration + +```html + +
+

Invoice Settings

+ +
+ + +
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + Check this if you're registered to report EU VAT through the OSS system +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+``` + +--- + +## Implementation Effort + +| Component | Estimate | +|-----------|----------| +| Database migrations | 0.5 day | +| VAT rates seed data | 0.5 day | +| VATService | 1 day | +| InvoiceService | 1 day | +| PDF generation | 1.5 days | +| API endpoints | 0.5 day | +| UI (settings + order button) | 1 day | +| Testing | 1 day | +| **Total** | **~7 days** | + +--- + +## Future Enhancements + +1. **VIES VAT Validation** - Verify B2B VAT numbers via EU API +2. **Credit Notes** - For returns/refunds +3. **Batch Invoice Generation** - Generate for multiple orders +4. **Email Delivery** - Send invoice PDF by email +5. **Accounting Export** - CSV/XML for accounting software diff --git a/docs/marketing/pricing.md b/docs/marketing/pricing.md index ae8b3517..8ab950ef 100644 --- a/docs/marketing/pricing.md +++ b/docs/marketing/pricing.md @@ -1,5 +1,13 @@ # Pricing Strategy +## Product Positioning + +**"Lightweight OMS for Letzshop Sellers"** + +A focused Order Management System that works alongside Letzshop, not instead of it. Provides the operational tools Letzshop lacks: real inventory, correct invoicing, customer ownership. + +--- + ## Market Context ### Luxembourg E-commerce Landscape @@ -11,151 +19,140 @@ ### Competitor Reference - Letzshop charges vendors ~15% commission on sales -- Enterprise solutions (Shopify Plus, etc.): EUR 300-2000+/month -- Local agencies: EUR 500-3000 for custom integrations +- Enterprise OMS (SAP, NetSuite): EUR 500-5000+/month +- Local agencies for custom integrations: EUR 5,000-20,000 +- Spreadsheets: Free but error-prone and time-consuming --- ## Pricing Tiers -### Starter - EUR 49/month -**Target:** Small vendors, 1-50 orders/month +### Essential - EUR 49/month -| Included | Limit | -|----------|-------| +**Target:** Solo vendors, getting started, Letzshop-focused + +| Feature | Limit | +|---------|-------| | Letzshop Order Sync | Up to 100 orders/month | -| Order Confirmation/Rejection | Unlimited | -| Tracking Number Sync | Unlimited | -| Product Export (CSV) | 3 exports/month | -| Team Members | 2 users | -| Email Support | 48h response | +| Order Confirmation & Tracking | Included | +| Inventory Management | Up to 200 products | +| Basic Invoice (Luxembourg VAT) | Included | +| Customer List | View only | +| Team Members | 1 user | +| Order History | 6 months | +| Email Support | 72h response | -**Value Proposition:** "Stop copying orders manually. Save 5+ hours/month." +**Value Proposition:** "Stop the spreadsheet chaos. Your orders, inventory, and invoices in one place." -**ROI Calculation:** -- Time saved: 5 hours/month at EUR 25/hour = EUR 125 value -- Price: EUR 49 -- **Net savings: EUR 76/month** +**Upgrade Triggers:** +- Exceeds 100 orders/month +- Ships to other EU countries (needs VAT invoicing) +- Needs team access +- Has more than 200 products --- ### Professional - EUR 99/month -**Target:** Active vendors, 50-200 orders/month -| Included | Limit | -|----------|-------| -| Everything in Starter | - | +**Target:** Active multi-channel vendors, shipping EU-wide + +| Feature | Limit | +|---------|-------| | Letzshop Order Sync | Up to 500 orders/month | -| Product Import (CSV) | Unlimited | -| Product Export (CSV) | Unlimited | -| Inventory Management | Full access | -| Order Item Exceptions | Full resolution tools | -| Team Members | 5 users | -| Team Roles & Permissions | Full access | +| Order Confirmation & Tracking | Included | +| Inventory Management | Unlimited products | +| **EU VAT Invoices** | Correct VAT for any EU country | +| **Product Locations** | Warehouse/bin tracking | +| **Incoming Stock / Purchase Orders** | Track what's on order | +| **Customer Export** | CSV for Mailchimp, marketing | +| Team Members | 3 users | +| Order History | 24 months | | Priority Email Support | 24h response | -**Value Proposition:** "Complete Letzshop automation + team collaboration." +**Value Proposition:** "Professional operations. Correct invoices. Know your customers." **ROI Calculation:** -- Time saved: 15 hours/month at EUR 25/hour = EUR 375 value -- Prevented order errors: ~EUR 50/month +- Time saved: 10+ hours/month at EUR 25/hour = EUR 250 value +- Accountant VAT corrections avoided: EUR 50/month - Price: EUR 99 -- **Net savings: EUR 326/month** +- **Net value: EUR 200+/month** + +**Upgrade Triggers:** +- Exceeds 500 orders/month +- Multiple Letzshop vendor accounts +- Needs API for integrations +- Team grows beyond 3 --- ### Business - EUR 199/month -**Target:** High-volume vendors, 200+ orders/month -| Included | Limit | -|----------|-------| -| Everything in Professional | - | +**Target:** High-volume vendors, teams, multiple operations + +| Feature | Limit | +|---------|-------| | Letzshop Order Sync | Unlimited orders | -| Customer Messaging | Full inbox | -| Analytics Dashboard | Advanced metrics | -| Team Members | 15 users | +| Everything in Professional | Included | +| **Multi-Vendor Support** | Multiple Letzshop accounts | +| **API Access** | For custom integrations | +| **Accounting Export** | CSV/XML for accounting software | +| Team Members | 10 users | +| Team Roles & Permissions | Full access control | +| Order History | Unlimited | | Phone Support | Available | -| Dedicated Onboarding | Included | +| Dedicated Onboarding | 2-hour session included | -**Value Proposition:** "Enterprise-grade automation at SMB pricing." +**Value Proposition:** "Enterprise-grade OMS at SMB pricing." --- ### Enterprise - Custom Pricing -**Target:** Multi-vendor companies, special requirements -| Included | Notes | -|----------|-------| -| Everything in Business | - | -| Multiple Vendor Accounts | Consolidated billing | -| Custom Integrations | API development | +**Target:** Large operations, special requirements + +| Feature | Notes | +|---------|-------| +| Everything in Business | Included | +| Unlimited Team Members | As needed | +| Custom Integrations | Built to spec | | SLA Guarantee | 99.9% uptime | | Dedicated Account Manager | Named contact | -| Custom Reporting | Built to spec | +| On-Premise Option | If required | **Starting at EUR 499/month** - Contact for quote --- -## Add-On Services +## Feature Comparison Matrix -### One-Time Services - -| Service | Price | Description | -|---------|-------|-------------| -| Onboarding & Training | EUR 199 | 2-hour session + setup assistance | -| Data Migration | EUR 299 | Import existing products/orders | -| Custom CSV Mapping | EUR 149 | Map your specific CSV format | - -### Monthly Add-Ons - -| Add-On | Price | Description | -|--------|-------|-------------| -| Additional Marketplace | +EUR 29/mo | Per additional marketplace | -| Extra Team Members | +EUR 5/user/mo | Beyond plan limit | -| API Access | +EUR 49/mo | For custom integrations | - ---- - -## For Vendors with Own Websites - -### Scenario A: Keep Existing Website + Add Automation - -For vendors who want to keep their WooCommerce/Shopify/custom site but need Letzshop integration. - -**Connector Service - EUR 149/month** - -| Feature | Description | -|---------|-------------| -| Bidirectional Order Sync | Website <-> Letzshop | -| Inventory Sync | Real-time stock levels | -| Product Sync | Push products to Letzshop | -| Unified Dashboard | See all orders in one place | - -**One-Time Setup: EUR 499-999** (depending on platform complexity) - -Supported Platforms: -- WooCommerce (WordPress) -- Shopify -- PrestaShop -- Magento (custom quote) -- Custom PHP/API (custom quote) - -### Scenario B: Migrate to Wizamart Platform - -For vendors wanting a complete solution. - -**Migration Package - EUR 799 one-time** - -Includes: -- Full product catalog migration -- Customer data import -- Order history import (last 12 months) -- DNS/domain setup assistance -- 2-week parallel running period -- 3 months at Professional tier - -**Then continues at chosen tier pricing.** +| Feature | Essential | Professional | Business | +|---------|:---------:|:------------:|:--------:| +| **Orders** | +| Letzshop Sync | 100/mo | 500/mo | Unlimited | +| Order Confirmation | ✓ | ✓ | ✓ | +| Tracking Sync | ✓ | ✓ | ✓ | +| Order History | 6 months | 24 months | Unlimited | +| **Inventory** | +| Product Limit | 200 | Unlimited | Unlimited | +| Stock Levels | ✓ | ✓ | ✓ | +| Product Locations | - | ✓ | ✓ | +| Incoming Stock / PO | - | ✓ | ✓ | +| **Invoicing** | +| Luxembourg Invoice | ✓ | ✓ | ✓ | +| EU VAT Invoice | - | ✓ | ✓ | +| Accounting Export | - | - | ✓ | +| **Customers** | +| Customer List | View | View + Export | View + Export | +| **Team** | +| Users | 1 | 3 | 10 | +| Roles & Permissions | - | - | ✓ | +| **Operations** | +| Multi-Vendor | - | - | ✓ | +| API Access | - | - | ✓ | +| **Support** | +| Email | 72h | 24h | 24h | +| Phone | - | - | ✓ | +| Onboarding | - | - | Included | --- @@ -165,39 +162,111 @@ Includes: | Offer | Details | |-------|---------| -| 30-Day Free Trial | Full Professional features | +| 14-Day Free Trial | Full Professional features | | 50% Off First 3 Months | Any tier | | Free Onboarding | With annual commitment | -### Annual Discount +### Annual Discount (2 months free) -| Tier | Monthly | Annual (17% off) | -|------|---------|------------------| -| Starter | EUR 49/mo | EUR 490/year (EUR 41/mo) | +| Tier | Monthly | Annual | +|------|---------|--------| +| Essential | EUR 49/mo | EUR 490/year (EUR 41/mo) | | Professional | EUR 99/mo | EUR 990/year (EUR 82/mo) | | Business | EUR 199/mo | EUR 1,990/year (EUR 166/mo) | --- -## Pricing Psychology Notes +## Add-On Services -1. **Anchor on time saved** - Luxembourg has high labor costs. 5 hours/month at EUR 25/hour = EUR 125. Our EUR 49 plan is "obviously worth it." +### One-Time Services -2. **Professional tier is the target** - Starter exists to make Professional look reasonable. Most vendors should land on Professional. +| Service | Price | Description | +|---------|-------|-------------| +| Onboarding & Training | EUR 149 | 1-hour session + setup assistance | +| Data Migration | EUR 299 | Import existing products/orders from spreadsheets | +| Custom Invoice Template | EUR 99 | Branded invoice design | -3. **No per-order fees** - Unlike Letzshop's commission model, our flat fee means the more they sell, the better the value. +### Monthly Add-Ons -4. **Annual commitment = lower churn** - 17% discount is significant enough to encourage annual. +| Add-On | Price | Description | +|--------|-------|-------------| +| Additional Team Member | +EUR 9/user/mo | Beyond plan limit | +| Additional Vendor Account | +EUR 29/mo | For Professional tier | +| Extended History | +EUR 19/mo | Unlimited history for Essential | -5. **Free trial removes friction** - 30 days is enough to become dependent on the automation. +--- + +## Pricing Psychology + +### Why This Structure Works + +1. **Essential (EUR 49) creates entry point** + - Low enough to try without much deliberation + - Genuinely useful for small operations + - Natural limits trigger upgrades organically + +2. **Professional (EUR 99) is the target tier** + - EU VAT invoicing is the killer feature (compliance fear) + - Most active vendors will need this + - Price feels reasonable for operational software + +3. **Business (EUR 199) provides anchor** + - Makes Professional look affordable + - Captures high-value customers + - Multi-vendor is a clear enterprise need + +4. **No per-order fees** + - Unlike Letzshop's commission model + - The more you sell, the better the value + - Predictable costs for budgeting + +5. **Annual = lower churn** + - 17% discount is meaningful + - 2 months free is easy to understand + - Reduces monthly cancellation opportunity --- ## Competitive Positioning -| Competitor | Their Model | Our Advantage | -|------------|-------------|---------------| -| Letzshop Direct | 15% commission | Flat fee, scales better | -| Manual Process | Free but time-consuming | Automation pays for itself | -| Custom Development | EUR 5,000-20,000 | Ready-to-use, maintained | -| Enterprise Solutions | EUR 300+/month | Luxembourg-focused, simpler | +| Alternative | Their Model | Our Advantage | +|-------------|-------------|---------------| +| Just Letzshop | Free but limited | Real inventory, correct VAT, customer access | +| Spreadsheets | Free but fragile | Automated, fewer errors, scales | +| Enterprise OMS | EUR 500+/month | Right-sized, affordable, Letzshop-specific | +| Custom Development | EUR 5,000-20,000 | Ready now, maintained, supported | +| Accountant fixes VAT | EUR 50-100/month | Built-in, automatic, instant | + +--- + +## Sales Guidance + +### Qualifying Questions + +1. "How many orders do you process per month?" + - <100 → Essential + - 100-500 → Professional + - 500+ → Business + +2. "Do you ship to other EU countries?" + - Yes → Professional minimum (VAT invoicing) + - No → Essential may suffice + +3. "How many people handle orders?" + - 1 → Essential + - 2-3 → Professional + - 4+ → Business + +4. "Do you have multiple Letzshop accounts?" + - Yes → Business + +### Objection Handling + +**"EUR 49 is too much for what I get"** +> "How much time do you spend on manual order entry and spreadsheets each month? At EUR 25/hour, 2 hours of saved time pays for itself." + +**"I can do this with spreadsheets"** +> "You can, until you oversell because inventory wasn't updated, or your accountant flags incorrect VAT. How much does one of those mistakes cost?" + +**"Why do I need Professional for EU invoices?"** +> "EU VAT rules are complex - wrong rate, wrong country, wrong invoice = compliance issues. We handle this automatically so your accountant doesn't have to fix it." diff --git a/docs/marketing/strategy/back-office-positioning.md b/docs/marketing/strategy/back-office-positioning.md new file mode 100644 index 00000000..54955116 --- /dev/null +++ b/docs/marketing/strategy/back-office-positioning.md @@ -0,0 +1,303 @@ +# Strategic Positioning: Lightweight OMS for Letzshop Sellers + +## The Core Insight + +Letzshop vendors don't need another e-commerce platform. They need a **lightweight Order Management System** that works alongside Letzshop, not instead of it. + +**Key realization:** Competing with Letzshop's native features is weak positioning. The value is in providing a proper **OMS layer** that Letzshop lacks - inventory truth, correct invoicing, operational tools. + +## Product Category + +**"Lightweight OMS for Letzshop Sellers"** + +| What It Is | What It's Not | +|------------|---------------| +| Order Management System | E-commerce platform | +| Back-office operations | Customer-facing storefront | +| Works WITH Letzshop | Replaces Letzshop | +| Right-sized for SMB | Enterprise complexity | + +--- + +## Real Pain Points (From Vendor Experience) + +| Pain Point | Current Workaround | Impact | +|------------|-------------------|--------| +| Inventory out of sync across channels | Manual updates, spreadsheets | Overselling, lost sales | +| Need another app to find products | Separate warehouse app, memory | Slow picking, errors | +| Stock not linked to incoming purchases | Mental notes, spreadsheets | False "out of stock" | +| VAT wrong for cross-border EU shipping | Accountant fixes, or ignore | Compliance risk, costs | +| Can't retarget/contact past customers | Letzshop owns the relationship | Lost repeat business | + +--- + +## The Positioning + +### What We Are NOT +- Not a Letzshop replacement +- Not a full e-commerce platform +- Not an enterprise ERP system + +### What We ARE +> **"Lightweight OMS for Letzshop Sellers"** +> +> Letzshop is where you sell. Wizamart is where you manage operations. + +A focused Order Management System for vendors who: +- Sell on Letzshop + other channels (own website, markets, B2B) +- Need professional invoicing with correct VAT +- Want to own their customer relationships +- Need real inventory management, not just Letzshop's view + +--- + +## Target Customer + +**Primary:** Multi-channel Letzshop vendors +- Sell on Letzshop + at least one other channel +- 50-500 orders/month total +- Frustrated with manual sync and spreadsheets +- May have accountant who complains about VAT + +**Secondary:** Growing Letzshop-only vendors +- Planning to expand to other channels +- Want professional operations before they scale +- Need correct invoicing for EU customers + +**NOT targeting:** Pure Letzshop vendors happy with manual processes + +--- + +## Core Feature Set + +### 1. Inventory Truth (Central Stock Management) +**Problem:** Letzshop shows its own stock count, but vendor sells elsewhere too. + +**Solution:** +- Single source of truth for all inventory +- One-way push to Letzshop (keeps it in sync) +- Vendor updates here when selling on other channels +- No complex connectors needed - vendor is the sync + +**Value:** Never oversell. Always know real stock. + +### 2. Product Locations +**Problem:** Vendor has to open another app to find where products are stored. + +**Solution:** +- Location/bin field on each product +- Shows in order picking view +- Print pick lists with locations + +**Value:** Faster fulfillment, fewer errors. + +### 3. Incoming Stock / Purchase Orders +**Problem:** Stock levels don't reflect what's on order from suppliers. + +**Solution:** +``` +In stock: 5 units +Reserved: 2 units +Available: 3 units +On order: 50 units (arriving Jan 15) +─────────────────────── +Available to promise: 53 units +``` + +**Value:** Don't mark products unavailable when restock is days away. + +### 4. Smart VAT Invoicing (Differentiator) +**Problem:** EU VAT depends on destination country. Letzshop invoices don't handle this. + +**Solution:** +- Automatic VAT calculation based on ship-to country +- Handles OSS (One-Stop-Shop) rules +- Generates compliant PDF invoices +- Proper VAT breakdown per rate + +**Value:** Compliance without accountant. Professional documents. + +### 5. Customer Ownership +**Problem:** Letzshop owns the customer relationship. Vendor can't retarget or build loyalty. + +**Solution:** +- All customers in your database +- Order history and lifetime value +- Export for email marketing (Mailchimp, etc.) +- Tagging and segmentation + +**Value:** Build repeat business. Own your customers. + +--- + +## Why This Wins + +### Low Support Overhead +| Feature | External Dependencies | Support Risk | +|---------|----------------------|--------------| +| Inventory management | None | Low | +| Product locations | None | Very Low | +| Purchase orders | None | Low | +| VAT invoicing | None (just rules) | Low | +| Customer export | None | Low | + +No connectors to WooCommerce/Shopify = No "why isn't it syncing?" tickets. + +### High Perceived Value +| Feature | Why It Feels Valuable | +|---------|----------------------| +| Inventory truth | "Finally, one place for everything" | +| VAT invoicing | "My accountant will love this" / fear of compliance | +| Customer ownership | "I can finally email my customers" | + +### Defensible Position +Letzshop is unlikely to build: +- Multi-channel inventory (they want you Letzshop-only) +- Correct cross-border VAT (complex, not their core) +- Customer export (they benefit from owning relationships) + +--- + +## Pricing Tiers + +### Tier Logic: What Drives Upgrades? + +The goal is natural upgrade pressure based on **growth**, not artificial limits. + +| Growth Signal | Triggers Upgrade To | +|---------------|---------------------| +| More orders | Higher tier (volume) | +| Shipping to EU | Pro (VAT invoicing) | +| Team grows | Pro/Business (users) | +| More products | Pro (locations, PO) | +| Multiple shops | Business (multi-vendor) | + +--- + +### Essential - EUR 49/month + +**Target:** Solo vendors, getting started, Letzshop + 1 channel + +| Feature | Limit | +|---------|-------| +| Letzshop Order Sync | Up to 100 orders/month | +| Inventory Management | Up to 200 products | +| Basic Invoices (Luxembourg) | Included | +| Team Members | 1 user | +| Order History | 6 months | +| Email Support | 72h response | + +**Why they upgrade:** Hit order limit, need EU VAT invoices, need team access. + +--- + +### Professional - EUR 99/month + +**Target:** Active vendors, multi-channel, shipping EU-wide + +| Feature | Limit | +|---------|-------| +| Letzshop Order Sync | Up to 500 orders/month | +| Inventory Management | Unlimited products | +| **EU VAT Invoices** | All EU countries | +| **Product Locations** | Warehouse/bin tracking | +| **Incoming Stock / PO** | Track supplier orders | +| **Customer Export** | CSV for marketing | +| Team Members | 3 users | +| Order History | 24 months | +| Priority Email Support | 24h response | + +**Why they upgrade:** Multiple team members, multiple Letzshop accounts, need API. + +--- + +### Business - EUR 199/month + +**Target:** Teams, high volume, multiple vendor accounts + +| Feature | Limit | +|---------|-------| +| Letzshop Order Sync | Unlimited orders | +| Everything in Professional | Included | +| **Multi-Vendor Support** | Multiple Letzshop accounts | +| **API Access** | For custom integrations | +| **Accounting Export** | CSV/XML for accountants | +| Team Members | 10 users | +| Team Roles & Permissions | Full RBAC | +| Order History | Unlimited | +| Phone Support | Available | +| Dedicated Onboarding | Included | + +--- + +### Feature Access by Tier + +| Feature | Essential | Professional | Business | +|---------|:---------:|:------------:|:--------:| +| Letzshop Order Sync | 100/mo | 500/mo | Unlimited | +| Inventory Management | 200 SKU | Unlimited | Unlimited | +| Product Locations | - | ✓ | ✓ | +| Incoming Stock / PO | - | ✓ | ✓ | +| Basic Invoice (LU) | ✓ | ✓ | ✓ | +| EU VAT Invoice | - | ✓ | ✓ | +| Customer List | View | + Export | + Export | +| Team Members | 1 | 3 | 10 | +| Multi-Vendor | - | - | ✓ | +| API Access | - | - | ✓ | +| Order History | 6 mo | 24 mo | Unlimited | + +--- + +### Why This Structure Works + +1. **Essential (EUR 49)** is genuinely useful but has natural limits + - 100 orders/month is ~3/day - fine for small sellers + - Luxembourg-only invoices work if you don't ship EU + - 1 user is fine for solo operation + +2. **Professional (EUR 99)** unlocks the "serious" features + - EU VAT invoicing is the killer upgrade trigger + - Product locations + PO = operational maturity + - 3 users = small team + +3. **Business (EUR 199)** is for scale + - Multi-vendor = manages multiple Letzshop shops + - API = integrates with other systems + - 10 users = real team + +**Price anchoring:** EUR 99 is the target tier. EUR 49 exists to capture price-sensitive leads. EUR 199 exists to make EUR 99 look reasonable. + +--- + +## Competitive Positioning + +| Alternative | Their Limitation | Our Advantage | +|-------------|------------------|---------------| +| Just use Letzshop | No multi-channel inventory, wrong VAT, no customer access | Complete back-office | +| Spreadsheets | Error-prone, no automation, time-consuming | Professional, synced | +| Full ERP (SAP, Odoo) | Overkill, expensive, complex | Right-sized, affordable | +| Accountant fixes VAT | EUR 50-100/month for corrections | Built-in, automatic | + +--- + +## Go-to-Market + +### Message Hierarchy + +**Primary:** "The back-office Letzshop doesn't give you" + +**Supporting:** +1. "One inventory for all your channels" +2. "Invoices with correct VAT, any EU country" +3. "Your customers, your data" + +### Initial Outreach Target +Vendors who: +- Have their own website AND Letzshop presence +- Ship to multiple EU countries +- Have complained about Letzshop limitations + +### Proof Points to Gather +- Time saved per week on manual sync +- VAT corrections avoided +- Overselling incidents prevented diff --git a/docs/marketing/strategy/customer-marketing-positioning.md b/docs/marketing/strategy/customer-marketing-positioning.md new file mode 100644 index 00000000..4dc90f4c --- /dev/null +++ b/docs/marketing/strategy/customer-marketing-positioning.md @@ -0,0 +1,272 @@ +# Customer Ownership & Marketing Positioning + +## The Problem + +Letzshop owns the customer relationship. Vendors: +- Can't export customer emails easily +- Can't send marketing campaigns +- Can't retarget past buyers +- Lose repeat business to marketplace algorithm + +**Result:** Vendors acquire customers through Letzshop but can't turn them into loyal, direct customers. + +--- + +## What "Customer Ownership" Could Look Like + +### Level 1: Data Export (Simple) + +**What it is:** Export customer list with order history to CSV. + +``` +┌────────────────────────────────────────────┐ +│ Wizamart Dashboard │ +│ ┌──────────────────────────────────────┐ │ +│ │ Customers (142) [Export CSV] │ │ +│ ├──────────────────────────────────────┤ │ +│ │ Marie Dupont 3 orders €245 │ │ +│ │ Jean Martin 1 order €89 │ │ +│ │ Sophie Weber 5 orders €512 │ │ +│ └──────────────────────────────────────┘ │ +└────────────────────────────────────────────┘ + │ + ▼ Export +┌────────────────────────────────────────────┐ +│ email,name,orders,total_spent,last_order │ +│ marie@...,Marie Dupont,3,245,2024-12-01 │ +│ jean@...,Jean Martin,1,89,2024-11-15 │ +└────────────────────────────────────────────┘ + │ + ▼ Import manually +┌────────────────────────────────────────────┐ +│ Mailchimp / Brevo / etc. │ +└────────────────────────────────────────────┘ +``` + +**Effort:** ~2 days (you already have customer data from orders) +**Support overhead:** Very low +**Value:** Medium - manual but functional + +--- + +### Level 2: Smart Segmentation (Medium) + +**What it is:** Tag customers, filter by behavior, export segments. + +``` +┌────────────────────────────────────────────┐ +│ Customer Segments │ +│ ┌──────────────────────────────────────┐ │ +│ │ 🏆 VIP (5+ orders) 23 │ │ +│ │ 😴 Dormant (no order 90d) 45 │ │ +│ │ 🆕 New (first order) 12 │ │ +│ │ 🇩🇪 Germany shipping 34 │ │ +│ │ 🎁 Holiday buyers 2023 28 │ │ +│ └──────────────────────────────────────┘ │ +│ │ +│ [Export Segment to CSV] │ +└────────────────────────────────────────────┘ +``` + +**Features:** +- Auto-tagging based on behavior (VIP, dormant, new) +- Manual tagging (wholesale, problem customer) +- Filter by: country, order count, total spent, last order date +- Export filtered lists + +**Effort:** ~1 week +**Support overhead:** Low +**Value:** High - actionable data + +--- + +### Level 3: Mailchimp Integration (Complex) + +**What it is:** Direct sync with Mailchimp - customers auto-sync, segments sync, tags sync. + +``` +┌─────────────┐ ┌─────────────┐ +│ Wizamart │ ──────► │ Mailchimp │ +│ │ Sync │ │ +│ Customers │ ◄────── │ Audiences │ +│ Orders │ │ Tags │ +│ Segments │ │ Campaigns │ +└─────────────┘ └─────────────┘ +``` + +**API Integration Required:** +- Mailchimp Marketing API v3 +- OAuth 2.0 for account connection +- Webhook for unsubscribes (GDPR) +- Audience sync (create/update members) +- Tag sync (apply tags from segments) + +**Effort:** ~2-3 weeks +**Support overhead:** MEDIUM-HIGH + - "Why aren't my contacts syncing?" + - "My tags aren't showing up" + - Mailchimp API changes / deprecations + - GDPR compliance (unsubscribe sync) + +**Value:** High - but is it 2-3 weeks of value? + +--- + +### Level 4: Built-in Email Marketing (Very Complex) + +**What it is:** Send emails directly from Wizamart. + +**Required:** +- Email template builder +- Email sending infrastructure (SendGrid, SES, Postmark) +- Deliverability management (SPF, DKIM, DMARC setup) +- Bounce/complaint handling +- Unsubscribe management +- Open/click tracking +- A/B testing +- Scheduling +- Campaign analytics + +**Effort:** 2-3 months (to do it well) +**Support overhead:** VERY HIGH + - "My emails go to spam" + - "Why didn't my campaign send?" + - Deliverability issues + - GDPR compliance + +**Value:** Very high - but you're building Mailchimp + +**Verdict:** Don't do this. Use existing tools. + +--- + +## Comparison: Back-Office vs Marketing Positioning + +### Option A: Back-Office Focus (Invoicing, Inventory, Operations) + +| Aspect | Assessment | +|--------|------------| +| **Core message** | "Run your business better" | +| **Primary features** | VAT invoicing, inventory truth, locations, incoming stock | +| **Target emotion** | Fear (compliance), relief (organization) | +| **Competitive moat** | VAT expertise, Letzshop-specific | +| **Support overhead** | Low (self-contained systems) | +| **Development effort** | ~2-3 weeks for MVP | +| **Revenue model** | Subscription (necessary tool) | + +**Customer type:** Operations-focused, compliance-worried + +### Option B: Marketing Focus (Customer Ownership, Retargeting) + +| Aspect | Assessment | +|--------|------------| +| **Core message** | "Grow your business" | +| **Primary features** | Customer CRM, segmentation, Mailchimp sync | +| **Target emotion** | Ambition (growth), frustration (Letzshop limits) | +| **Competitive moat** | Weak (many CRM tools exist) | +| **Support overhead** | Medium-High (external integrations) | +| **Development effort** | ~3-4 weeks for meaningful features | +| **Revenue model** | Subscription (growth tool) | + +**Customer type:** Growth-focused, marketing-savvy + +--- + +## The Strategic Question + +### Who is easier to sell to? + +| Customer Type | Buys Because | Objection | +|---------------|--------------|-----------| +| **Operations person** | "I need this to not mess up" | "Is it really necessary?" | +| **Growth person** | "This will make me money" | "Will it actually work?" | + +### Which has stickier retention? + +| Tool Type | Stickiness | Why | +|-----------|------------|-----| +| **Back-office** | HIGH | Your invoices, inventory data - hard to migrate | +| **Marketing** | MEDIUM | Customer list is portable, can switch tools | + +### Which is simpler to support? + +| Tool Type | External Dependencies | Support Risk | +|-----------|----------------------|--------------| +| **Back-office** | None | LOW | +| **Marketing** | Mailchimp API, email delivery | MEDIUM-HIGH | + +--- + +## My Recommendation + +### Start with Back-Office, Add Customer Export + +**Phase 1 (Now):** Back-office tools +- VAT invoicing (differentiator, compliance fear) +- Inventory management (already built) +- Product locations (simple, useful) +- Incoming stock/PO (nice to have) +- **Customer export to CSV** (satisfies marketing need without complexity) + +**Phase 2 (Later, if demand):** Marketing enhancement +- Smart segmentation +- Mailchimp sync (only if customers ask for it specifically) + +### Why This Order? + +1. **Back-office is a "must-have"** - vendors need correct invoices +2. **Marketing is a "nice-to-have"** - they can use Mailchimp directly with CSV export +3. **Lower risk** - no external API dependencies to start +4. **Faster to market** - VAT invoicing is ~1 week, Mailchimp is ~3 weeks +5. **Clearer positioning** - "The back-office Letzshop doesn't give you" + +### The Hybrid Pitch + +> "Wizamart gives you the tools Letzshop doesn't: +> +> - **Correct invoices** with proper VAT for any EU country +> - **Real inventory** across all your channels +> - **Your customers** - export anytime for your marketing +> +> EUR 49/month. You focus on selling, we handle the back-office." + +The customer export feature satisfies the marketing need without building a marketing platform. + +--- + +## If You Want Marketing Focus Instead + +If you strongly feel marketing is the right angle: + +### Minimum Viable Marketing Product + +1. Customer list with order history ✓ (you have this) +2. Basic tagging (manual + auto VIP/dormant) +3. Segment filtering +4. CSV export with segments +5. **Mailchimp sync** (this is the "magic" feature) + +**Skip:** Built-in email sending, campaign builder, analytics + +**Effort:** ~3 weeks total + +**The pitch becomes:** +> "Own your Letzshop customers. Sync them to Mailchimp automatically. +> Build repeat business without copy-pasting spreadsheets." + +But: This competes with many existing tools. Back-office is more defensible. + +--- + +## Final Comparison Table + +| Factor | Back-Office (Invoicing) | Marketing (Customer CRM) | +|--------|------------------------|--------------------------| +| Uniqueness | HIGH (VAT expertise) | LOW (many CRM tools) | +| Development time | ~2 weeks | ~3-4 weeks | +| Support overhead | LOW | MEDIUM-HIGH | +| External dependencies | NONE | Mailchimp API | +| Stickiness | HIGH | MEDIUM | +| Emotional driver | Fear (compliance) | Ambition (growth) | +| Price sensitivity | LOW (necessary) | HIGH (nice-to-have) | +| **Recommendation** | **START HERE** | Add later if demanded | diff --git a/mkdocs.yml b/mkdocs.yml index 759af6b6..b6fe4a68 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -147,6 +147,8 @@ nav: - Order Item Exceptions: implementation/order-item-exceptions.md - Product Suppliers Table: implementation/product-suppliers-table.md - Unified Order View: implementation/unified-order-view.md + - VAT Invoice Feature: implementation/vat-invoice-feature.md + - OMS Feature Plan: implementation/oms-feature-plan.md - Seed Scripts Audit: development/seed-scripts-audit.md - Database Seeder: - Documentation: development/database-seeder/database-seeder-documentation.md @@ -222,6 +224,9 @@ nav: - Overview: marketing/index.md - Feature List & Roadmap: marketing/features.md - Pricing Strategy: marketing/pricing.md + - Strategy: + - Back-Office Positioning: marketing/strategy/back-office-positioning.md + - Customer & Marketing: marketing/strategy/customer-marketing-positioning.md - Outreach Templates: - Letzshop Automation: marketing/outreach/letzshop-automation.md - Website Vendors: marketing/outreach/website-vendors.md