Improvements for tax reporting

This commit is contained in:
David Bomba 2025-11-21 09:30:08 +11:00
parent 694f1476de
commit 56b1abcf54
8 changed files with 85 additions and 182 deletions

View File

@ -26,7 +26,7 @@ class TaxDetail
public string $tax_status; // "collected", "pending", "refundable", "partially_paid", "adjustment"
// Adjustment-specific fields (used when tax_status is "adjustment")
public ?string $adjustment_reason; // "invoice_cancelled", "tax_rate_change", "exemption_applied", "correction"
public ?string $postal_code; // "invoice_cancelled", "tax_rate_change", "exemption_applied", "correction"
public float $line_total;
public float $total_tax;
@ -41,7 +41,7 @@ class TaxDetail
$this->tax_amount = $attributes['tax_amount'] ?? 0.0;
$this->tax_status = $attributes['tax_status'] ?? 'pending';
// Adjustment fields
$this->adjustment_reason = $attributes['adjustment_reason'] ?? null;
$this->postal_code = $attributes['postal_code'] ?? null;
$this->line_total = $attributes['line_total'] ?? 0.0;
$this->total_tax = $attributes['total_tax'] ?? 0.0;
@ -57,7 +57,7 @@ class TaxDetail
'taxable_amount' => $this->taxable_amount,
'tax_amount' => $this->tax_amount,
'tax_status' => $this->tax_status,
'adjustment_reason' => $this->adjustment_reason,
'postal_code' => $this->postal_code,
'line_total' => $this->line_total,
'total_tax' => $this->total_tax,
];

View File

@ -171,10 +171,10 @@ class InvoiceTransactionEventEntry
$previous_tax_details = $previous_transaction_event->metadata->tax_report->tax_details;
$postal_code = $invoice->client->postal_code;
foreach ($taxes as $tax) {
$previousLine = collect($previous_tax_details)->where('tax_name', $tax['name'])->first() ?? null;
nlog($previousLine);
nlog($tax);
$tax_detail = [
'tax_name' => $tax['name'],
@ -183,12 +183,9 @@ nlog($tax);
'total_tax' => $tax['total'],
'taxable_amount' => ($tax['base_amount'] ?? $calc->getNetSubtotal()) - ($previousLine->line_total ?? 0),
'tax_amount' => $tax['total'] - ($previousLine->total_tax ?? 0),
// 'taxable_amount_adjustment' => ($tax['base_amount'] ?? $calc->getNetSubtotal()) - ($previousLine->taxable_amount ?? 0),
// 'tax_amount_adjustment' => $tax['total'] - ($previousLine->tax_amount ?? 0),
'postal_code' => $postal_code,
];
nlog($tax_detail);
$details[] = $tax_detail;
}
@ -229,15 +226,7 @@ nlog($tax);
$taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
//If there is a previous transaction event, we need to consider the taxable amount.
// $previous_transaction_event = TransactionEvent::where('event_id', TransactionEvent::INVOICE_UPDATED)
// ->where('invoice_id', $invoice->id)
// ->orderBy('timestamp', 'desc')
// ->first();
if($this->paid_ratio == 0){
// setup a 0/0 recorded
}
$postal_code = $invoice->client->postal_code;
foreach ($taxes as $tax) {
$tax_detail = [
@ -247,6 +236,8 @@ nlog($tax);
'tax_amount' => ($tax['total'] * $this->paid_ratio * -1),
'line_total' => $tax['base_amount'] * $this->paid_ratio * -1,
'total_tax' => $tax['total'] * $this->paid_ratio * -1,
'postal_code' => $postal_code,
];
$details[] = $tax_detail;
}
@ -280,15 +271,8 @@ nlog($tax);
$taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
//If there is a previous transaction event, we need to consider the taxable amount.
// $previous_transaction_event = TransactionEvent::where('event_id', TransactionEvent::INVOICE_UPDATED)
// ->where('invoice_id', $invoice->id)
// ->orderBy('timestamp', 'desc')
// ->first();
$postal_code = $invoice->client->postal_code;
if($this->paid_ratio == 0){
// setup a 0/0 recorded
}
foreach ($taxes as $tax) {
$tax_detail = [
@ -298,6 +282,7 @@ nlog($tax);
'tax_amount' => ($tax['total'] * $this->paid_ratio),
'line_total' => $tax['base_amount'] * $this->paid_ratio,
'total_tax' => $tax['total'] * $this->paid_ratio,
'postal_code' => $postal_code,
];
$details[] = $tax_detail;
}
@ -330,6 +315,7 @@ nlog($tax);
$details = [];
$taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
$postal_code = $invoice->client->postal_code;
foreach ($taxes as $tax) {
$tax_detail = [
@ -339,6 +325,7 @@ nlog($tax);
'tax_amount' => $tax['total'] * -1,
'line_total' => $tax['base_amount'] * -1,
'total_tax' => $tax['total'] * -1,
'postal_code' => $postal_code,
];
$details[] = $tax_detail;
}
@ -375,6 +362,7 @@ nlog($tax);
$details = [];
$taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
$postal_code = $invoice->client->postal_code;
foreach ($taxes as $tax) {
$tax_detail = [
@ -384,6 +372,7 @@ nlog($tax);
'tax_amount' => $tax['total'],
'line_total' => $tax['base_amount'] ?? $calc->getNetSubtotal(),
'total_tax' => $tax['total'],
'postal_code' => $postal_code,
];
$details[] = $tax_detail;
}
@ -401,18 +390,5 @@ nlog($tax);
]);
}
private function getTotalTaxPaid($invoice)
{
if($invoice->amount == 0){
return 0;
}
$total_paid = $this->payments->sum('amount') - $this->payments->sum('refunded');
return round($invoice->total_taxes * ($total_paid / $invoice->amount), 2);
}
}

View File

@ -84,15 +84,9 @@ class InvoiceTransactionEventEntryCash
$this->paid_ratio = $invoice->paid_to_date / $invoice->amount;
nlog("paid ratio => {$this->paid_ratio}");
return $this;
}
private function calculateRatio(float $amount): float
{
return round($amount * $this->paid_ratio, 2);
}
private function getMetadata($invoice)
{
@ -108,6 +102,9 @@ class InvoiceTransactionEventEntryCash
'tax_rate' => $tax['tax_rate'],
'taxable_amount' => ($tax['base_amount'] ?? $calc->getNetSubtotal()) * $this->paid_ratio,
'tax_amount' => $tax['total'] * $this->paid_ratio,
'line_total' => $tax['base_amount'],
'total_tax' => $tax['total'],
'postal_code' => $invoice->client->postal_code,
];
$details[] = $tax_detail;
}

View File

@ -54,7 +54,7 @@ class PaymentTransactionEventEntry implements ShouldQueue
public function handle()
{
nlog("set invoice adjustment => {$this->invoice_adjustment}");
try{
$this->runLog();
}
@ -99,12 +99,9 @@ class PaymentTransactionEventEntry implements ShouldQueue
->get()
->filter(function($invoice){
//only insert adjustment entries if we are after the end of the month!!
nlog(Carbon::parse($invoice->date)->endOfMonth()->isBefore(now()->addSeconds($this->payment->company->timezone_offset())));
return Carbon::parse($invoice->date)->endOfMonth()->isBefore(now()->addSeconds($this->payment->company->timezone_offset()));
})
->each(function($invoice){
// nlog(" I am inserting!!! ");
$this->setPaidRatio($invoice);
//delete any other payment mutations here if this is a delete event, the refunds are redundant in this time period
@ -156,44 +153,6 @@ class PaymentTransactionEventEntry implements ShouldQueue
return $this;
}
private function getRefundRatio(Invoice $invoice): float
{
// For partial refunds, calculate ratio based on what was previously paid
// Get the previous transaction event to find the historical paid_to_date
if ($this->invoice_adjustment <= 0) {
return 0;
}
// Get the most recent transaction event to see what was previously recorded
$previous_event = $invoice->transaction_events()
->orderBy('id', 'desc')
->first();
if ($previous_event && $previous_event->invoice_paid_to_date > 0) {
// Ratio: refund_amount / previous_paid_to_date
// This gives us the portion of the previous payment being refunded
nlog("Using previous event: refund {$this->invoice_adjustment} / {$previous_event->invoice_paid_to_date}");
return $this->invoice_adjustment / $previous_event->invoice_paid_to_date;
}
// Fallback: calculate what paid_to_date was BEFORE this refund
// Since the refund has already been processed, paid_to_date is already reduced
// So: paid_to_date_before_refund = current_paid_to_date + refund_amount
$paid_to_date_before = $invoice->paid_to_date + $this->invoice_adjustment;
if ($paid_to_date_before > 0) {
nlog("No previous event: refund {$this->invoice_adjustment} / {$paid_to_date_before}");
return $this->invoice_adjustment / $paid_to_date_before;
}
return 0;
}
private function calculateRatio(float $amount): float
{
return round($amount * $this->paid_ratio, 2);
}
/**
* Existing tax details are not deleted, but pending taxes are set to 0
*
@ -224,6 +183,9 @@ class PaymentTransactionEventEntry implements ShouldQueue
'tax_rate' => $tax['tax_rate'],
'taxable_amount' => round($base_amount * $refund_ratio, 2) * -1,
'tax_amount' => round($tax['total'] * $refund_ratio, 2) * -1,
'line_total' => $base_amount,
'total_tax' => $tax['total'],
'postal_code' => $invoice->client->postal_code,
];
$details[] = $tax_detail;
}
@ -266,6 +228,9 @@ class PaymentTransactionEventEntry implements ShouldQueue
'taxable_amount' => $base_amount * -1,
'tax_amount' => $tax['total'] * -1,
'tax_status' => 'payment_deleted',
'line_total' => $base_amount,
'total_tax' => $tax['total'],
'postal_code' => $invoice->client->postal_code,
];
$details[] = $tax_detail;
@ -304,7 +269,7 @@ class PaymentTransactionEventEntry implements ShouldQueue
$total_paid = $this->payments->sum('amount') - $this->payments->sum('refunded');
nlog("total paid => {$total_paid} - total taxes => {$invoice->total_taxes} - amount => {$invoice->amount}");
// nlog("total paid => {$total_paid} - total taxes => {$invoice->total_taxes} - amount => {$invoice->amount}");
return round($invoice->total_taxes * ($total_paid / $invoice->amount), 2);

View File

@ -40,6 +40,7 @@ class InvoiceItemReportRow
ctrans('texts.tax_amount'),
ctrans('texts.taxable_amount'),
ctrans('texts.status'),
ctrans('texts.postal_code'),
];
if ($regional_calculator) {
@ -62,6 +63,7 @@ class InvoiceItemReportRow
$this->tax_detail->tax_amount,
$this->tax_detail->taxable_amount,
$this->status->label(),
$this->tax_detail->postal_code,
];
return $this->appendRegionalColumns($row, $this->tax_detail->tax_amount);
@ -80,6 +82,7 @@ class InvoiceItemReportRow
$this->tax_detail->tax_amount,
$this->tax_detail->taxable_amount,
$this->status->label(),
$this->tax_detail->postal_code,
];
return $this->appendRegionalColumns($row, $this->tax_detail->tax_amount);

View File

@ -24,6 +24,7 @@ class TaxDetail
public float $tax_amount,
public float $line_total = 0,
public float $total_tax = 0,
public string $postal_code = '',
) {}
/**
@ -38,6 +39,7 @@ class TaxDetail
tax_amount: $metadata->tax_amount ?? 0,
line_total: $metadata->line_total ?? 0,
total_tax: $metadata->total_tax ?? 0,
postal_code: $metadata->postal_code ?? '',
);
}
@ -126,6 +128,7 @@ class TaxDetail
'tax_amount' => $this->tax_amount,
'line_total' => $this->line_total,
'total_tax' => $this->total_tax,
'postal_code' => $this->postal_code,
];
}
}

View File

@ -318,7 +318,7 @@ class TaxPeriodReport extends BaseExport
{
$currency = $this->company->currency();
$formatted = number_format(9990.00, $currency->precision, $currency->decimal_separator, $currency->thousand_separator);
$formatted = number_format(90.00, $currency->precision, $currency->decimal_separator, $currency->thousand_separator);
$formatted = str_replace('9', '#', $formatted);
$this->number_format = $formatted;
@ -356,8 +356,10 @@ class TaxPeriodReport extends BaseExport
public function createInvoiceSummarySheet()
{
$worksheet_title = $this->cash_accounting ? ctrans('texts.cash_accounting') : ctrans('texts.accrual_accounting');
$worksheet = $this->spreadsheet->createSheet();
$worksheet->setTitle(ctrans('texts.invoice')." ".ctrans('texts.cash_vs_accrual'));
$worksheet->setTitle(substr(ctrans('texts.invoice')." ".$worksheet_title, 0, 30));
$worksheet->fromArray($this->data['invoices'], null, 'A1');
$worksheet->getStyle('B:B')->getNumberFormat()->setFormatCode($this->company->date_format());
@ -374,13 +376,14 @@ class TaxPeriodReport extends BaseExport
*/
public function createInvoiceItemSummarySheet()
{
$worksheet_title = $this->cash_accounting ? ctrans('texts.cash_accounting') : ctrans('texts.accrual_accounting');
$worksheet = $this->spreadsheet->createSheet();
$worksheet->setTitle(ctrans('texts.invoice_item')." ".ctrans('texts.cash_vs_accrual'));
$worksheet->setTitle(substr(ctrans('texts.invoice_item')." ".$worksheet_title, 0, 30));
$worksheet->fromArray($this->data['invoice_items'], null, 'A1');
$worksheet->getStyle('B:B')->getNumberFormat()->setFormatCode($this->company->date_format());
$worksheet->getStyle('D:D')->getNumberFormat()->setFormatCode($this->number_format."%");
$worksheet->getStyle('D:D')->getNumberFormat()->setFormatCode($this->number_format);
$worksheet->getStyle('E:E')->getNumberFormat()->setFormatCode($this->currency_format);
$worksheet->getStyle('F:F')->getNumberFormat()->setFormatCode($this->currency_format);

View File

@ -150,6 +150,7 @@ class TaxPeriodReportTest extends TestCase
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'is_deleted' => 0,
'postal_code' => rand(10000, 99999),
]);
}
@ -255,8 +256,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true,
];
$pl = new TaxPeriodReport($this->company, $payload);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testSingleInvoiceTaxReportStructure', $this->company, $payload);
$this->assertNotEmpty($data);
@ -305,8 +305,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false,
];
$pl = new TaxPeriodReport($this->company, $payload);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testSingleInvoiceTaxReportStructure', $this->company, $payload);
$this->assertCount(2, $invoice->transaction_events);
// Report should have data rows with proper structure
@ -438,8 +437,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true,
];
$pl = new TaxPeriodReport($this->company, $payload);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceReportingOverMultiplePeriodsWithAccrualAccountingCheckAdjustmentsForIncreases', $this->company, $payload);
$this->assertCount(2, $data['invoices']); // Header + 1 delta row
$this->assertCount(2, $data['invoice_items']); // Header + 1 delta row
@ -557,8 +555,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true,
];
$pl = new TaxPeriodReport($this->company, $payload);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceReportingOverMultiplePeriodsWithAccrualAccountingCheckAdjustmentsForDecreases', $this->company, $payload);
$this->assertCount(2, $data['invoices']); // Header + 1 delta row
$this->assertCount(2, $data['invoice_items']); // Header + 1 delta row
@ -641,8 +638,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true, //accrual
];
$pl = new TaxPeriodReport($this->company, $payload);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceReportingOverMultiplePeriodsWithCashAccountingCheckAdjustments', $this->company, $payload);
$transaction_event = $invoice->transaction_events()
->where('event_id', '!=', TransactionEvent::INVOICE_UPDATED)
@ -763,8 +759,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, //cash
];
$pl = new TaxPeriodReport($this->company, $payload);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceWithRefundAndCashReportsAreCorrect', $this->company, $payload);
$invoice = $invoice->fresh();
$payment = $invoice->payments()->first();
@ -866,8 +861,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, //cash
];
$pl = new TaxPeriodReport($this->company, $payload);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceWithRefundAndCashReportsAreCorrectAcrossReportingPeriods', $this->company, $payload);
// Verify October report data (payment in same period)
$this->assertCount(2, $data['invoices']); // Header + 1 payment row
@ -930,9 +924,8 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, //cash
];
$pl = new TaxPeriodReport($this->company, $payload);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceWithRefundAndCashReportsAreCorrectAcrossReportingPeriods', $this->company, $payload);
// nlog($invoice->fresh()->transaction_events()->get()->toArray());
// nlog($data);
@ -962,8 +955,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, //cash
];
$pl = new TaxPeriodReport($this->company, $payload);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceWithRefundAndCashReportsAreCorrectAcrossReportingPeriods', $this->company, $payload);
// Verify combined October-November report (payment + adjustment)
$this->assertCount(3, $data['invoices']); // Header + payment row + adjustment row
@ -1049,8 +1041,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true, // accrual
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testCancelledInvoiceInSamePeriodAccrual', $this->company, $payload, false);
// Should have cancelled status, but no tax liability for unpaid portion
$this->assertCount(2, $data['invoices']); // Header + 1 invoice
@ -1115,8 +1106,7 @@ class TaxPeriodReportTest extends TestCase
// Move to next period and cancel
$this->travelTo(\Carbon\Carbon::createFromDate(2026, 1, 2)->startOfDay());
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testCancelledInvoiceInNextPeriodAccrual', $this->company, $payload, false);
// Verify December report shows the invoice created
$this->assertCount(2, $data['invoices']); // Header + 1 invoice
@ -1150,8 +1140,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true,
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testCancelledInvoiceInNextPeriodAccrual', $this->company, $payload, false);
// Verify January report shows the cancellation
$this->assertGreaterThanOrEqual(2, count($data['invoices'])); // At least header + 1 invoice
@ -1183,8 +1172,7 @@ class TaxPeriodReportTest extends TestCase
$payload['start_date'] = '2026-01-01';
$payload['end_date'] = '2026-01-31';
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testCancelledInvoiceInNextPeriodAccrual', $this->company, $payload, false);
// Find our specific invoice in January report
$found = false;
@ -1270,8 +1258,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true,
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: true);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testCancelledInvoiceWithPartialPaymentAccrual', $this->company, $payload, true);
$this->assertCount(2, $data['invoices']);
$invoice_report = $data['invoices'][1];
@ -1343,8 +1330,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true,
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: true);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testDeletedInvoiceInSamePeriodAccrual', $this->company, $payload, true);
// Should only have header row, no invoice data
$this->assertCount(1, $data['invoices']); // Just header
@ -1413,8 +1399,7 @@ class TaxPeriodReportTest extends TestCase
nlog("initial invoice");
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testDeletedPaidInvoiceInNextPeriodAccrual', $this->company, $payload, false);
// Verify October report shows the invoice before deletion
$this->assertCount(2, $data['invoices']); // Header + 1 invoice
@ -1452,8 +1437,7 @@ class TaxPeriodReportTest extends TestCase
// nlog("post delete");
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testDeletedPaidInvoiceInNextPeriodAccrual', $this->company, $payload, false);
// nlog($invoice->fresh()->transaction_events()->get()->toArray());
// (new InvoiceTransactionEventEntry())->run($invoice);
@ -1463,8 +1447,7 @@ class TaxPeriodReportTest extends TestCase
$this->assertEquals(-30, $data['invoices'][1][4]); // +$30 GST
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testDeletedPaidInvoiceInNextPeriodAccrual', $this->company, $payload, false);
$this->assertCount(2, $data['invoices']);
$this->assertEquals(-30, $data['invoices'][1][4]); // +$30 GST
@ -1473,8 +1456,7 @@ class TaxPeriodReportTest extends TestCase
$payload['start_date'] = '2025-11-01';
$payload['end_date'] = '2025-11-30';
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testDeletedPaidInvoiceInNextPeriodAccrual', $this->company, $payload, false);
nlog($data);
@ -1551,9 +1533,8 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true,
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testDeletedPaidInvoiceInNextPeriodAccrual', $this->company, $payload, false);
$this->assertCount(2, $data['invoices']);
$repo = new InvoiceRepository();
@ -1569,8 +1550,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true,
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testDeletedPaidInvoiceInNextPeriodAccrual', $this->company, $payload, false);
nlog($data);
@ -1655,8 +1635,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: true);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testPaymentDeletedInSamePeriodCash', $this->company, $payload, true);
// No payment, no cash report entry
$this->assertCount(1, $data['invoices']); // Just header
@ -1719,8 +1698,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testPaymentDeletedInNextPeriodCash', $this->company, $payload, false);
$this->assertCount(2, $data['invoices']); // Header + 1 payment row
$this->assertCount(2, $data['invoice_items']); // Header + 1 payment row
@ -1775,8 +1753,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testPaymentDeletedInNextPeriodCash', $this->company, $payload, false);
$this->assertCount(2, $data['invoices']); // Header + 1 deletion adjustment row
$this->assertCount(2, $data['invoice_items']); // Header + 1 deletion adjustment row
@ -1864,8 +1841,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true, // accrual
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testPaymentDeletedInNextPeriodAccrual', $this->company, $payload, false);
nlog($invoice->fresh()->transaction_events()->get()->toArray());
@ -1892,8 +1868,7 @@ class TaxPeriodReportTest extends TestCase
$payload['start_date'] = '2025-11-01';
$payload['end_date'] = '2025-11-30';
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: true);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testPaymentDeletedInNextPeriodAccrual', $this->company, $payload, true);
$this->assertCount(1, $data['invoices']); // Just header, no invoice events in November for accrual
@ -1959,8 +1934,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: true);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testCancelledInvoiceInSamePeriodCash', $this->company, $payload, true);
// Cash accounting: unpaid cancelled invoice = no tax liability
$this->assertCount(1, $data['invoices']); // Just header, no data
@ -2024,8 +1998,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: true);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testCancelledPaidInvoiceInSamePeriodCash', $this->company, $payload, true);
// Should show the payment event but cancellation offsets it
// The exact behavior depends on implementation
@ -2089,8 +2062,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testCancelledPartiallyPaidInvoiceInNextPeriodCash', $this->company, $payload, false);
$this->assertCount(2, $data['invoices']);
$this->assertEquals(10, $data['invoices'][1][4]); // +$30 GST from payment
@ -2110,8 +2082,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testCancelledPartiallyPaidInvoiceInNextPeriodCash', $this->company, $payload, false);
// Should show cancelled status with negative adjustment
$this->assertCount(1, $data['invoices']);
@ -2179,8 +2150,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testCancelledInvoiceWithPartialPaymentCash', $this->company, $payload, false);
$this->assertCount(2, $data['invoices']);
$this->assertEquals(10, $data['invoices'][1][4]); // +$15 GST (50% of $30)
@ -2200,8 +2170,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testCancelledInvoiceWithPartialPaymentCash', $this->company, $payload, false);
// Should show reversal of the 50% that was paid
$this->assertEquals(1, count($data['invoices']));
@ -2270,8 +2239,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true, // accrual
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceReversedWithCreditNoteNextPeriodAccrual', $this->company, $payload, false);
$this->assertCount(2, $data['invoices']);
$this->assertEquals(30, $data['invoices'][1][4]); // +$30 GST
@ -2351,8 +2319,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceReversedWithCreditNoteNextPeriodCash', $this->company, $payload, false);
nlog($data);
@ -2404,8 +2371,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceReversedWithCreditNoteNextPeriodCash', $this->company, $payload, false);
$reversed_event = $invoice->fresh()->transaction_events()->where('metadata->tax_report->tax_summary->status', 'reversed')->first();
$this->assertNotNull($reversed_event);
@ -2487,8 +2453,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testPartialPaymentThenFullRefundAcrossPeriods', $this->company, $payload, false);
$this->assertCount(2, $data['invoices']); // Header + 1 partial payment row
$this->assertCount(2, $data['invoice_items']); // Header + 1 partial payment row
@ -2549,8 +2514,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => false, // cash
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testPartialPaymentThenFullRefundAcrossPeriods', $this->company, $payload, false);
// Should show negative adjustment
$this->assertGreaterThanOrEqual(1, count($data['invoices']));
@ -2631,11 +2595,8 @@ class TaxPeriodReportTest extends TestCase
'date_range' => 'custom',
'is_income_billed' => true,
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$pl = null;
$data = $this->executeTaxPeriodReportAndSave('testInvoiceIncreasedMultipleTimesAcrossPeriods', $this->company, $payload, false);
$invoice = $invoice->fresh();
$this->assertEquals(1, $invoice->fresh()->transaction_events()->count());
@ -2662,8 +2623,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true,
];
$pb = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pb->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceIncreasedMultipleTimesAcrossPeriods', $this->company, $payload, false);
$this->assertEquals(2, $invoice->fresh()->transaction_events()->count());
@ -2683,8 +2643,7 @@ class TaxPeriodReportTest extends TestCase
'date_range' => 'custom',
'is_income_billed' => true,
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceIncreasedMultipleTimesAcrossPeriods', $this->company, $payload, false);
$this->assertEquals(3, $invoice->fresh()->transaction_events()->count());
@ -2697,8 +2656,7 @@ class TaxPeriodReportTest extends TestCase
'is_income_billed' => true,
];
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceIncreasedMultipleTimesAcrossPeriods', $this->company, $payload, false);
$this->assertCount(2, $data['invoices']); // Header + 1 invoice row
$this->assertCount(2, $data['invoice_items']); // Header + 1 item row
@ -2722,8 +2680,7 @@ class TaxPeriodReportTest extends TestCase
$payload['start_date'] = '2025-11-01';
$payload['end_date'] = '2025-11-30';
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceIncreasedMultipleTimesAcrossPeriods', $this->company, $payload, false);
$this->assertCount(2, $data['invoices']); // Header + 1 delta row
$this->assertCount(2, $data['invoice_items']); // Header + 1 delta row
@ -2747,8 +2704,7 @@ class TaxPeriodReportTest extends TestCase
$payload['start_date'] = '2025-12-01';
$payload['end_date'] = '2025-12-31';
$pl = new TaxPeriodReport($this->company, $payload, skip_initialization: false);
$data = $pl->boot()->getData();
$data = $this->executeTaxPeriodReportAndSave('testInvoiceIncreasedMultipleTimesAcrossPeriods', $this->company, $payload, false);
$this->assertCount(2, $data['invoices']); // Header + 1 delta row
$this->assertCount(2, $data['invoice_items']); // Header + 1 delta row