Skip to content

[Invoices]: CSV import of invoices, invoice items, and payments (v1 parity) #676

Description

@nielsdrost7

InvoicePlane v1 could import historical invoices, invoice items, and payments from CSV — the backbone of migrating billing history into the app. v2 has no import code at all. The import/export epic #85 covers products (#140), catalogs (#139), clients (#141), and Excel support (#80), but invoice/item/payment import is listed nowhere.

Behavioral spec: v1 application/modules/import/models/Mdl_import.php and controllers/Import.php.


Specific

Add Filament ImportActions (with Importer classes) for three CSV types, matching v1's column contracts:

  • invoices.csvuser_email, client_name, invoice_date_created, invoice_date_due, invoice_number, invoice_terms (v1 Mdl_import.php:41-48)
  • invoice_items.csvinvoice_number, item_tax_rate, item_date_added, item_name, item_description, item_quantity, item_price (:49-57)
  • payments.csvinvoice_number, payment_method, payment_date, payment_amount, payment_note (:58-64)

Resolution rules (v1 parity):

  • user_email → existing user; row skipped with error if not found (:198-208).
  • client_name → existing Relation by name; auto-created if missing (:217-227).
  • invoice_number (items/payments) → existing invoice; row skipped if not found (:282-291, :360-367).
  • item_tax_rate (percent) → existing TaxRate by percent; auto-created if missing (:292-309).
  • payment_method (name) → existing payment method; auto-created if missing (:369-384).

Measurable

  • Import buttons on the Invoices and Payments list pages (company panel).
  • A 10-row valid invoices.csv creates exactly 10 invoices with correct dates, numbers, and terms; amounts recalculate after item import.
  • Rows referencing unknown users/invoice numbers are skipped and reported per-row; no partial records.
  • Auto-created relations/tax rates/payment methods are company-scoped.

Achievable

Importer classes at Modules/Invoices/Filament/Company/Imports/{InvoiceImporter,InvoiceItemImporter}.php and Modules/Payments/Filament/Company/Imports/PaymentImporter.php, wired via ImportAction::make()->importer(...) on the List pages. resolveRecord() implements the lookup/auto-create rules above. Requires publishing Filament's imports/failed_import_rows migrations (currently absent — only the exports table exists).

Relevant

Child of epic #85. Without historical invoices and payments, migrating users lose their books — client import alone (#141) is not a usable migration.

Time-Bound

One sprint after #141 lands (reuses its Importer conventions).


Arrange

  • Company with 1 user (known email), 1 existing relation "Acme", a 20% tax rate, no payment methods.
  • invoices.csv with 3 rows: one for "Acme", one for unknown client "Beta LLC", one with an unknown user_email.
  • invoice_items.csv with rows for both invoice numbers, one item at tax rate 21 (not existing).
  • payments.csv with one payment via method "Bank transfer" (not existing).

Act

  • Run the three imports in order (invoices → items → payments) via the Filament ImportActions.

Assert

  • 2 invoices created ("Acme", "Beta LLC"); the unknown-user row is reported as skipped.
  • Relation "Beta LLC" was auto-created, scoped to the company.
  • A 21% TaxRate was auto-created and linked to its item.
  • Payment method "Bank transfer" was auto-created; payment attached to the right invoice.
  • Invoice totals/balances recalculate correctly after item + payment import.
  • No cross-company leakage (second company sees none of the records).
  • PHPUnit feature tests cover each resolution rule and each skip path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    refinedRefined with SMART plan / test suggestions

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions