You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Core]: The user shall be able to produce a report with total V.A.T amount that is due by the company according to the sales of products made #73
// Assert that the total VAT amount in the response matches the expected value
$response->assertJson(['total_vat' => number_format($expectedVat, 2, '.', '')]);
Let a company generate a report of total VAT due, aggregated from actual invoice tax data, for a given period. Current state: the issue body's own Arrange/Act/Assert block references Sale, Product::vat_rate, and a vat.report route that do not exist anywhere in the codebase — it's boilerplate, not a real spec, and is disregarded here in favour of what actually exists. What does exist: Invoice has a real invoice_tax_total column (cast decimal:4, populated by InvoiceFactory/the invoice calculator on every invoice), and a taxRates(): BelongsToMany relation to Modules\Core\Models\TaxRate via the invoice_tax_rates pivot table (with tax_total on the pivot). Modules\Core\Services\InvoiceTaxRateService exists as a class but is currently an empty stub — there is no service that sums tax across invoices for a date range today.
Measurable
A new VatReportService::generate($company, $from, $to) sums invoice_tax_total for the company's invoices within the given period.
New Modules/Core/Services/VatReportService.php (or implement the currently-empty InvoiceTaxRateService — confirm which is the intended home before starting) with a generate(Company $company, Carbon $from, Carbon $to) method querying Invoice::query()->whereBetween('invoiced_at', [$from, $to]) scoped by the existing BelongsToCompany global scope, summing invoice_tax_total and grouping taxRates() pivot data by tax_rate_id.
Return a simple value object (or array) with totalVat and byTaxRate, following the pattern used by the existing ReportDataSource/ReportFieldService reporting scaffolding in Modules/Core.
Add a Filament page (admin or company panel — confirm which panel this targets) to select a date range and render the totals; there is no existing "VAT report" page to extend.
Add PHPUnit coverage in Modules/Core/Tests/Feature/.
Confirm with the reporter whether "VAT due" should also account for VAT reclaimable on expenses (input VAT) or only output VAT on sales, since the title says "according to the sales of products made" but real-world VAT remittance usually nets the two.
Relevant
VAT remittance is a legal obligation for most companies using this software; without an aggregate report, a company would have to manually total tax across every invoice for a filing period.
Time-bound
Sprint 6 (confirm output-only vs. net VAT scope before starting, per the Achievable note above).
Activity
nielsdrost7 commentedon Jan 2, 2026
@coderabbitai make pull-request with AAA test Scenario's: Arrange, Act, Assert. And also make sure you test edge cases
coderabbitai commentedon Jan 2, 2026
Rate Limit Exceeded
@nielsdrost7 have exceeded the limit for the number of chat messages per hour. Please wait 0 minutes and 37 seconds before sending another message.
nielsdrost7 commentedon Jul 16, 2026
SMART Plan — VAT due report
Specific
Let a company generate a report of total VAT due, aggregated from actual invoice tax data, for a given period. Current state: the issue body's own Arrange/Act/Assert block references
Sale,Product::vat_rate, and avat.reportroute that do not exist anywhere in the codebase — it's boilerplate, not a real spec, and is disregarded here in favour of what actually exists. What does exist:Invoicehas a realinvoice_tax_totalcolumn (castdecimal:4, populated byInvoiceFactory/the invoice calculator on every invoice), and ataxRates(): BelongsToManyrelation toModules\Core\Models\TaxRatevia theinvoice_tax_ratespivot table (withtax_totalon the pivot).Modules\Core\Services\InvoiceTaxRateServiceexists as a class but is currently an empty stub — there is no service that sums tax across invoices for a date range today.Measurable
VatReportService::generate($company, $from, $to)sumsinvoice_tax_totalfor the company's invoices within the given period.it_sums_invoice_tax_totals_within_the_given_period()#[Test] public function it_sums_invoice_tax_totals_within_the_given_period(): void { /* Arrange */ Invoice::factory()->for($this->company)->create([ 'invoiced_at' => now()->subDays(5)->toDateString(), 'invoice_tax_total' => 42.5000, ]); /* Act */ $report = app(VatReportService::class)->generate($this->company, now()->subMonth(), now()); /* Assert */ $this->assertSame(42.5, $report->totalVat); }invoiced_at) are excluded.it_excludes_invoices_outside_the_requested_period()#[Test] public function it_excludes_invoices_outside_the_requested_period(): void { /* Arrange */ Invoice::factory()->for($this->company)->create([ 'invoiced_at' => now()->subYear()->toDateString(), 'invoice_tax_total' => 100.0, ]); /* Act */ $report = app(VatReportService::class)->generate($this->company, now()->subMonth(), now()); /* Assert */ $this->assertSame(0.0, $report->totalVat); }BelongsToCompany's global scope).it_does_not_include_another_companys_invoice_tax()#[Test] public function it_does_not_include_another_companys_invoice_tax(): void { /* Arrange */ $otherCompany = Company::factory()->create(); Invoice::factory()->for($otherCompany)->create([ 'invoiced_at' => now()->toDateString(), 'invoice_tax_total' => 500.0, ]); /* Act */ $report = app(VatReportService::class)->generate($this->company, now()->subMonth(), now()); /* Assert */ $this->assertSame(0.0, $report->totalVat); }InvoiceusesSoftDeletes.it_excludes_soft_deleted_invoices_from_the_vat_total()#[Test] public function it_excludes_soft_deleted_invoices_from_the_vat_total(): void { /* Arrange */ $invoice = Invoice::factory()->for($this->company)->create([ 'invoiced_at' => now()->toDateString(), 'invoice_tax_total' => 30.0, ]); $invoice->delete(); /* Act */ $report = app(VatReportService::class)->generate($this->company, now()->subMonth(), now()); /* Assert */ $this->assertSame(0.0, $report->totalVat); }TaxRate(via theinvoice_tax_ratespivot'stax_total) is available, not just a single grand total.it_breaks_down_vat_by_tax_rate()#[Test] public function it_breaks_down_vat_by_tax_rate(): void { /* Arrange */ $taxRate = TaxRate::factory()->for($this->company)->create(['percentage' => 21]); $invoice = Invoice::factory()->for($this->company)->create(['invoiced_at' => now()->toDateString()]); $invoice->taxRates()->attach($taxRate->id, ['tax_total' => 21.0]); /* Act */ $report = app(VatReportService::class)->generate($this->company, now()->subMonth(), now()); /* Assert */ $this->assertSame(21.0, $report->byTaxRate[$taxRate->id] ?? null); }Achievable
Modules/Core/Services/VatReportService.php(or implement the currently-emptyInvoiceTaxRateService— confirm which is the intended home before starting) with agenerate(Company $company, Carbon $from, Carbon $to)method queryingInvoice::query()->whereBetween('invoiced_at', [$from, $to])scoped by the existingBelongsToCompanyglobal scope, summinginvoice_tax_totaland groupingtaxRates()pivot data bytax_rate_id.totalVatandbyTaxRate, following the pattern used by the existingReportDataSource/ReportFieldServicereporting scaffolding inModules/Core.Modules/Core/Tests/Feature/.Relevant
VAT remittance is a legal obligation for most companies using this software; without an aggregate report, a company would have to manually total tax across every invoice for a filing period.
Time-bound
Sprint 6 (confirm output-only vs. net VAT scope before starting, per the Achievable note above).