Tax Period Reports
This commit is contained in:
parent
ef36f8d1e4
commit
739ef74f81
|
|
@ -53,16 +53,6 @@ class PreviewReport implements ShouldQueue
|
|||
Cache::put($this->hash, base64_encode($report), 60 * 60);
|
||||
}
|
||||
|
||||
// public function middleware()
|
||||
// {
|
||||
// return [
|
||||
// (new WithoutOverlapping("report-{$this->company->company_key}-{$this->report_class}"))
|
||||
// ->releaseAfter(60)
|
||||
// ->expireAfter(60) // 5 minutes
|
||||
// ->dontRelease(), // This prevents the job from being marked as a "release" which counts towards attempts
|
||||
// ];
|
||||
// }
|
||||
|
||||
/**
|
||||
* Handle a job failure.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,225 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\Invoice;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Activity;
|
||||
use App\Models\TransactionEvent;
|
||||
use Illuminate\Support\Collection;
|
||||
use App\DataMapper\TaxReport\TaxDetail;
|
||||
use App\DataMapper\TaxReport\TaxReport;
|
||||
use App\DataMapper\TaxReport\TaxSummary;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use App\DataMapper\TransactionEventMetadata;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
|
||||
class InvoiceTransactionEventEntryCash
|
||||
{
|
||||
private Collection $payments;
|
||||
|
||||
private float $paid_ratio;
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
*/
|
||||
public function run($invoice, $start_date, $end_date)
|
||||
{
|
||||
|
||||
$this->setPaidRatio($invoice);
|
||||
|
||||
$this->payments = $invoice->payments->flatMap(function ($payment) use ($start_date, $end_date) {
|
||||
return $payment->invoices()->get()->map(function ($invoice) use ($payment) {
|
||||
return [
|
||||
'number' => $payment->number,
|
||||
'amount' => $invoice->pivot->amount,
|
||||
'refunded' => $invoice->pivot->refunded,
|
||||
'date' => $invoice->pivot->created_at->format('Y-m-d'),
|
||||
];
|
||||
})->filter(function ($payment) use ($start_date, $end_date) {
|
||||
// Filter payments where the pivot created_at is within the date boundaries
|
||||
return \Carbon\Carbon::parse($payment['date'])->isBetween($start_date, $end_date);
|
||||
});
|
||||
});
|
||||
|
||||
TransactionEvent::create([
|
||||
'invoice_id' => $invoice->id,
|
||||
'client_id' => $invoice->client_id,
|
||||
'client_balance' => $invoice->client->balance,
|
||||
'client_paid_to_date' => $invoice->client->paid_to_date,
|
||||
'client_credit_balance' => $invoice->client->credit_balance,
|
||||
'invoice_balance' => $invoice->balance ?? 0,
|
||||
'invoice_amount' => $invoice->amount ?? 0 ,
|
||||
'invoice_partial' => $invoice->partial ?? 0,
|
||||
'invoice_paid_to_date' => $invoice->paid_to_date ?? 0,
|
||||
'invoice_status' => $invoice->is_deleted ? 7 : $invoice->status_id,
|
||||
'event_id' => TransactionEvent::PAYMENT_CASH,
|
||||
'timestamp' => now()->timestamp,
|
||||
'metadata' => $this->getMetadata($invoice),
|
||||
'period' => $end_date,
|
||||
]);
|
||||
}
|
||||
|
||||
private function setPaidRatio(Invoice $invoice): self
|
||||
{
|
||||
if ($invoice->amount == 0) {
|
||||
$this->paid_ratio = 0;
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->paid_ratio = $invoice->paid_to_date / $invoice->amount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
* @param mixed $invoice
|
||||
*/
|
||||
private function getCancelledMetaData($invoice)
|
||||
{
|
||||
|
||||
$calc = $invoice->calc();
|
||||
|
||||
$details = [];
|
||||
|
||||
$taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
|
||||
|
||||
foreach ($taxes as $tax) {
|
||||
$tax_detail = [
|
||||
'tax_name' => $tax['name'],
|
||||
'tax_rate' => $tax['tax_rate'],
|
||||
'taxable_amount' => $tax['base_amount'] ?? $calc->getNetSubtotal(),
|
||||
'tax_amount' => $this->calculateRatio($tax['total']),
|
||||
'tax_amount_paid' => $this->calculateRatio($tax['total']),
|
||||
'tax_amount_remaining' => 0,
|
||||
];
|
||||
$details[] = $tax_detail;
|
||||
}
|
||||
|
||||
return new TransactionEventMetadata([
|
||||
'tax_report' => [
|
||||
'tax_details' => $details,
|
||||
'payment_history' => $this->payments->toArray(),
|
||||
'tax_summary' => [
|
||||
'total_taxes' => $invoice->total_taxes,
|
||||
'total_paid' => $this->getTotalTaxPaid($invoice),
|
||||
'status' => 'cancelled',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all tax details to 0
|
||||
*
|
||||
* @param mixed $invoice
|
||||
*/
|
||||
private function getDeletedMetaData($invoice)
|
||||
{
|
||||
|
||||
$calc = $invoice->calc();
|
||||
|
||||
$details = [];
|
||||
|
||||
$taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
|
||||
|
||||
foreach ($taxes as $tax) {
|
||||
$tax_detail = [
|
||||
'tax_name' => $tax['name'],
|
||||
'tax_rate' => $tax['tax_rate'],
|
||||
'taxable_amount' => $tax['base_amount'] ?? $calc->getNetSubtotal(),
|
||||
'tax_amount' => $tax['total'],
|
||||
'tax_amount_paid' => $this->calculateRatio($tax['total']),
|
||||
'tax_amount_remaining' => 0,
|
||||
];
|
||||
$details[] = $tax_detail;
|
||||
}
|
||||
|
||||
return new TransactionEventMetadata([
|
||||
'tax_report' => [
|
||||
'tax_details' => $details,
|
||||
'payment_history' => $this->payments->toArray(),
|
||||
'tax_summary' => [
|
||||
'total_taxes' => $invoice->total_taxes,
|
||||
'total_paid' => $this->getTotalTaxPaid($invoice),0,
|
||||
'status' => 'deleted',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
private function getMetadata($invoice)
|
||||
{
|
||||
|
||||
// if ($invoice->status_id == Invoice::STATUS_CANCELLED) {
|
||||
// return $this->getCancelledMetaData($invoice);
|
||||
// } elseif ($invoice->is_deleted) {
|
||||
// return $this->getDeletedMetaData($invoice);
|
||||
// }
|
||||
|
||||
$calc = $invoice->calc();
|
||||
|
||||
$details = [];
|
||||
|
||||
$taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
|
||||
|
||||
foreach ($taxes as $tax) {
|
||||
$tax_detail = [
|
||||
'tax_name' => $tax['name'],
|
||||
'tax_rate' => $tax['tax_rate'],
|
||||
'taxable_amount' => $tax['base_amount'] ?? $calc->getNetSubtotal(),
|
||||
'tax_amount' => $tax['total'],
|
||||
'tax_amount_paid' => $this->calculateRatio($tax['total']),
|
||||
'tax_amount_remaining' => $tax['total'] - $this->calculateRatio($tax['total']),
|
||||
];
|
||||
$details[] = $tax_detail;
|
||||
}
|
||||
|
||||
return new TransactionEventMetadata([
|
||||
'tax_report' => [
|
||||
'tax_details' => $details,
|
||||
'payment_history' => $this->payments->toArray(),
|
||||
'tax_summary' => [
|
||||
'total_taxes' => $invoice->total_taxes,
|
||||
'total_paid' => $this->getTotalTaxPaid($invoice),
|
||||
'status' => 'updated',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ class TaxPeriodReport extends BaseExport
|
|||
|
||||
public function run()
|
||||
{
|
||||
|
||||
nlog($this->input);
|
||||
MultiDB::setDb($this->company->db);
|
||||
App::forgetInstance('translator');
|
||||
App::setLocale($this->company->locale());
|
||||
|
|
@ -97,16 +97,20 @@ class TaxPeriodReport extends BaseExport
|
|||
*/
|
||||
private function initializeData(): self
|
||||
{
|
||||
Invoice::withTrashed()
|
||||
$q = Invoice::withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [2,3,4,5])
|
||||
->whereBetween('date', ['1970-01-01', now()->subMonth()->endOfMonth()->format('Y-m-d')])
|
||||
->whereDoesntHave('transaction_events')
|
||||
->cursor()
|
||||
->whereDoesntHave('transaction_events');
|
||||
|
||||
nlog($q->count(). " records to update");
|
||||
|
||||
$q->cursor()
|
||||
->each(function($invoice){
|
||||
|
||||
if($invoice->status_id == Invoice::STATUS_SENT){
|
||||
nlog($invoice->id. " - ".$invoice->number);
|
||||
(new InvoiceTransactionEventEntry())->run($invoice, \Carbon\Carbon::parse($invoice->date)->endOfMonth()->format('Y-m-d'));
|
||||
}
|
||||
elseif(in_array($invoice->status_id, [Invoice::STATUS_PAID, Invoice::STATUS_PARTIAL])){
|
||||
|
|
@ -121,10 +125,14 @@ class TaxPeriodReport extends BaseExport
|
|||
->map(function ($group) {
|
||||
return $group->first();
|
||||
})->each(function ($pp){
|
||||
nlog($pp->paymentable->id. " - Paid Updater");
|
||||
(new InvoiceTransactionEventEntryCash())->run($pp->paymentable, \Carbon\Carbon::parse($pp->created_at)->startOfMonth()->format('Y-m-d'), \Carbon\Carbon::parse($pp->created_at)->endOfMonth()->format('Y-m-d'));
|
||||
});
|
||||
|
||||
}
|
||||
else {
|
||||
nlog($invoice->id. " - ".$invoice->status_id. " NOT PROCESSED");
|
||||
}
|
||||
});
|
||||
|
||||
return $this;
|
||||
|
|
@ -132,6 +140,8 @@ class TaxPeriodReport extends BaseExport
|
|||
|
||||
private function resolveQuery()
|
||||
{
|
||||
nlog($this->start_date. " - ".$this->end_date);
|
||||
nlog($this->company->id);
|
||||
$query = Invoice::query()
|
||||
->withTrashed()
|
||||
->with('transaction_events')
|
||||
|
|
@ -180,15 +190,19 @@ class TaxPeriodReport extends BaseExport
|
|||
case 'last_month':
|
||||
$this->start_date = now()->startOfMonth()->subMonth()->format('Y-m-d');
|
||||
$this->end_date = now()->startOfMonth()->subMonth()->endOfMonth()->format('Y-m-d');
|
||||
break;
|
||||
case 'this_quarter':
|
||||
$this->start_date = (new \Carbon\Carbon('0 months'))->startOfQuarter()->format('Y-m-d');
|
||||
$this->end_date = (new \Carbon\Carbon('0 months'))->endOfQuarter()->format('Y-m-d');
|
||||
break;
|
||||
case 'last_quarter':
|
||||
$this->start_date = (new \Carbon\Carbon('-3 months'))->startOfQuarter()->format('Y-m-d');
|
||||
$this->end_date = (new \Carbon\Carbon('-3 months'))->endOfQuarter()->format('Y-m-d');
|
||||
break;
|
||||
case 'last365_days':
|
||||
$this->start_date = now()->startOfDay()->subDays(365)->format('Y-m-d');
|
||||
$this->end_date = now()->startOfDay()->format('Y-m-d');
|
||||
break;
|
||||
case 'this_year':
|
||||
|
||||
$first_month_of_year = $this->company->first_month_of_year ?? 1;
|
||||
|
|
@ -200,6 +214,7 @@ class TaxPeriodReport extends BaseExport
|
|||
|
||||
$this->start_date = $fin_year_start->format('Y-m-d');
|
||||
$this->end_date = $fin_year_start->copy()->addYear()->subDay()->format('Y-m-d');
|
||||
break;
|
||||
case 'last_year':
|
||||
|
||||
$first_month_of_year = $this->company->first_month_of_year ?? 1;
|
||||
|
|
@ -212,6 +227,8 @@ class TaxPeriodReport extends BaseExport
|
|||
|
||||
$this->start_date = $fin_year_start->format('Y-m-d');
|
||||
$this->end_date = $fin_year_start->copy()->addYear()->subDay()->format('Y-m-d');
|
||||
|
||||
break;
|
||||
case 'custom':
|
||||
|
||||
try {
|
||||
|
|
@ -224,6 +241,7 @@ class TaxPeriodReport extends BaseExport
|
|||
|
||||
$this->start_date = $custom_start_date->format('Y-m-d');
|
||||
$this->end_date = $custom_end_date->format('Y-m-d');
|
||||
break;
|
||||
case 'all':
|
||||
default:
|
||||
$this->start_date = now()->startOfYear()->format('Y-m-d');
|
||||
|
|
@ -310,6 +328,7 @@ class TaxPeriodReport extends BaseExport
|
|||
|
||||
$query = $this->resolveQuery();
|
||||
|
||||
nlog($query->count(). "records to iterate");
|
||||
$this->data['invoices'] = [];
|
||||
$this->data['invoices'][] =
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue