insert([ ['id' => 1, 'name' => 'Media'], ['id' => 2, 'name' => 'Management'], ]); }); test('it moves media date range and spending to management items on distinct referenced invoices', function () { $client = Client::factory()->create(); $parentInvoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-PARENT', ]); ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-LINKED-1', 'linked_invoice_id' => $parentInvoice->id, ]); $secondLinkedInvoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-LINKED-2', 'linked_invoice_id' => $parentInvoice->id, ]); $unlinkedInvoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-UNLINKED', ]); $parentPayment = ClientInvoicePayment::query()->create([ 'client_invoice_id' => $parentInvoice->id, 'payment_no' => 'P1', ]); $childPayment = ClientInvoicePayment::query()->create([ 'client_invoice_id' => $secondLinkedInvoice->id, 'payment_no' => 'P2', ]); $unlinkedPayment = ClientInvoicePayment::query()->create([ 'client_invoice_id' => $unlinkedInvoice->id, 'payment_no' => 'P3', ]); $parentMediaItem = ClientInvoicePaymentItem::query()->create([ 'client_invoice_payment_id' => $parentPayment->id, 'billing_item_types_id' => 1, 'start_date' => '2026-06-01', 'end_date' => '2026-06-30', 'spending' => 125.50, ]); $parentManagementItem = ClientInvoicePaymentItem::query()->create([ 'client_invoice_payment_id' => $parentPayment->id, 'billing_item_types_id' => 2, 'spending' => 10, ]); $childMediaItem = ClientInvoicePaymentItem::query()->create([ 'client_invoice_payment_id' => $childPayment->id, 'billing_item_types_id' => 1, 'start_date' => '2026-06-15', 'end_date' => '2026-06-20', 'spending' => 50, ]); $childManagementItem = ClientInvoicePaymentItem::query()->create([ 'client_invoice_payment_id' => $childPayment->id, 'billing_item_types_id' => 2, ]); $unlinkedMediaItem = ClientInvoicePaymentItem::query()->create([ 'client_invoice_payment_id' => $unlinkedPayment->id, 'billing_item_types_id' => 1, 'start_date' => '2026-07-01', 'end_date' => '2026-07-31', 'spending' => 99, ]); $unlinkedManagementItem = ClientInvoicePaymentItem::query()->create([ 'client_invoice_payment_id' => $unlinkedPayment->id, 'billing_item_types_id' => 2, ]); $this->artisan('customer:repair-linked-invoice-payment-items') ->expectsOutputToContain('Done. 1 media item merged into management, 1 media item removed, 0 skipped.') ->assertSuccessful(); expect(ClientInvoicePaymentItem::query()->whereKey($parentMediaItem->id)->exists())->toBeFalse() ->and($parentManagementItem->fresh()->start_date->toDateString())->toBe('2026-06-01') ->and($parentManagementItem->fresh()->end_date->toDateString())->toBe('2026-06-30') ->and((float) $parentManagementItem->fresh()->spending)->toBe(135.50) ->and($childMediaItem->fresh())->not->toBeNull() ->and($childManagementItem->fresh()->start_date)->toBeNull() ->and($unlinkedMediaItem->fresh())->not->toBeNull() ->and($unlinkedManagementItem->fresh()->start_date)->toBeNull() ->and((float) $unlinkedManagementItem->fresh()->spending)->toBe(0.0); }); test('dry run does not update or delete items', function () { $client = Client::factory()->create(); $parentInvoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-PARENT', ]); ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-LINKED', 'linked_invoice_id' => $parentInvoice->id, ]); $payment = ClientInvoicePayment::query()->create([ 'client_invoice_id' => $parentInvoice->id, ]); $mediaItem = ClientInvoicePaymentItem::query()->create([ 'client_invoice_payment_id' => $payment->id, 'billing_item_types_id' => 1, 'start_date' => '2026-06-01', 'end_date' => '2026-06-30', 'spending' => 125.50, ]); $managementItem = ClientInvoicePaymentItem::query()->create([ 'client_invoice_payment_id' => $payment->id, 'billing_item_types_id' => 2, ]); $this->artisan('customer:repair-linked-invoice-payment-items', ['--dry-run' => true]) ->expectsOutputToContain('Done. 1 media item merged into management, 1 media item removed, 0 skipped.') ->assertSuccessful(); expect($mediaItem->fresh())->not->toBeNull() ->and($managementItem->fresh()->start_date)->toBeNull() ->and($managementItem->fresh()->end_date)->toBeNull() ->and((float) $managementItem->fresh()->spending)->toBe(0.0); });