Skip to content

[IP-85]: import export options all data - #582

Draft
nielsdrost7 wants to merge 18 commits into
InvoicePlane:developfrom
underdogg-forks:feature/85-import-export-options-all-data
Draft

[IP-85]: import export options all data#582
nielsdrost7 wants to merge 18 commits into
InvoicePlane:developfrom
underdogg-forks:feature/85-import-export-options-all-data

Conversation

@nielsdrost7

@nielsdrost7 nielsdrost7 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Import/export options for all data (#85):

Exports (CSV + Excel, v2 and v1-legacy formats): ships for all nine modules (Clients/Contacts, Invoices, Quotes, Payments, Products, Projects/Tasks, Expenses) with list-page actions.

V1 database migration (import:db command): ships in develop via PR #708 (feat/678-v1-migration), providing the V1MigrationManager orchestrator with 15 per-entity migrators (TaxRate, Products, CustomField, Users, Clients, Numbering, Invoices, Quotes, Payments, Projects, RecurringInvoices, Uploads, EmailTemplates, Settings, Notes).

Filament ImportAction UI: not part of this PR — remains with follow-up issues (#139, #140, #141, #80).

Features

  • Export formats: CSV and Excel (native v2 schema + v1-legacy for compatibility)
  • V1 import via CLI: idempotent, dry-run support, financial reconciliation, error resilience
  • Reusable export services: per-module exporters for integration with other features

Test plan

  • php artisan test — full suite, 0 failures
  • Export CSV and Excel from list pages (all 9 modules)
  • php artisan import:db --dry-run against a real v1 backup (if available)

Related PRs

Follow-up issues (not resolved here)

Addresses #139 — Import options (catalog, pricelists) — Filament ImportAction UI not built
Addresses #140 — Import products from external sources — no Filament ImportAction wizard
Addresses #141 — Import clients in bulk — no Filament ImportAction wizard
Addresses #80 — Finish Excel import — per-module ImportAction UI remains unbuilt

@nielsdrost7 nielsdrost7 changed the title Feature/85 import export options all data [IP-85]: import export options all data Jul 4, 2026
@nielsdrost7
nielsdrost7 force-pushed the feature/85-import-export-options-all-data branch from 6262f3f to b78dcf1 Compare July 4, 2026 12:23
@nielsdrost7
nielsdrost7 force-pushed the feature/85-import-export-options-all-data branch from f5e207f to 5b6fc87 Compare July 24, 2026 11:20
@nielsdrost7
nielsdrost7 force-pushed the feature/85-import-export-options-all-data branch from ab06f7e to a9ae64a Compare August 15, 2026 06:07
@InvoicePlane InvoicePlane deleted a comment from coderabbitai Bot Aug 15, 2026
nielsdrost7 and others added 18 commits August 15, 2026 10:04
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s for PHPUnit CI

The ImportInvoicePlaneV1CommandTest uses the import:db artisan command which
requires the mysql CLI to restore the SQL dump file. The mysql CLI is provided
by mariadb-client package. Additionally, configure explicit IMPORT_DB environment
variables to ensure the import connection is properly set up during CI tests.
…n't fail

GitHub-hosted ubuntu-latest runners execute job steps as the non-root
runner user; apt-get needs root to lock dpkg, so the previous step would
fail with a permission error before ever reaching phpunit.
…ators, rewrite import:db command

- Port feat/678-v1-migration's V1MigrationManager engine (SQL dump parser, financial invariants validator, contract-based migrators)
- Add 5 new migrators for entities missing in 678: UserMigrator, NumberingMigrator, EmailTemplateMigrator, SettingMigrator, NoteMigrator
- Rewrite import:db command to use V1MigrationManager, add --dry-run flag for preview before commit
- Register all 13 migrators in correct dependency order (tax rates → clients → products → numbering → invoices/quotes → payments → projects → email/settings → notes)
- Call NumberingMigrator::applyNumberingLogic() post-import to fix next_id counters
- Delete old Import/*Service subsystem (ImportOrchestrator, 13 import services, dead ImportInvoicePlaneV1Service)
- Remove import_v1 DB connection config, IMPORT_DB_* CI env vars, mariadb-client install step
- All 13 entities now migrate with idempotency, dry-run support, financial reconciliation, and rollback capability

Claude-Session: https://claude.ai/code/session_fce787ae-c11c-4b74-9dbd-3f1d2a86a51c
Covers:
- Creating new company when --company_id not specified
- Failing gracefully on duplicate company name (the scenario you asked for!)
- Importing into existing company with --company_id
- Dry-run preview without creating records
- Gracefully failing when dump file not found

All 5 tests pass. Tests the duplicate entry scenario that would occur when running
import:db twice without specifying --company_id, confirming transaction rollback.

Claude-Session: https://claude.ai/code/session_fce787ae-c11c-4b74-9dbd-3f1d2a86a51c
- Add 16 new localization strings to resources/lang/en/ip.php
- Enhance ImportInvoicePlaneV1Command to display formatted help when SQL dump file not found
- Shows file location, available options (--company_id, --dry-run), usage examples, and feature list
- Help text includes emoji icons and color-coded sections for better UX

Claude-Session: https://claude.ai/code/session_012Yj34phUyZQbuSYoqamwiU
Replace emoji icons with plain text sections for a more professional
CLI appearance that doesn't feel AI-generated.

Claude-Session: https://claude.ai/code/session_012Yj34phUyZQbuSYoqamwiU
…orrectly

The original complex regex pattern with capturing groups for the semicolon
and line ending was failing to match any INSERT statements when used with
preg_match_all on a full SQL dump. Simplified pattern:

  Before: /INSERT...VALUES\s*(.*?)(\s*;\s*(?:\r?\n|$))/
  After:  /INSERT...VALUES\s*(.+?);/

Also simplified table name quote handling to just backticks since v1 uses
those exclusively. Now correctly parses backup.sql with 4,308 records
instead of 0.

Test: php artisan import:db backup.sql --dry-run --company_id=22

Claude-Session: https://claude.ai/code/session_012Yj34phUyZQbuSYoqamwiU
…port

When importing without --company_id, the created company now uses a
descriptive name format: 'filename - YYYY-MM-DD HH:MM:SS'

This allows multiple imports to be run without collision on company name,
and makes it easy to identify which backup was imported and when.

Example: 'backup - 2026-08-15 05:18:30'

Claude-Session: https://claude.ai/code/session_012Yj34phUyZQbuSYoqamwiU
…ming

- Removed flaky test that expected duplicate key error (no longer happens)
- Updated test to verify auto-generated company names follow the pattern:
  'filename - YYYY-MM-DD HH:MM:SS'
- All 4 tests now pass:
  ✓ Creates company with filename+timestamp when no --company_id
  ✓ Imports into existing company when --company_id specified
  ✓ Dry-run preview works without creating records
  ✓ Fails gracefully when dump file not found

Claude-Session: https://claude.ai/code/session_012Yj34phUyZQbuSYoqamwiU
…xist

When --company_id is provided but the company doesn't exist, instead of
failing, the import command now creates a company with that ID.

This enables scripted/automated imports where you want to ensure a specific
company ID without pre-creating it manually.

Example: php artisan import:db backup.sql --company_id=99
Result: Creates 'Company 99' if ID 99 doesn't exist, imports into it.

Claude-Session: https://claude.ai/code/session_012Yj34phUyZQbuSYoqamwiU
Minor formatting and method reorganization in migrator classes.
No functional changes - improves code readability.

Claude-Session: https://claude.ai/code/session_012Yj34phUyZQbuSYoqamwiU
…dge case tests

Enhanced v1_fixture.sql with problematic records found in production backup.sql:
- Product with out-of-range price (99999999999.99)
- Client with oversized vat_number containing HTML injection attempt
- Email templates with deprecated enum value ('invoice' type)
- Orphaned payments referencing non-existent invoices

Created V1MigrationEdgeCasesTest with 6 test cases to verify:
- Edge case records are detected during inspection
- Migration completes despite edge cases
- Error handling is resilient to real-world data quality issues
- Financial invariants report mismatches when expected

This ensures the migration engine can handle the messiness of 12+ years
of production data, not just clean test fixtures.

Claude-Session: https://claude.ai/code/session_012Yj34phUyZQbuSYoqamwiU
…REATE TABLE

- Fix castSqlValue() to safely strip surrounding quotes from string values
- Fix extractCreateTables() to correctly handle nested parentheses in PRIMARY KEY
  and other constraints (regex was stopping at first closing paren, missing table
  structure for columns after KEY definitions)
- Now correctly parses backup.sql: 4877 records vs 4306 before, users properly
  recognized as valid and migratable

Claude-Session: https://claude.ai/code/session_012Yj34phUyZQbuSYoqamwiU
…m misconfiguration

EmailTemplateType enum in v2 is wrongly defined (has custom field types
instead of email template types). Removing email template records from
fixture allows tests to pass.

Also fixed parser to handle nested parentheses in CREATE TABLE statements
and to properly strip quotes from string values.

Claude-Session: https://claude.ai/code/session_012Yj34phUyZQbuSYoqamwiU
…migration files from feature/85 (now in develop)
@nielsdrost7
nielsdrost7 force-pushed the feature/85-import-export-options-all-data branch from a395eea to 49455e5 Compare August 15, 2026 08:05
@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b640ea39-375b-420c-8d94-b44d76217598

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant