441 lines
17 KiB
Markdown
441 lines
17 KiB
Markdown
# 04 – Datenmodell (Mapping Doctrine → Eloquent)
|
||
|
||
Dieses Dokument beschreibt das **neue DB-Schema** und wie die Legacy-Tabellen dorthin gemappt werden. Ziel: Saubere Laravel-Konventionen, `portal`-Scoping und Legacy-IDs für Imports.
|
||
|
||
## Gemeinsame Konventionen
|
||
|
||
- Jede Tabelle hat: `id` (BIGINT UNSIGNED AI), `created_at`, `updated_at`.
|
||
- Fachliche Entitäten haben: `deleted_at` (Soft-Delete).
|
||
- Multi-Portal-Entitäten haben zusätzlich: `portal` (ENUM `presseecho`, `businessportal24`, `both` – `both` nur für Stammdaten).
|
||
- Importierte Datensätze haben: `legacy_portal` (ENUM), `legacy_id` (BIGINT), UNIQUE `(legacy_portal, legacy_id)`.
|
||
- FK-Spalten: `*_id` BIGINT UNSIGNED, constrainted, i.d.R. `ON DELETE` wie in Legacy.
|
||
- UUIDs nur dort, wo externe Referenzen nötig (z.B. öffentliche PM-Links): Spalte `uuid`, indiziert.
|
||
|
||
---
|
||
|
||
## 1. Users & Profil
|
||
|
||
### `users` (erweitert das bestehende Schema)
|
||
|
||
| Spalte | Typ | Legacy-Feld | Bemerkung |
|
||
|---|---|---|---|
|
||
| id | BIGINT PK | — | Neu |
|
||
| name | VARCHAR(120) | — | Display-Name |
|
||
| email | VARCHAR(190) UNIQUE | `sfGuardUserProfile.email` | |
|
||
| email_verified_at | TIMESTAMP | – | |
|
||
| password | VARCHAR(255) NULL | – | bcrypt; **null erlaubt** (Magic-Link-Only-User wie viele Companies) |
|
||
| remember_token | VARCHAR(100) | sfGuardRememberKey | |
|
||
| two_factor_* | (bestehend) | – | Fortify |
|
||
| registration_type | ENUM('agency','company','apiuser','existing_*') | `sfGuardUserProfile.registration_type` | |
|
||
| language | CHAR(2) DEFAULT 'de' | `sfGuardUserProfile.language` | |
|
||
| portal | ENUM('presseecho','businessportal24','both') | – | Standard Portal |
|
||
| is_active | BOOL DEFAULT 1 | `sfGuardUser.is_active` | |
|
||
| is_super_admin | BOOL DEFAULT 0 | `sfGuardUser.is_super_admin` | nur Migrationshilfe; später via Spatie-Rolle |
|
||
| last_login_at | TIMESTAMP | `sfGuardUser.last_login` | |
|
||
| last_login_ip | VARCHAR(45) | `sfGuardUser.ip_address` | |
|
||
| gdpr_consent_at | TIMESTAMP NULL | – | Neu, für Consent-Tracking |
|
||
| legacy_portal | ENUM | – | Import-Spur |
|
||
| legacy_id | BIGINT | `sfGuardUser.id` | |
|
||
| deleted_at | TIMESTAMP | – | |
|
||
|
||
> **Nicht übernommen (D-09):** Legacy-Password-Hash (`sha1+salt`) und Legacy-`api_key` wandern **nicht** in die neue `users`-Tabelle. Bei Go-Live bekommen alle User eine Reset-Mail; API-Clients müssen sich neuen Sanctum-Token generieren.
|
||
|
||
### `profiles` (1:1 zu User, entspricht Rest von `sfGuardUserProfile`)
|
||
|
||
| Spalte | Typ | Legacy-Feld |
|
||
|---|---|---|
|
||
| id, user_id (UK) | – | `user_id` |
|
||
| salutation_key | VARCHAR(20) | `salutation_id` → Config-Key (z.B. `mr`,`mrs`) |
|
||
| title | VARCHAR(80) | `title` |
|
||
| first_name, last_name | VARCHAR(80) | |
|
||
| phone | VARCHAR(40) | |
|
||
| address | VARCHAR(1000) | |
|
||
| country_code | CHAR(2) | `country_id` → ISO Code aus Config |
|
||
| birthdate | DATE | |
|
||
| backlink_url | VARCHAR(255) | |
|
||
| show_stats | BOOL | |
|
||
| validation_date | TIMESTAMP | |
|
||
| contract_date | TIMESTAMP | |
|
||
| validate_token | VARCHAR(32) | `validate` |
|
||
| tax_id_number | VARCHAR(255) | |
|
||
| tax_exempt | BOOL | |
|
||
| tax_exempt_reason | VARCHAR(1000) | |
|
||
| disable_footer_code | BOOL | |
|
||
| timestamps | | |
|
||
|
||
### Rollen/Rechte (Spatie)
|
||
|
||
- Tabellen: `roles`, `permissions`, `model_has_roles`, `model_has_permissions`, `role_has_permissions` (von Spatie).
|
||
- Initial-Rollen: `admin`, `editor`, `customer`, `api-only`.
|
||
- Migration: `sfGuardGroup` → Mapping auf neue Rollen (nicht 1:1!), `sfGuardPermission` → Permissions.
|
||
|
||
### `magic_links` ⭐ NEU (D-10)
|
||
|
||
| Spalte | Typ | Bemerkung |
|
||
|---|---|---|
|
||
| id | BIGINT PK | |
|
||
| user_id | FK `users.id` | |
|
||
| token_hash | CHAR(64) UNIQUE | sha256 des ausgegebenen Tokens |
|
||
| purpose | ENUM('login','press_release_access','invoice_access','initial_setup') | |
|
||
| payload | JSON NULL | z. B. `{"press_release_id": 42}` |
|
||
| expires_at | TIMESTAMP | TTL typisch 15 min |
|
||
| consumed_at | TIMESTAMP NULL | Single-Use |
|
||
| ip_requested | VARCHAR(45) | |
|
||
| ip_consumed | VARCHAR(45) NULL | |
|
||
| timestamps | | |
|
||
|
||
Scheduler-Job `PurgeExpiredMagicLinks` räumt alte/konsumierte Einträge > 30 Tage auf.
|
||
|
||
---
|
||
|
||
## 2. Companies & Contacts
|
||
|
||
### `companies`
|
||
|
||
| Spalte | Typ | Legacy (`Company`) |
|
||
|---|---|---|
|
||
| id | BIGINT PK | |
|
||
| portal | ENUM | – |
|
||
| owner_user_id | BIGINT NULL | `user_id` |
|
||
| type | ENUM('agency','company') DEFAULT 'company' | `ctype` |
|
||
| name | VARCHAR(255) | |
|
||
| slug | VARCHAR(255) UNIQUE WITHIN portal | (Sluggable) |
|
||
| address | VARCHAR(1000) | |
|
||
| country_code | CHAR(2) | `country_id` |
|
||
| phone | VARCHAR(40) | |
|
||
| fax | VARCHAR(40) | |
|
||
| email | VARCHAR(190) | |
|
||
| website | VARCHAR(190) | |
|
||
| logo_path | VARCHAR(255) | `logo` (Legacy, minimal) |
|
||
| logo_variants | JSON NULL | – (neu, D-05: `{sq,wide,dark}`) |
|
||
| is_active | BOOL DEFAULT 1 | |
|
||
| disable_footer_code | BOOL | |
|
||
| legacy_portal, legacy_id | | |
|
||
| timestamps, deleted_at | | |
|
||
|
||
### `company_user` (Pivot)
|
||
|
||
| user_id (PK) | company_id (PK) | role ENUM('member','responsible','owner') |
|
||
|
||
→ Vereinigt `CompanyUser` + `ResponsibleCompanyUser` in eine Pivot-Tabelle mit `role`.
|
||
|
||
### `contact_user` (Pivot)
|
||
|
||
| user_id (PK) | contact_id (PK) |
|
||
|
||
→ Direkte User-Kontakt-Zuordnung für den Admin- und Customer-Kontext. Im Legacy-System wurde diese Zugehörigkeit indirekt aus `company_user`/`responsible_company_user` plus `contacts.company_id` abgeleitet. Der Import befüllt die Tabelle über `legacy:import --step=link-associations` idempotent aus bestehenden Firmenzuordnungen.
|
||
|
||
### `contacts`
|
||
|
||
| Spalte | Typ | Legacy (`Contact`) |
|
||
|---|---|---|
|
||
| id | BIGINT | |
|
||
| company_id | FK `companies.id` | |
|
||
| salutation_key | VARCHAR(20) | `salutation_id` |
|
||
| title | VARCHAR(80) | |
|
||
| first_name, last_name | VARCHAR(80) | |
|
||
| responsibility | VARCHAR(255) | |
|
||
| phone, fax | VARCHAR(40) | |
|
||
| email | VARCHAR(190) | |
|
||
| legacy_portal, legacy_id | | |
|
||
| timestamps, deleted_at | | |
|
||
|
||
---
|
||
|
||
## 3. Kategorien
|
||
|
||
### `categories`
|
||
|
||
| id | parent_id NULL | portal | is_active | legacy_*, timestamps |
|
||
|
||
### `category_translations`
|
||
|
||
| id | category_id FK | locale CHAR(2) | name VARCHAR(255) | slug VARCHAR(255) | description TEXT |
|
||
| UNIQUE (category_id, locale) |
|
||
|
||
---
|
||
|
||
## 4. Pressemitteilungen
|
||
|
||
### `press_releases`
|
||
|
||
| Spalte | Typ | Legacy (`PressRelease`) |
|
||
|---|---|---|
|
||
| id | BIGINT | |
|
||
| uuid | CHAR(36) UNIQUE | – neu für öffentliche Links |
|
||
| portal | ENUM | – |
|
||
| user_id | FK | |
|
||
| company_id | FK NULL | |
|
||
| category_id | FK | |
|
||
| language | CHAR(2) | |
|
||
| title | VARCHAR(255) | |
|
||
| slug | VARCHAR(255) | (unique: portal+language+slug) |
|
||
| text | MEDIUMTEXT | |
|
||
| backlink_url | VARCHAR(255) | |
|
||
| keywords | VARCHAR(255) | |
|
||
| status | ENUM('draft','review','published','rejected','archived') | `status` string |
|
||
| hits | INT UNSIGNED DEFAULT 0 | |
|
||
| teaser_begin | INT UNSIGNED | |
|
||
| teaser_end | INT UNSIGNED | |
|
||
| no_export | BOOL | |
|
||
| published_at | TIMESTAMP NULL | – neu |
|
||
| legacy_portal, legacy_id | | |
|
||
| timestamps, deleted_at | | |
|
||
|
||
### `press_release_images` (erweitert, D-05)
|
||
|
||
Spalten: `id`, `press_release_id FK`, `disk VARCHAR(30) DEFAULT 'public'`, `path VARCHAR(512)`, `variants JSON` (thumb/medium/large), `title VARCHAR(120)`, `description VARCHAR(500)`, `copyright VARCHAR(255)`, `is_preview BOOL`, `sort_order INT UNSIGNED`, `width INT`, `height INT`, `mime VARCHAR(60)`, `legacy_*`, `timestamps`, `deleted_at`.
|
||
|
||
Legacy war minimalistisch – künftig dürfen PMs mehrere, größere Bilder enthalten (kein hartes Limit, UI-Limit konfigurierbar).
|
||
|
||
### `press_release_contact` (Pivot, wie Legacy `PressReleaseContact`)
|
||
|
||
| press_release_id | contact_id |
|
||
|
||
---
|
||
|
||
## 5. Newsletter
|
||
|
||
### `newsletter_subscriptions`
|
||
|
||
| Spalte | Typ | Legacy (`NewsletterSubscription`) |
|
||
|---|---|---|
|
||
| id | | |
|
||
| portal | ENUM | – |
|
||
| user_id | FK NULL | `user_id` |
|
||
| salutation_key | VARCHAR(20) | |
|
||
| first_name, last_name | VARCHAR(80) | |
|
||
| email | VARCHAR(190) (unique per portal) | |
|
||
| ip_address | VARCHAR(45) | |
|
||
| is_confirmed | BOOL | `is_active` |
|
||
| confirmation_token | VARCHAR(32) | `validate` |
|
||
| subscribed_at | TIMESTAMP | `subscribe_date` |
|
||
| unsubscribed_at | TIMESTAMP | `unsubscribe_date` |
|
||
| legacy_portal, legacy_id | | |
|
||
| timestamps | | |
|
||
|
||
---
|
||
|
||
## 6. Blacklist / FooterCode
|
||
|
||
### `blacklists`
|
||
|
||
| id | portal | title VARCHAR(255) | content MEDIUMTEXT | timestamps |
|
||
|
||
### `footer_codes` (bleibt, Q-08b)
|
||
|
||
| id | portal | language CHAR(2) NULL | title VARCHAR(255) | content TEXT | is_global BOOL | timestamps |
|
||
|
||
### `category_footer_code` (Pivot)
|
||
|
||
| footer_code_id | category_id |
|
||
|
||
> **Entfällt (D-14):** `promotion_links`, `promotion_link_category` – werden nicht migriert und nicht neu angelegt.
|
||
|
||
---
|
||
|
||
## 7. Billing & Payment (komplett neu, D-03/D-12/D-13)
|
||
|
||
**Wichtig:** Legacy-PaymentOptions, -UserPaymentOptions, -UserPayments und -Coupons werden **nicht 1:1 in die neue Billing-Logik migriert**. Nur *aktive* Subscriptions werden als "grandfathered" übernommen. Legacy-Rechnungen werden dagegen vollständig als read-only Archivdaten importiert (`legacy_invoices`) und nicht in den neuen Rechnungskreis (`invoices`) umgebucht.
|
||
|
||
### `billing_addresses` (entspricht `UserBillingAddress`)
|
||
|
||
| id | user_id FK UNIQUE | salutation_key | title | name | address1 | address2 | postal_code | city | country_code | timestamps |
|
||
|
||
### `invoice_billing_addresses`
|
||
|
||
Wie `billing_addresses`, aber ohne `user_id`-FK; dafür Snapshot zum Zeitpunkt der Rechnung.
|
||
|
||
### `payment_options` (neu definiert)
|
||
|
||
| Spalte | Typ | Bemerkung |
|
||
|---|---|---|
|
||
| id | BIGINT PK | |
|
||
| article_number | VARCHAR(60) UNIQUE | Interne Artikelnummer |
|
||
| type | ENUM('recurring','onetime') | Stripe-Subscription oder One-Off |
|
||
| price_cents | INT UNSIGNED | |
|
||
| currency | CHAR(3) DEFAULT 'EUR' | |
|
||
| interval | ENUM('monthly','yearly','once') NULL | Für Recurring |
|
||
| is_hidden | BOOL | |
|
||
| stripe_product_id | VARCHAR(60) | |
|
||
| stripe_price_id | VARCHAR(60) | |
|
||
| timestamps, deleted_at | | |
|
||
|
||
### `payment_option_translations` (DE/EN)
|
||
|
||
| id | payment_option_id FK | locale | name | description |
|
||
|
||
### `user_payment_options` (aktive Abos)
|
||
|
||
| Spalte | Typ | Bemerkung |
|
||
|---|---|---|
|
||
| id | BIGINT PK | |
|
||
| user_id | FK | |
|
||
| payment_option_id | FK | |
|
||
| status | ENUM('active','past_due','cancelled','grandfathered') | |
|
||
| **grandfathered_until** | DATE NULL | ⭐ D-13: Alt-Konditionen bis zu diesem Datum |
|
||
| legacy_conditions | JSON NULL | Snapshot alter Konditionen für Grandfathering |
|
||
| current_period_start | DATE | |
|
||
| current_period_end | DATE | |
|
||
| stripe_subscription_id | VARCHAR(60) NULL | |
|
||
| cancelled_at | TIMESTAMP NULL | |
|
||
| timestamps | | |
|
||
|
||
### `user_payment_option_company` (Pivot)
|
||
|
||
| user_payment_option_id | company_id | is_active | timestamps |
|
||
|
||
### `user_payments` (nur noch Stripe-Charges)
|
||
|
||
| id | user_payment_option_id FK NULL | amount_cents | currency | status ENUM('pending','succeeded','failed','refunded') | stripe_charge_id | stripe_invoice_id NULL | timestamps |
|
||
|
||
### `invoices` (neuer Rechnungskreis)
|
||
|
||
| Spalte | Bemerkung |
|
||
|---|---|
|
||
| id | |
|
||
| user_id FK | |
|
||
| user_payment_id FK NULL | |
|
||
| invoice_billing_address_id FK | Snapshot |
|
||
| number VARCHAR(40) UNIQUE | neue Nummernkreis, z. B. `{YYYY}{MM}-{seq}` |
|
||
| status | ENUM('open','paid','void','uncollectible') (Stripe-nah) |
|
||
| amount_cents / tax_cents / total_cents | |
|
||
| currency CHAR(3) | |
|
||
| is_netto BOOL | |
|
||
| invoice_date / due_date / paid_at | |
|
||
| stripe_invoice_id VARCHAR(60) NULL | |
|
||
| pdf_path | |
|
||
| timestamps, deleted_at | |
|
||
|
||
> **Entfällt:** `payment_method`-Enum – es gibt nur noch Stripe. Keine Rechnungszahlungs-Variante mehr.
|
||
|
||
### `legacy_invoices` ⭐ ARCHIV (D-12)
|
||
|
||
Separate, read-only Archivtabelle mit allen Rechnungen aus beiden Legacy-Portalen. Ziel ist der vollständige Import der vorhandenen Rechnungsdaten inkl. Status und User-Zuordnung; die PDF-Erzeugung für Legacy-Rechnungen bleibt DB-basiert und erfolgt bei Abruf/on demand.
|
||
|
||
| Spalte | Typ | Legacy-Quelle |
|
||
|---|---|---|
|
||
| id | BIGINT PK | (neu) |
|
||
| legacy_portal | ENUM | — |
|
||
| legacy_id | BIGINT | `invoice.id` |
|
||
| user_id | FK `users.id` NULL | Via Import-Map auflösbar |
|
||
| legacy_user_id | BIGINT | Original-User-ID im Legacy |
|
||
| number | VARCHAR(40) | Original-Rechnungsnummer |
|
||
| amount_cents / tax_cents / total_cents | | |
|
||
| status | VARCHAR(40) | Original-Status-String |
|
||
| invoice_date / due_date / paid_at | | |
|
||
| payment_method | VARCHAR(40) | Nur als Info (SPK, PayPal, Invoice …) |
|
||
| pdf_path | VARCHAR(512) NULL | Optionaler Cachepfad, falls ein generiertes PDF abgelegt wird; nicht primäre Quelle |
|
||
| raw_snapshot | JSON | Vollständige Original-Zeile bzw. alle für die Legacy-PDF-Erzeugung nötigen Daten für Audit/Rendering |
|
||
| pdf_payload | JSON NULL | Snapshot für PDF-Erzeugung (`invoice`, `invoice_billing_address`, `user_payment`) |
|
||
| pdf_generated_at | TIMESTAMP NULL | Zeitpunkt der letzten on-demand PDF-Erzeugung |
|
||
| imported_at | TIMESTAMP | |
|
||
|
||
UNIQUE `(legacy_portal, legacy_id)`.
|
||
|
||
**DB-Vorbereitung vor Go-Live:** `pdf_payload` enthält die für die on-demand PDF-Erzeugung nötigen Legacy-Snapshots. Der neue `invoices`-Kreislauf bleibt davon getrennt.
|
||
|
||
> **Coupons vertagt (D-16):** Keine Tabelle in Phase 1. Wird später, ggf. direkt als Stripe-Coupons, nachgezogen.
|
||
|
||
---
|
||
|
||
## 8. Import-Tracking
|
||
|
||
### `legacy_import_map`
|
||
|
||
| Spalte | Typ | Zweck |
|
||
|---|---|---|
|
||
| id | BIGINT | |
|
||
| legacy_portal | ENUM('presseecho','businessportal24') | |
|
||
| legacy_table | VARCHAR(80) | z. B. `sf_guard_user`, `press_release` |
|
||
| legacy_id | BIGINT | |
|
||
| target_table | VARCHAR(80) | |
|
||
| target_id | BIGINT | |
|
||
| imported_at | TIMESTAMP | |
|
||
| UNIQUE (legacy_portal, legacy_table, legacy_id) | | |
|
||
|
||
Diese Tabelle erlaubt idempotente Imports und Rückverfolgung.
|
||
|
||
---
|
||
|
||
## 9. Tabellen-Übersicht (Total)
|
||
|
||
| # | Tabelle | Quelle / Herkunft |
|
||
|---|---|---|
|
||
| 1 | users | sfGuardUser + sfGuardUserProfile (teilw.) |
|
||
| 2 | profiles | sfGuardUserProfile |
|
||
| 3 | magic_links | NEU (D-10) |
|
||
| 4 | roles / permissions / … | Spatie |
|
||
| 5 | companies | Company + Agency |
|
||
| 6 | company_user | CompanyUser + ResponsibleCompanyUser |
|
||
| 7 | contacts | Contact |
|
||
| 8 | categories | Category |
|
||
| 9 | category_translations | Category_Translation |
|
||
| 10 | press_releases | PressRelease |
|
||
| 11 | press_release_images | PressReleaseImage (erweitert) |
|
||
| 12 | press_release_contact | PressReleaseContact |
|
||
| 13 | newsletter_subscriptions | NewsletterSubscription (nur Daten, Workflow neu) |
|
||
| 14 | blacklists | Blacklist |
|
||
| 15 | footer_codes, category_footer_code | FooterCode (bleibt) |
|
||
| 16 | billing_addresses | UserBillingAddress |
|
||
| 17 | invoice_billing_addresses | InvoiceBillingAddress |
|
||
| 18 | payment_options | NEU (nicht aus Legacy) |
|
||
| 19 | payment_option_translations | NEU |
|
||
| 20 | user_payment_options | Teil-Übernahme aktiver Abos (Grandfathering) |
|
||
| 21 | user_payment_option_company | s.o. |
|
||
| 22 | user_payments | NEU (Stripe-Charges) |
|
||
| 23 | invoices | NEU (Stripe-getriebener Rechnungskreis) |
|
||
| 24 | **legacy_invoices** | ⭐ ARCHIV aller alten Rechnungen |
|
||
| 25 | legacy_import_map | NEU (technisch) |
|
||
|
||
**Raus aus der DB, rein in Config/Code:**
|
||
|
||
| Legacy-Tabelle | Ziel |
|
||
|---|---|
|
||
| `Country` | `config/countries.php` |
|
||
| `Salutation` + `Salutation_Translation` | `config/salutations.php` |
|
||
|
||
**Ganz raus / entfallen:**
|
||
|
||
| Legacy-Tabelle | Grund |
|
||
|---|---|
|
||
| `ApiUser` | Duplikat zu `users.registration_type = apiuser` / Rolle `api-only` |
|
||
| `sfGuardRememberKey` | Laravel `remember_token`-Spalte reicht |
|
||
| `sfGuardForgotPassword` | Fortify löst Reset selbst |
|
||
| `PromotionLink` / `PromotionLinkCategory` | Feature entfällt (D-14) |
|
||
| `Coupon` | Vertagt (D-16) |
|
||
| `PaymentOption_ExcludeGroup` / `_AccessGroup` | Ersetzt durch Spatie-Permissions + direkte Product-Whitelist |
|
||
| `Invoice` (als aktive Tabelle) | Ersetzt durch neuen Rechnungskreis + `legacy_invoices`-Archiv |
|
||
|
||
---
|
||
|
||
## 10. Indizes & Performance
|
||
|
||
- `press_releases`: Index `(portal, status, published_at DESC)` für Public-Listing; Unique `(portal, language, slug)`.
|
||
- `press_releases`: Index `uuid` (öffentliche Share-Links).
|
||
- `newsletter_subscriptions`: Unique `(portal, email)`.
|
||
- `magic_links`: Index `(user_id, purpose, consumed_at)`; Unique `token_hash`.
|
||
- `invoices`: Index `(user_id, invoice_date)`, `stripe_invoice_id`.
|
||
- `legacy_invoices`: Unique `(legacy_portal, legacy_id)`; Index `(user_id, invoice_date DESC)`.
|
||
- `legacy_import_map`: Unique-Index siehe oben.
|
||
|
||
---
|
||
|
||
## 11. Migrationen (Reihenfolge)
|
||
|
||
1. `users` (Alterung der bestehenden Tabelle durch Zusatz-Spalten, `password` NULLABLE)
|
||
2. `profiles`, `billing_addresses`
|
||
3. `magic_links`
|
||
4. Spatie-Migrationen (+ Seeder für Rollen `admin`, `editor`, `customer`, `api-only`)
|
||
5. `companies`, `company_user`, `contacts`
|
||
6. `categories`, `category_translations` (inkl. EN-Seed)
|
||
7. `blacklists`, `footer_codes`, `category_footer_code`
|
||
8. `press_releases`, `press_release_images`, `press_release_contact`
|
||
9. `newsletter_subscriptions`
|
||
10. `payment_options`, `payment_option_translations`
|
||
11. `user_payment_options`, `user_payment_option_company`, `user_payments`
|
||
12. `invoice_billing_addresses`, `invoices`
|
||
13. `legacy_invoices` (Archiv)
|
||
14. `legacy_import_map`
|
||
|
||
> Separate Phase (Phase 6): Import-Commands füllen diese Tabellen aus den Legacy-DBs.
|