Skip to content

[Invoices]: Snapshot company contact data at invoice creation #382

Closed

Description

@justnx

i just move to another address and realized that there seems no way to change my contact and phone data without having the effect that all old invoices now have the same new contact data.

a feature that stores changed contact data separately in db and map that field id against each invoice would be awesome.

Activity

  1. clockwiseq commented on Jul 11, 2021

    @clockwiseq

    I agree. I have had this issue in the past and it would be nice to have multiple addresses/contact information and have the invoice engine pull from a specific company profile record. There are really 2 viable solutions to implementing something like this and I would like more input from the community on which one makes more sense:

    1. Company Profile Foreign Key on Invoice Header - this would simply allow the user to add multiple company profiles and as an invoice header is created, the user is presented with a dropdown that allows them to choose which company profile to use for this particular invoice. This way, archived invoices will retain the address that was associated with the company profile that was used when the invoice was created. As far as development, there would need to be a change to the database to add the foreign key of the company profile to the invoice header and then modify the user interface to include a new company profile dropdown on the invoice editor view. The default company profile should be selected by default. I believe this would enhance IP in a positive manner and would be the proper solution to this issue.

    2. Add Invoice Template Name on Invoice Header - this would simply allow the user to select which invoice template should be used for a specific invoice on the view. You could then create templates that had specific address information on it. This still requires modification to the invoice header object and database and would require a change to the invoice generation engine. This is simply a solution and not the one I recommend.

    Last comment about this, this issue exists in many other invoicing and ERP systems. Some of them are extremely costly and they haven't implemented anything to solve this. I believe this would be a great opportunity to entice non-IP users to switch. It truly is a common issue and I have struggled with it in the past.

  2. dagelf commented on Jan 22, 2022

    @dagelf

    The obvious way is to keep archive PDF copies of each quote and invoice and display those. You can already do this on email if you enable the BCC (Blind carbon copy) option in the settings.

    An efficient way would be to store historic contact details, and note the contact details version in the database entry for the invoice.

    An elaborate way of doing it would be to add all the details to the database entry for each invoice.

  3. changed the title [-]Possibility to change own contact data without changing existing invoices[/-] [+][Feature Request] Possibility to change own contact data without changing existing invoices[/+] on Sep 17, 2023
  4. nielsdrost7 commented on Jun 13, 2026

    @nielsdrost7
    Collaborator

    Planning Note — v2 Implementation

    Story: As a company admin, I want changes to my company's name, address, or contact details to NOT retroactively alter the header information on previously issued invoices, so that old invoices remain legally accurate even after I move address.

    v2 context: This is the "invoice snapshot" problem. v2 stores a foreign key company_id on invoices and renders company data from the live Company model at PDF-render time. To fix this, company address/contact data must be snapshotted onto the invoice at creation/send time.

    Acceptance Criteria:

    • At the time an invoice is created (or moved to sent status), the following company fields are snapshotted onto the invoice record: name, address, city, zip, country, phone, email, VAT number, IBAN.
    • These snapshot fields are used for all subsequent PDF renders and guest views — not the live company record.
    • Updating company settings does NOT alter existing invoice snapshots.
    • New invoices created after the address change use the updated address.
    • The edit form does NOT expose snapshot fields directly (they are read-only audit data).

    Implementation:

    • Add snapshot columns to invoices (and quotes): company_name, company_address, company_city, company_zip, company_country, company_phone, company_email, company_vat_number — all nullable varchar.
    • InvoiceService::createInvoice(): populate snapshot columns from $company->* at creation time.
    • InvoiceService::sendInvoice(): if snapshot is still null (legacy), populate it at send time.
    • PDF/guest blade: use $invoice->company_name ?? $invoice->company->name for backwards compatibility.

    Test Scenarios (PHPUnit):

    // Invoice snapshot captures company address at creation
    /* Arrange */ $this->company->update(['name' => 'ACME Corp', 'address' => '123 Main St']);
                  $invoice = app(InvoiceService::class)->createInvoice(['invoiced_at' => today()]);
    /* Assert */  $this->assertEquals('ACME Corp', $invoice->company_name);
                  $this->assertEquals('123 Main St', $invoice->company_address);
    // Changing company address does not update old invoice
    /* Arrange */ $invoice = Invoice::factory()->for($this->company)->create(['company_address' => '123 Main St']);
    /* Act */     $this->company->update(['address' => '456 New Ave']);
    /* Assert */  $this->assertEquals('123 Main St', $invoice->fresh()->company_address);
  5. changed the title [-][Feature Request] Possibility to change own contact data without changing existing invoices[/-] [+][Invoices]: Snapshot company contact data at invoice creation[/+] on Jun 13, 2026
  6. added this to the v2.0.0-alpha milestone on Jun 13, 2026
  7. nielsdrost7 commented on Jul 16, 2026

    @nielsdrost7
    Collaborator

    Test Suggestions

    Added to support future implementation of this request. The issue body is unchanged.

    Today Modules\Invoices\Models\Invoice has no snapshot of the client's contact/address data — it only has a customer() relation to Relation, so editing a client's contact details retroactively changes how every past invoice displays that contact, which is the bug being reported here. If this is implemented (e.g. by copying contact fields onto the invoice at creation time), the following should be covered:

    • PHPUnit: it_snapshots_the_clients_contact_data_at_invoice_creation()
    #[Test]
    public function it_snapshots_the_clients_contact_data_at_invoice_creation(): void
    {
        /* Arrange */
        $client = Relation::factory()->for($this->company)->create(['address_1' => '1 Old Street']);
    
        /* Act */
        $invoice = app(InvoiceService::class)->createInvoice([
            'customer_id' => $client->id,
            'invoiced_at' => now()->toDateString(),
        ]);
    
        /* Assert */
        $this->assertSame('1 Old Street', $invoice->customer_address_1 ?? null);
    }
    • PHPUnit: it_does_not_change_an_existing_invoices_snapshot_when_the_client_moves()
    #[Test]
    public function it_does_not_change_an_existing_invoices_snapshot_when_the_client_moves(): void
    {
        /* Arrange */
        $client  = Relation::factory()->for($this->company)->create(['address_1' => '1 Old Street']);
        $invoice = app(InvoiceService::class)->createInvoice(['customer_id' => $client->id, 'invoiced_at' => now()->toDateString()]);
    
        /* Act */
        $client->update(['address_1' => '2 New Street']);
    
        /* Assert */
        $this->assertSame('1 Old Street', $invoice->fresh()->customer_address_1 ?? null);
    }
    • PHPUnit: it_renders_the_snapshotted_address_on_the_invoice_pdf_not_the_clients_current_address()
    #[Test]
    public function it_renders_the_snapshotted_address_on_the_invoice_pdf_not_the_clients_current_address(): void
    {
        /* Arrange */
        $client  = Relation::factory()->for($this->company)->create(['address_1' => '1 Old Street']);
        $invoice = app(InvoiceService::class)->createInvoice(['customer_id' => $client->id, 'invoiced_at' => now()->toDateString()]);
        $client->update(['address_1' => '2 New Street']);
    
        /* Act */
        $response = $this->get(route('filament.company.resources.invoices.view', ['tenant' => Str::lower($this->company->search_code), 'record' => $invoice->id]));
    
        /* Assert */
        $response->assertSee('1 Old Street')->assertDontSee('2 New Street');
    }

    Edge cases worth covering: client with no address set at all (nullable snapshot fields), and a client that's soft-deleted after the invoice exists (invoice must still render its snapshot, not fail on a missing relation).

  8. added a commit that references this issue on Aug 22, 2026
    f7410db
  9. nielsdrost7 commented on Aug 22, 2026

    @nielsdrost7
    Collaborator

    @justnx your feature was just developed here: #728 <<< it's for InvoicePlane v2

    It stores all company information at the time of creating the invoice on the invoice record itself and uses it for creating the invoices, things like that.
    It's not merged yet into InvoicePlane version 2, but it's on its way.

  10. added a commit that references this issue on Aug 22, 2026
    c34fe43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

refinedRefined with SMART plan / test suggestions

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions