Files
orderPRO/.paul/codebase/db_schema.md
Jacek Pyziak 0b4ffb7146 feat(110): statistics summary
Phase 110 complete:
- add Statistics -> Podsumowanie page
- add monthly order count and value charts per integration plus total
- use Chart.js with table fallback and 04-2026 default history start
- update PAUL and DOCS technical documentation
2026-04-28 22:48:31 +02:00

864 lines
33 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.
# Database Schema
**Updated:** 2026-04-28 | **Total tables:** 55 | **Engine:** InnoDB | **Charset:** utf8mb4_unicode_ci
---
## Auth / Users
**users** — System user accounts with authentication
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK, AUTO_INCREMENT |
| `name` | VARCHAR(120) | NO | |
| `email` | VARCHAR(190) | NO | UNIQUE |
| `password_hash` | VARCHAR(255) | NO | |
| `remember_token` | VARCHAR(255) | YES | SHA256 of cookie token |
| `created_at` | DATETIME | NO | DEFAULT CURRENT_TIMESTAMP |
---
## Products
**products** — Main product catalog
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `uuid` | CHAR(36) | NO | UNIQUE |
| `type` | ENUM('simple','variant_parent') | NO | DEFAULT 'simple' |
| `sku` | VARCHAR(128) | YES | UNIQUE |
| `ean` | VARCHAR(32) | YES | |
| `status` | TINYINT(1) | NO | DEFAULT 1 |
| `promoted` | TINYINT(1) | NO | DEFAULT 0 |
| `new_to_date` | DATE | YES | |
| `additional_message` | TINYINT(1) | NO | DEFAULT 0 |
| `additional_message_required` | TINYINT(1) | NO | DEFAULT 0 |
| `additional_message_text` | TEXT | YES | |
| `vat` | DECIMAL(5,2) | YES | |
| `weight` | DECIMAL(10,3) | YES | |
| `price_brutto` | DECIMAL(12,2) | NO | DEFAULT 0.00 |
| `price_brutto_promo` | DECIMAL(12,2) | YES | |
| `price_netto` | DECIMAL(12,2) | YES | |
| `price_netto_promo` | DECIMAL(12,2) | YES | |
| `quantity` | DECIMAL(12,3) | NO | DEFAULT 0.000 |
| `producer_id` | INT UNSIGNED | YES | |
| `producer_name` | VARCHAR(255) | YES | |
| `product_unit_id` | INT UNSIGNED | YES | |
| `custom_fields_json` | TEXT | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | ON UPDATE CURRENT_TIMESTAMP |
| `deleted_at` | DATETIME | YES | Soft delete |
Indexes: `products_status_idx`, `products_type_idx`, `products_updated_at_idx`, `products_ean_idx`
**product_translations** — Localized product names/descriptions
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `product_id` | INT UNSIGNED | NO | FK → products(id) CASCADE |
| `lang` | VARCHAR(8) | NO | |
| `name` | VARCHAR(255) | NO | |
| `short_description` | TEXT | YES | |
| `description` | LONGTEXT | YES | |
| `meta_title` | VARCHAR(255) | YES | |
| `meta_description` | VARCHAR(255) | YES | |
| `meta_keywords` | VARCHAR(255) | YES | |
| `seo_link` | VARCHAR(255) | YES | |
| `security_information` | MEDIUMTEXT | YES | GPSR data |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
UNIQUE: `(product_id, lang)`
**product_images** — Product image storage references
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `product_id` | INT UNSIGNED | NO | FK → products(id) CASCADE |
| `storage_path` | VARCHAR(255) | NO | |
| `alt` | VARCHAR(255) | YES | |
| `sort_order` | INT | NO | DEFAULT 0 |
| `is_main` | TINYINT(1) | NO | DEFAULT 0 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**product_categories** — Productcategory associations (m2m)
| Column | Type | Notes |
|--------|------|-------|
| `product_id` | INT UNSIGNED | FK → products(id) CASCADE |
| `category_id` | INT UNSIGNED | |
| `created_at` | DATETIME | |
PK: `(product_id, category_id)`
**product_variants** — Variants for `variant_parent` products
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `product_id` | INT UNSIGNED | NO | FK → products(id) CASCADE |
| `permutation_hash` | VARCHAR(191) | NO | |
| `sku` | VARCHAR(128) | YES | UNIQUE |
| `ean` | VARCHAR(32) | YES | |
| `status` | TINYINT(1) | NO | DEFAULT 1 |
| `stock_0_buy` | TINYINT(1) | NO | DEFAULT 0 |
| `price_brutto` | DECIMAL(12,2) | YES | |
| `price_netto` | DECIMAL(12,2) | YES | |
| `weight` | DECIMAL(10,3) | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
UNIQUE: `(product_id, permutation_hash)`
**product_variant_attributes** — Variantattribute value mapping
| Column | Type | Notes |
|--------|------|-------|
| `variant_id` | INT UNSIGNED | FK → product_variants(id) CASCADE |
| `attribute_id` | INT UNSIGNED | FK → attributes(id) RESTRICT |
| `value_id` | INT UNSIGNED | FK → attribute_values(id) RESTRICT |
| `created_at` | DATETIME | |
PK: `(variant_id, attribute_id)`
**attributes** — Attribute definitions (e.g., size, color)
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `type` | TINYINT UNSIGNED | NO | DEFAULT 1 |
| `status` | TINYINT(1) | NO | DEFAULT 1 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**attribute_translations** — Localized attribute names
| Column | Type | Notes |
|--------|------|-------|
| `attribute_id` | INT UNSIGNED | FK → attributes(id) CASCADE |
| `lang` | VARCHAR(8) | |
| `name` | VARCHAR(255) | |
| `created_at` | DATETIME | |
| `updated_at` | DATETIME | |
PK: `(attribute_id, lang)`
**attribute_values** — Possible values per attribute
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `attribute_id` | INT UNSIGNED | NO | FK → attributes(id) CASCADE |
| `status` | TINYINT(1) | NO | DEFAULT 1 |
| `is_default` | TINYINT(1) | NO | DEFAULT 0 |
| `impact_on_price` | DECIMAL(12,2) | YES | |
| `sort_order` | INT | NO | DEFAULT 0 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**attribute_value_translations** — Localized value names
| Column | Type | Notes |
|--------|------|-------|
| `value_id` | INT UNSIGNED | FK → attribute_values(id) CASCADE |
| `lang` | VARCHAR(8) | |
| `name` | VARCHAR(255) | |
| `created_at` | DATETIME | |
| `updated_at` | DATETIME | |
PK: `(value_id, lang)`
**product_change_log** — Audit trail for product modifications
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `product_id` | INT UNSIGNED | NO | FK → products(id) CASCADE |
| `user_id` | INT UNSIGNED | YES | FK → users(id) SET NULL |
| `change_type` | VARCHAR(64) | NO | |
| `before_json` | JSON | YES | |
| `after_json` | JSON | YES | |
| `created_at` | DATETIME | NO | |
**sales_channels** — Selling channel definitions
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `code` | VARCHAR(64) | NO | UNIQUE |
| `name` | VARCHAR(128) | NO | |
| `type` | VARCHAR(64) | NO | |
| `status` | TINYINT(1) | NO | DEFAULT 1 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**product_channel_map** — Link products to external channel offers
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `product_id` | INT UNSIGNED | NO | FK → products(id) CASCADE |
| `channel_id` | INT UNSIGNED | NO | FK → sales_channels(id) CASCADE |
| `integration_id` | INT UNSIGNED | YES | FK → integrations(id) SET NULL |
| `external_product_id` | VARCHAR(128) | YES | |
| `external_variant_id` | VARCHAR(128) | YES | |
| `sync_state` | VARCHAR(32) | NO | DEFAULT 'not_linked' |
| `link_type` | VARCHAR(32) | NO | DEFAULT 'manual' |
| `link_status` | VARCHAR(32) | NO | DEFAULT 'active' |
| `confidence` | TINYINT UNSIGNED | YES | |
| `linked_at` | DATETIME | YES | |
| `unlinked_at` | DATETIME | YES | |
| `sync_meta_json` | JSON | YES | |
| `last_sync_at` | DATETIME | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
UNIQUE: `(product_id, channel_id, external_product_id, external_variant_id)`
**channel_offers** — External channel offer snapshots
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `integration_id` | INT UNSIGNED | NO | FK → integrations(id) CASCADE |
| `channel_id` | INT UNSIGNED | NO | FK → sales_channels(id) CASCADE |
| `external_product_id` | VARCHAR(128) | NO | |
| `external_variant_id` | VARCHAR(128) | YES | |
| `external_offer_id` | VARCHAR(128) | YES | |
| `name` | VARCHAR(255) | NO | |
| `sku` | VARCHAR(128) | YES | |
| `ean` | VARCHAR(32) | YES | |
| `price_brutto` | DECIMAL(12,2) | YES | |
| `quantity` | DECIMAL(12,3) | YES | |
| `currency` | VARCHAR(8) | YES | |
| `offer_status` | VARCHAR(32) | NO | DEFAULT 'active' |
| `source_updated_at` | DATETIME | YES | |
| `last_seen_at` | DATETIME | NO | |
| `payload_json` | JSON | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
UNIQUE: `(integration_id, external_product_id, external_variant_id)`
**product_link_events** — Audit log for product-channel linking changes
**product_link_alerts** — Alerts for product-channel link issues (ENUM status: `active`, `resolved`)
**product_integration_translations** — Integration-specific product description overrides
---
## Orders
**orders** — Imported orders from sales channels
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `internal_order_number` | VARCHAR(11) | YES | UNIQUE, auto-assigned |
| `integration_id` | INT UNSIGNED | NO | FK → integrations(id) CASCADE |
| `external_order_id` | VARCHAR(64) | NO | |
| `external_order_number` | VARCHAR(128) | YES | |
| `status` | VARCHAR(64) | YES | Internal status code |
| `currency` | CHAR(3) | YES | |
| `total_gross` | DECIMAL(12,2) | YES | |
| `total_net` | DECIMAL(12,2) | YES | |
| `total_with_tax` | DECIMAL(12,2) | YES | |
| `total_paid` | DECIMAL(12,2) | YES | |
| `buyer_email` | VARCHAR(190) | YES | |
| `buyer_name` | VARCHAR(190) | YES | |
| `buyer_phone` | VARCHAR(64) | YES | |
| `payment_method` | VARCHAR(128) | YES | |
| `payment_status` | VARCHAR(64) | YES | |
| `external_payment_type_id` | VARCHAR(128) | YES | |
| `delivery_method` | VARCHAR(128) | YES | |
| `delivery_price` | DECIMAL(12,2) | YES | |
| `delivery_tracking_number` | VARCHAR(128) | YES | |
| `notes` | TEXT | YES | |
| `external_created_at` | DATETIME | YES | |
| `external_updated_at` | DATETIME | YES | |
| `last_status_checked_at` | DATETIME | YES | |
| `payload_json` | JSON | YES | Full raw API payload |
| `fetched_at` | DATETIME | NO | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
UNIQUE: `(integration_id, external_order_id)`
**order_items** — Line items within orders
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `order_id` | INT UNSIGNED | NO | FK → orders(id) CASCADE |
| `external_item_id` | VARCHAR(64) | YES | |
| `name` | VARCHAR(255) | NO | |
| `sku` | VARCHAR(128) | YES | |
| `ean` | VARCHAR(64) | YES | |
| `quantity` | DECIMAL(12,3) | NO | DEFAULT 0 |
| `price_gross` | DECIMAL(12,2) | YES | |
| `price_net` | DECIMAL(12,2) | YES | |
| `vat` | DECIMAL(6,2) | YES | |
| `personalization` | TEXT | YES | Customer personalization data for design generation |
| `project_generated` | TINYINT(1) | NO | DEFAULT 0 |
| `project_generated_at` | DATETIME | YES | |
| `payload_json` | JSON | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**order_activity_log** — Event log for order state changes
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | BIGINT UNSIGNED | NO | PK |
| `order_id` | BIGINT UNSIGNED | NO | FK → orders(id) CASCADE |
| `event_type` | VARCHAR(32) | NO | |
| `summary` | VARCHAR(255) | NO | |
| `details_json` | JSON | YES | |
| `actor_type` | VARCHAR(16) | NO | DEFAULT 'system' |
| `actor_name` | VARCHAR(128) | YES | |
| `created_at` | DATETIME | NO | |
**order_payments** — Payments linked to orders
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `order_id` | INT UNSIGNED | NO | FK → orders(id) CASCADE |
| `source_payment_id` | VARCHAR(64) | YES | |
| `external_payment_id` | VARCHAR(64) | YES | |
| `payment_type_id` | VARCHAR(64) | NO | |
| `payment_date` | DATETIME | YES | |
| `amount` | DECIMAL(12,2) | YES | |
| `currency` | CHAR(3) | YES | |
| `comment` | VARCHAR(255) | YES | |
| `payload_json` | JSON | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
UNIQUE: `(order_id, source_payment_id)`
---
## Order Statuses
**order_status_groups** — Status group categories (e.g., "Nowe", "W realizacji")
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `name` | VARCHAR(120) | NO | |
| `code` | VARCHAR(64) | NO | UNIQUE |
| `color_hex` | CHAR(7) | NO | DEFAULT '#64748b' |
| `sort_order` | INT | NO | DEFAULT 0 |
| `is_active` | TINYINT(1) | NO | DEFAULT 1 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**order_statuses** — Individual statuses within groups
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `group_id` | INT UNSIGNED | NO | FK → order_status_groups(id) CASCADE |
| `name` | VARCHAR(120) | NO | |
| `code` | VARCHAR(64) | NO | UNIQUE |
| `sort_order` | INT | NO | DEFAULT 0 |
| `is_active` | TINYINT(1) | NO | DEFAULT 1 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**order_status_mappings** — Map external (e.g., shopPRO) statuses to internal ones
| Column | Type | Notes |
|--------|------|-------|
| `id` | INT UNSIGNED | PK |
| `integration_id` | INT UNSIGNED | FK → integrations(id) CASCADE |
| `shoppro_status_code` | VARCHAR(64) | |
| `shoppro_status_name` | VARCHAR(128) | |
| `orderpro_status_code` | VARCHAR(64) | |
| `created_at` | DATETIME | |
| `updated_at` | DATETIME | |
UNIQUE: `(integration_id, shoppro_status_code)`
**integration_order_sync_state** — Track order fetch progress per integration
**integration_order_status_sync_state** — Track status sync progress per integration and direction
---
## Shipments & Delivery
**shipment_packages** — Prepared shipments with tracking
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | BIGINT UNSIGNED | NO | PK |
| `order_id` | BIGINT UNSIGNED | NO | FK → orders(id) CASCADE |
| `provider` | VARCHAR(32) | NO | DEFAULT 'allegro_wza' |
| `delivery_method_id` | VARCHAR(128) | YES | |
| `credentials_id` | VARCHAR(128) | YES | |
| `command_id` | VARCHAR(64) | YES | |
| `shipment_id` | VARCHAR(64) | YES | |
| `tracking_number` | VARCHAR(128) | YES | |
| `status` | VARCHAR(32) | NO | DEFAULT 'draft' |
| `delivery_status` | VARCHAR(32) | NO | DEFAULT 'unknown' |
| `delivery_status_raw` | VARCHAR(128) | YES | Provider's original status string |
| `delivery_status_updated_at` | DATETIME | YES | |
| `carrier_id` | VARCHAR(64) | YES | |
| `package_type` | VARCHAR(16) | NO | DEFAULT 'PACKAGE' |
| `weight_kg` | DECIMAL(8,3) | YES | |
| `length_cm` | DECIMAL(8,1) | YES | |
| `width_cm` | DECIMAL(8,1) | YES | |
| `height_cm` | DECIMAL(8,1) | YES | |
| `insurance_amount` | DECIMAL(12,2) | YES | |
| `cod_amount` | DECIMAL(12,2) | YES | |
| `label_format` | VARCHAR(8) | NO | DEFAULT 'PDF' |
| `label_path` | VARCHAR(512) | YES | |
| `receiver_point_id` | VARCHAR(64) | YES | Parcel locker ID |
| `sender_point_id` | VARCHAR(64) | YES | |
| `reference_number` | VARCHAR(128) | YES | |
| `error_message` | TEXT | YES | |
| `payload_json` | JSON | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
Indexes: `shipment_packages_order_idx`, `shipment_packages_status_idx`, `shipment_packages_tracking_idx`, `idx_delivery_status`
**shipment_presets** — Predefined shipment configurations (saved templates)
| Column | Type | Notes |
|--------|------|-------|
| `id` | BIGINT UNSIGNED | PK |
| `name` | VARCHAR(100) | |
| `color` | VARCHAR(7) | DEFAULT '#3b82f6' |
| `carrier` | VARCHAR(32) | |
| `provider_code` | VARCHAR(32) | |
| `delivery_method_id` | VARCHAR(128) | |
| `credentials_id` | VARCHAR(128) | DEFAULT '' |
| `carrier_id` | VARCHAR(64) | DEFAULT '' |
| `package_type` | VARCHAR(16) | DEFAULT 'PACKAGE' |
| `length_cm` | DECIMAL(8,1) | DEFAULT 25.0 |
| `width_cm` | DECIMAL(8,1) | DEFAULT 20.0 |
| `height_cm` | DECIMAL(8,1) | DEFAULT 8.0 |
| `weight_kg` | DECIMAL(8,3) | DEFAULT 1.000 |
| `sender_point_id` | VARCHAR(64) | DEFAULT '' |
| `label_format` | VARCHAR(8) | DEFAULT 'PDF' |
| `sort_order` | INT UNSIGNED | DEFAULT 0 |
| `created_at` | TIMESTAMP | |
| `updated_at` | TIMESTAMP | |
**delivery_statuses** — Normalized delivery status definitions (Phase 108)
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `key` | VARCHAR(50) | NO | UNIQUE |
| `label_pl` | VARCHAR(100) | NO | Polish label |
| `color` | VARCHAR(7) | NO | DEFAULT '#6c757d' |
| `sort_order` | TINYINT UNSIGNED | NO | DEFAULT 0 |
| `is_terminal` | TINYINT(1) | NO | DEFAULT 0 — marks final states |
| `is_system` | TINYINT(1) | NO | DEFAULT 0 — system-managed |
| `created_at` | DATETIME | NO | |
**delivery_status_mappings** — Map provider-specific raw statuses to normalized keys (Phase 108)
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `provider` | VARCHAR(32) | NO | |
| `raw_status` | VARCHAR(64) | NO | |
| `normalized_status` | VARCHAR(32) | NO | FK ref → delivery_statuses.key |
| `description` | VARCHAR(255) | NO | DEFAULT '' |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
UNIQUE: `(provider, raw_status)`
---
## Integrations
**integrations** — Integration configurations for external services
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `type` | VARCHAR(32) | NO | e.g., 'allegro', 'shoppro', 'apaczka', 'inpost' |
| `name` | VARCHAR(128) | NO | |
| `base_url` | VARCHAR(255) | NO | |
| `api_key_encrypted` | TEXT | YES | AES-encrypted |
| `timeout_seconds` | SMALLINT UNSIGNED | NO | DEFAULT 10 |
| `is_active` | TINYINT(1) | NO | DEFAULT 1 |
| `orders_fetch_enabled` | TINYINT(1) | NO | DEFAULT 0 |
| `orders_fetch_start_date` | DATE | YES | |
| `order_status_sync_direction` | VARCHAR(32) | NO | DEFAULT 'shoppro_to_orderpro' |
| `last_test_status` | VARCHAR(16) | YES | |
| `last_test_http_code` | SMALLINT UNSIGNED | YES | |
| `last_test_message` | VARCHAR(255) | YES | |
| `last_test_at` | DATETIME | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
UNIQUE: `(type, name)`
**integration_test_logs** — API test results log
**allegro_integration_settings** — Allegro OAuth tokens and API config
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `integration_id` | INT UNSIGNED | YES | UNIQUE, FK → integrations(id) CASCADE |
| `environment` | VARCHAR(16) | NO | DEFAULT 'sandbox' |
| `client_id` | VARCHAR(128) | YES | |
| `client_secret_encrypted` | TEXT | YES | |
| `redirect_uri` | VARCHAR(255) | YES | |
| `orders_fetch_enabled` | TINYINT(1) | NO | DEFAULT 0 |
| `orders_fetch_start_date` | DATE | YES | |
| `access_token_encrypted` | MEDIUMTEXT | YES | AES-encrypted |
| `refresh_token_encrypted` | MEDIUMTEXT | YES | AES-encrypted |
| `token_type` | VARCHAR(32) | YES | |
| `token_scope` | VARCHAR(255) | YES | |
| `token_expires_at` | DATETIME | YES | |
| `connected_at` | DATETIME | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**allegro_order_status_mappings** — Allegro status → internal status
| Column | Type | Notes |
|--------|------|-------|
| `id` | INT UNSIGNED | PK |
| `allegro_status_code` | VARCHAR(64) | UNIQUE |
| `allegro_status_name` | VARCHAR(120) | |
| `orderpro_status_code` | VARCHAR(64) | |
| `created_at` | DATETIME | |
| `updated_at` | DATETIME | |
**allegro_delivery_method_mappings** — Map order delivery method strings to Allegro services
| Column | Type | Notes |
|--------|------|-------|
| `id` | INT UNSIGNED | PK |
| `order_delivery_method` | VARCHAR(200) | UNIQUE |
| `allegro_delivery_method_id` | VARCHAR(128) | |
| `allegro_credentials_id` | VARCHAR(128) | |
| `allegro_carrier_id` | VARCHAR(128) | |
| `allegro_service_name` | VARCHAR(255) | |
| `created_at` | DATETIME | |
| `updated_at` | DATETIME | |
**apaczka_integration_settings** — Apaczka API credentials
| Column | Type | Notes |
|--------|------|-------|
| `id` | TINYINT UNSIGNED | PK (fixed 1 row) |
| `integration_id` | INT UNSIGNED | UNIQUE, FK → integrations(id) |
| `api_key_encrypted` | TEXT | |
| `created_at` | DATETIME | |
| `updated_at` | DATETIME | |
**inpost_integration_settings** — InPost ShipX settings
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | TINYINT UNSIGNED | NO | PK (fixed 1 row) |
| `integration_id` | INT UNSIGNED | YES | UNIQUE, FK → integrations(id) |
| `api_token_encrypted` | TEXT | YES | |
| `organization_id` | VARCHAR(50) | YES | |
| `environment` | ENUM('sandbox','production') | NO | DEFAULT 'sandbox' |
| `default_dispatch_method` | ENUM('pop','parcel_locker','courier') | NO | DEFAULT 'pop' |
| `default_dispatch_point` | VARCHAR(50) | YES | |
| `default_insurance` | DECIMAL(10,2) | YES | |
| `default_locker_size` | ENUM('small','medium','large') | NO | DEFAULT 'small' |
| `default_courier_length` | SMALLINT UNSIGNED | YES | DEFAULT 20 |
| `default_courier_width` | SMALLINT UNSIGNED | YES | DEFAULT 15 |
| `default_courier_height` | SMALLINT UNSIGNED | YES | DEFAULT 8 |
| `label_format` | ENUM('Pdf','Zpl','Epl') | NO | DEFAULT 'Pdf' |
| `weekend_delivery` | TINYINT(1) | NO | DEFAULT 0 |
| `auto_insurance_value` | TINYINT(1) | NO | DEFAULT 0 |
| `multi_parcel` | TINYINT(1) | NO | DEFAULT 0 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
---
## Accounting / Receipts
**receipt_configs** — Receipt generation configurations
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `name` | VARCHAR(128) | NO | |
| `is_active` | TINYINT(1) | NO | DEFAULT 1 |
| `number_format` | VARCHAR(64) | NO | DEFAULT 'PAR/%N/%M/%Y' |
| `numbering_type` | ENUM('monthly','yearly') | NO | DEFAULT 'monthly' |
| `is_named` | TINYINT(1) | NO | DEFAULT 0 |
| `sale_date_source` | ENUM('order_date','payment_date','issue_date') | NO | DEFAULT 'issue_date' |
| `order_reference` | ENUM('none','orderpro','integration') | NO | DEFAULT 'none' |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**receipts** — Generated receipts / invoices
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `order_id` | BIGINT UNSIGNED | NO | FK → orders(id) CASCADE |
| `config_id` | INT UNSIGNED | NO | FK → receipt_configs(id) RESTRICT |
| `receipt_number` | VARCHAR(64) | NO | UNIQUE |
| `issue_date` | DATETIME | NO | |
| `sale_date` | DATETIME | NO | |
| `seller_data_json` | JSON | NO | Snapshot of company data at issue time |
| `buyer_data_json` | JSON | YES | |
| `items_json` | JSON | NO | |
| `total_net` | DECIMAL(12,2) | NO | DEFAULT 0.00 |
| `total_gross` | DECIMAL(12,2) | NO | DEFAULT 0.00 |
| `order_reference_value` | VARCHAR(128) | YES | |
| `created_by` | INT UNSIGNED | YES | |
| `created_at` | DATETIME | NO | |
**receipt_number_counters** — Sequential numbering per config/period
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `config_id` | INT UNSIGNED | NO | FK → receipt_configs(id) CASCADE |
| `year` | SMALLINT UNSIGNED | NO | |
| `month` | TINYINT UNSIGNED | YES | NULL for yearly numbering |
| `last_number` | INT UNSIGNED | NO | DEFAULT 0 |
UNIQUE: `(config_id, year, month)`
---
## Email
**email_mailboxes** — SMTP mailbox configurations
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `name` | VARCHAR(100) | NO | |
| `smtp_host` | VARCHAR(255) | NO | |
| `smtp_port` | SMALLINT UNSIGNED | NO | DEFAULT 587 |
| `smtp_encryption` | ENUM('tls','ssl','none') | NO | DEFAULT 'tls' |
| `smtp_username` | VARCHAR(255) | NO | |
| `smtp_password_encrypted` | TEXT | NO | AES-encrypted |
| `sender_email` | VARCHAR(255) | NO | |
| `sender_name` | VARCHAR(200) | YES | |
| `html_layout` | LONGTEXT | YES | Wrapper HTML for all emails from this mailbox |
| `is_default` | TINYINT(1) | NO | DEFAULT 0 |
| `is_active` | TINYINT(1) | NO | DEFAULT 1 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**email_templates** — Email message templates with variable placeholders
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `name` | VARCHAR(200) | NO | |
| `subject` | VARCHAR(500) | NO | |
| `body_html` | TEXT | NO | Supports `{{variable}}` placeholders |
| `mailbox_id` | INT UNSIGNED | YES | FK → email_mailboxes(id) SET NULL |
| `attachment1` | LONGTEXT | YES | |
| `is_active` | TINYINT(1) | NO | DEFAULT 1 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**email_logs** — Sent email history
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | BIGINT UNSIGNED | NO | PK |
| `template_id` | INT UNSIGNED | YES | FK → email_templates(id) SET NULL |
| `mailbox_id` | INT UNSIGNED | YES | FK → email_mailboxes(id) SET NULL |
| `order_id` | INT UNSIGNED | YES | |
| `recipient_email` | VARCHAR(255) | NO | |
| `recipient_name` | VARCHAR(200) | YES | |
| `subject` | VARCHAR(500) | NO | |
| `body_html` | TEXT | NO | |
| `attachments_json` | JSON | YES | |
| `status` | ENUM('sent','failed','pending') | NO | DEFAULT 'pending' |
| `error_message` | TEXT | YES | |
| `sent_at` | DATETIME | YES | |
| `created_at` | DATETIME | NO | |
---
## Automation
**automation_rules** — Business rules for order event automation
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `name` | VARCHAR(128) | NO | |
| `event_type` | VARCHAR(64) | NO | e.g., 'order_status_changed', 'order_status_aged' |
| `is_active` | TINYINT(1) | NO | DEFAULT 1 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
Index: `(event_type, is_active)`
**automation_conditions** — Conditions for automation rules
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `rule_id` | INT UNSIGNED | NO | FK → automation_rules(id) CASCADE |
| `condition_type` | VARCHAR(64) | NO | |
| `condition_value` | JSON | NO | |
| `sort_order` | SMALLINT UNSIGNED | NO | DEFAULT 0 |
**automation_actions** — Actions executed when rules trigger
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `rule_id` | INT UNSIGNED | NO | FK → automation_rules(id) CASCADE |
| `action_type` | VARCHAR(64) | NO | e.g., 'send_email', 'update_status', 'create_receipt' |
| `action_config` | JSON | NO | |
| `sort_order` | SMALLINT UNSIGNED | NO | DEFAULT 0 |
**automation_execution_logs** — Audit log for rule executions
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | BIGINT UNSIGNED | NO | PK |
| `event_type` | VARCHAR(64) | NO | |
| `rule_id` | INT UNSIGNED | YES | FK → automation_rules(id) SET NULL |
| `rule_name` | VARCHAR(128) | NO | Snapshot at execution time |
| `order_id` | INT UNSIGNED | NO | FK → orders(id) CASCADE |
| `execution_status` | VARCHAR(16) | NO | |
| `result_message` | VARCHAR(500) | YES | |
| `context_json` | JSON | YES | |
| `executed_at` | DATETIME | NO | |
| `created_at` | DATETIME | NO | |
**automation_email_once_deliveries** — Idempotency guard: email sent-once per rule+action+order (Phase 107)
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | BIGINT UNSIGNED | NO | PK |
| `rule_id` | INT UNSIGNED | NO | FK → automation_rules(id) CASCADE |
| `action_id` | INT UNSIGNED | NO | FK → automation_actions(id) CASCADE |
| `order_id` | BIGINT UNSIGNED | NO | FK → orders(id) CASCADE |
| `created_at` | DATETIME | NO | |
UNIQUE: `(rule_id, action_id, order_id)`
---
## Print Queue
**print_api_keys** — API keys for remote print client authentication
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT | NO | PK |
| `name` | VARCHAR(128) | NO | |
| `key_hash` | VARCHAR(128) | NO | UNIQUE, SHA256 |
| `key_prefix` | VARCHAR(8) | NO | Shown in UI for identification |
| `is_active` | TINYINT(1) | NO | DEFAULT 1 |
| `last_used_at` | DATETIME | YES | |
| `created_at` | DATETIME | NO | |
**print_jobs** — Print queue for remote label printing
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `order_id` | BIGINT UNSIGNED | NO | |
| `package_id` | BIGINT UNSIGNED | NO | |
| `label_path` | VARCHAR(255) | NO | |
| `status` | ENUM('pending','printing','completed','failed') | NO | DEFAULT 'pending' |
| `created_by` | INT UNSIGNED | NO | |
| `created_at` | DATETIME | NO | |
| `completed_at` | DATETIME | YES | |
---
## Cron & Scheduling
**cron_jobs** — Individual cron job queue entries
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `job_type` | VARCHAR(80) | NO | |
| `status` | ENUM('pending','processing','completed','failed','cancelled') | NO | DEFAULT 'pending' |
| `priority` | TINYINT UNSIGNED | NO | DEFAULT 100 |
| `payload` | JSON | YES | |
| `result` | JSON | YES | |
| `attempts` | SMALLINT UNSIGNED | NO | DEFAULT 0 |
| `max_attempts` | SMALLINT UNSIGNED | NO | DEFAULT 3 |
| `last_error` | VARCHAR(500) | YES | |
| `scheduled_at` | DATETIME | NO | |
| `started_at` | DATETIME | YES | |
| `completed_at` | DATETIME | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
Index: `(status, priority, scheduled_at)`
**cron_schedules** — Recurring job definitions
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `job_type` | VARCHAR(80) | NO | UNIQUE |
| `interval_seconds` | INT UNSIGNED | NO | |
| `priority` | TINYINT UNSIGNED | NO | DEFAULT 100 |
| `max_attempts` | SMALLINT UNSIGNED | NO | DEFAULT 3 |
| `payload` | JSON | YES | |
| `enabled` | TINYINT(1) | NO | DEFAULT 1 |
| `last_run_at` | DATETIME | YES | |
| `next_run_at` | DATETIME | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
---
## Settings & Configuration
**app_settings** — Global key-value configuration store
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `setting_key` | VARCHAR(120) | NO | UNIQUE |
| `setting_value` | TEXT | YES | |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
Default keys: `cron_run_on_web`, `cron_web_limit`, `gs1_api_login`, `gs1_prefix`, `products_sku_format`
**company_settings** — Single-record seller/company configuration (always id=1)
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | TINYINT UNSIGNED | NO | PK, always 1 |
| `company_name` | VARCHAR(200) | YES | |
| `person_name` | VARCHAR(200) | YES | |
| `street` | VARCHAR(200) | YES | |
| `city` | VARCHAR(128) | YES | |
| `postal_code` | VARCHAR(16) | YES | |
| `country_code` | CHAR(2) | NO | DEFAULT 'PL' |
| `phone` | VARCHAR(64) | YES | |
| `email` | VARCHAR(128) | YES | |
| `tax_number` | VARCHAR(64) | YES | NIP |
| `bank_account` | VARCHAR(64) | YES | |
| `bank_owner_name` | VARCHAR(200) | YES | |
| `contact_person_first_name` | VARCHAR(100) | YES | |
| `contact_person_last_name` | VARCHAR(100) | YES | |
| `contact_person_phone` | VARCHAR(64) | YES | |
| `contact_person_email` | VARCHAR(128) | YES | |
| `default_package_length_cm` | DECIMAL(8,1) | NO | DEFAULT 25.0 |
| `default_package_width_cm` | DECIMAL(8,1) | NO | DEFAULT 20.0 |
| `default_package_height_cm` | DECIMAL(8,1) | NO | DEFAULT 8.0 |
| `default_package_weight_kg` | DECIMAL(8,3) | NO | DEFAULT 1.000 |
| `default_label_format` | VARCHAR(8) | NO | DEFAULT 'PDF' |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
---
## Design Generation
**project_mappings** — Map product name patterns to graphic generation scripts
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `product_name_pattern` | VARCHAR(255) | NO | Pattern matched against order_items.name |
| `script_name` | VARCHAR(255) | NO | Script filename in tools/generowanie/ |
| `output_dir` | VARCHAR(500) | YES | |
| `is_active` | TINYINT(1) | NO | DEFAULT 1 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
---
## Schema Characteristics
| Property | Value |
|----------|-------|
| Engine | InnoDB (all tables) |
| Charset | utf8mb4_unicode_ci |
| Encrypted columns | `*_encrypted` suffix — AES via `IntegrationSecretCipher` |
| Soft deletes | `products.deleted_at` only |
| Audit via JSON | `payload_json` snapshots in orders, shipments, receipts |
| Migrations | `database/migrations/YYYYMMDD_NNNNNN_description.sql` |
| Deferred indexes | `idx_order_addresses_order_type`, `idx_shipment_packages_order_delivery` — apply at >50k orders |
## Reporting Usage
**Statistics Summary (`/statistics/summary`)** — no dedicated reporting tables.
- Reads existing `orders` rows and groups by month using the same effective order date used by `/statistics/orders`.
- Default summary history starts at April 2026 (`2026-04-01`), even if older rows exist.
- Splits series by channel key: Allegro as one series and each shopPRO integration by `orders.integration_id`.
- Uses `integrations.name` only for display labels when available.
- Filters by selected status groups through `order_status_groups` and `order_statuses`.
- Uses existing gross amount columns via `OrdersStatisticsRepository::grossAmountSql()`.
- No schema migration was introduced for Phase 110.