feat(113): fakturownia integration foundation

Phase 113 complete (v3.7 Invoices):
- DB: invoices, invoice_configs, invoice_number_counters, fakturownia_integration_settings + orders.invoice_requested
- FakturowniaIntegrationRepository (multi-account via integrations.type='fakturownia')
- FakturowniaApiClient (testConnection; createInvoice/downloadPdf STUBs)
- IntegrationsRepository::updateTestResult() (reusable test-result writer)
- /settings/integrations/fakturownia (list + edit + test + delete)
- Karta Fakturownia w hubie /settings/integrations

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 22:11:55 +02:00
parent 322b23b7be
commit 2382018739
20 changed files with 1766 additions and 32 deletions

View File

@@ -1,6 +1,6 @@
# Database Schema
**Updated:** 2026-04-28 | **Total tables:** 55 | **Engine:** InnoDB | **Charset:** utf8mb4_unicode_ci
**Updated:** 2026-05-10 | **Total tables:** 59 | **Engine:** InnoDB | **Charset:** utf8mb4_unicode_ci
---
@@ -263,7 +263,7 @@ UNIQUE: `(integration_id, external_product_id, external_variant_id)`
| `delivery_method` | VARCHAR(128) | YES | |
| `delivery_price` | DECIMAL(12,2) | YES | |
| `delivery_tracking_number` | VARCHAR(128) | YES | |
| `notes` | TEXT | YES | |
| `invoice_requested` | TINYINT(1) | NO | DEFAULT 0 (Phase 113-01) |
| `external_created_at` | DATETIME | YES | |
| `external_updated_at` | DATETIME | YES | |
| `last_status_checked_at` | DATETIME | YES | |
@@ -274,6 +274,8 @@ UNIQUE: `(integration_id, external_product_id, external_variant_id)`
UNIQUE: `(integration_id, external_order_id)`
> Note: Order notes are stored in the separate `order_notes` table (no `notes` column on `orders`).
**order_items** — Line items within orders
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
@@ -556,6 +558,21 @@ UNIQUE: `(type, name)`
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**fakturownia_integration_settings** — Fakturownia account credentials (Phase 113-01; multi-account via integration_id)
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `integration_id` | INT UNSIGNED | NO | UNIQUE, FK → integrations(id) CASCADE |
| `account_prefix` | VARCHAR(64) | NO | Subdomain: {prefix}.fakturownia.pl |
| `api_token_encrypted` | TEXT | YES | AES-encrypted via `IntegrationSecretCipher` |
| `department_id` | VARCHAR(64) | YES | Optional Fakturownia department |
| `default_kind` | VARCHAR(32) | NO | DEFAULT 'vat' |
| `default_payment_to_days` | TINYINT UNSIGNED | NO | DEFAULT 7 |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
UNIQUE: `(integration_id)` — one settings row per Fakturownia integration. Multiple integrations of `type='fakturownia'` allowed.
---
## Accounting / Receipts
@@ -605,6 +622,62 @@ UNIQUE: `(config_id, year, month)`
---
## Invoices (Phase 113-01)
**invoice_configs** — Invoice generation configurations (analogous to `receipt_configs` plus delegation flag)
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `name` | VARCHAR(128) | NO | |
| `integration_id` | INT UNSIGNED | YES | FK → integrations(id) SET NULL — points to Fakturownia account when delegated |
| `is_delegated` | TINYINT(1) | NO | DEFAULT 0 — when 1, invoice creation calls Fakturownia API |
| `is_active` | TINYINT(1) | NO | DEFAULT 1 |
| `number_format` | VARCHAR(64) | NO | DEFAULT 'FV/%N/%M/%Y' |
| `numbering_type` | ENUM('monthly','yearly') | NO | DEFAULT 'monthly' |
| `sale_date_source` | ENUM('order_date','payment_date','issue_date') | NO | DEFAULT 'issue_date' |
| `order_reference` | ENUM('none','orderpro','integration') | NO | DEFAULT 'none' |
| `payment_to_days` | TINYINT UNSIGNED | NO | DEFAULT 7 |
| `default_kind` | VARCHAR(32) | NO | DEFAULT 'vat' (vat/proforma/etc) |
| `created_at` | DATETIME | NO | |
| `updated_at` | DATETIME | NO | |
**invoices** — Generated invoices (snapshot pattern like `receipts`, plus external linkage when delegated)
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `order_id` | BIGINT UNSIGNED | NO | FK → orders(id) CASCADE |
| `config_id` | INT UNSIGNED | NO | FK → invoice_configs(id) RESTRICT |
| `invoice_number` | VARCHAR(64) | NO | UNIQUE |
| `issue_date` | DATETIME | NO | |
| `sale_date` | DATETIME | NO | |
| `payment_due_date` | DATETIME | YES | |
| `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 | |
| `external_invoice_id` | VARCHAR(128) | YES | Fakturownia invoice id when delegated |
| `external_pdf_url` | VARCHAR(500) | YES | URL returned by Fakturownia |
| `kind` | VARCHAR(32) | NO | DEFAULT 'vat' |
| `created_by` | INT UNSIGNED | YES | |
| `created_at` | DATETIME | NO | |
Indexes: `invoices_number_unique`, `invoices_order_idx`, `invoices_config_date_idx (config_id, issue_date)`, `invoices_external_idx`
**invoice_number_counters** — Sequential numbering per config/period (mirrors `receipt_number_counters`)
| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `id` | INT UNSIGNED | NO | PK |
| `config_id` | INT UNSIGNED | NO | FK → invoice_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