TaxReports
This commit is contained in:
parent
e204862488
commit
b78ba2b1fc
|
|
@ -0,0 +1,83 @@
|
|||
<?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\Http\Controllers\Reports;
|
||||
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\PreviewReport;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Services\Report\TaxPeriodReport;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class TaxPeriodReportController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
private string $filename = 'tax_period.xlsx';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/reports/tasks",
|
||||
* operationId="getTaskReport",
|
||||
* tags={"reports"},
|
||||
* summary="Task reports",
|
||||
* description="Export task reports",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(ref="#/components/schemas/GenericReportSchema")
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="success",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
if ($request->has('send_email') && $request->get('send_email') && $request->missing('output')) {
|
||||
SendToAdmin::dispatch($user->company(), $request->all(), TaxPeriodReport::class, $this->filename);
|
||||
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
|
||||
$hash = \Illuminate\Support\Str::uuid();
|
||||
|
||||
PreviewReport::dispatch($user->company(), $request->all(), TaxPeriodReport::class, $hash);
|
||||
|
||||
return response()->json(['message' => $hash], 200);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ use Illuminate\Queue\InteractsWithQueue;
|
|||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use App\Listeners\Invoice\InvoiceTransactionEventEntry;
|
||||
use App\Listeners\Invoice\InvoiceTransactionEventEntryAccrual;
|
||||
use App\Listeners\Invoice\InvoiceTransactionEventEntryCash;
|
||||
|
||||
class InvoiceTaxSummary implements ShouldQueue
|
||||
{
|
||||
|
|
@ -165,7 +165,7 @@ class InvoiceTaxSummary implements ShouldQueue
|
|||
Invoice::withTrashed()
|
||||
->with('payments')
|
||||
->where('company_id', $company->id)
|
||||
->whereIn('status_id', [3,4,5]) // Paid statuses
|
||||
->whereIn('status_id', [3,4]) // Paid statuses
|
||||
->where('is_deleted', 0)
|
||||
->whereColumn('amount', '!=', 'balance')
|
||||
->whereHas('client', function ($query) {
|
||||
|
|
@ -190,7 +190,7 @@ class InvoiceTaxSummary implements ShouldQueue
|
|||
})
|
||||
->cursor()
|
||||
->each(function (Invoice $invoice) use ($startDate, $endDate) {
|
||||
(new InvoiceTransactionEventEntryAccrual())->run($invoice, $startDate, $endDate);
|
||||
(new InvoiceTransactionEventEntryCash())->run($invoice, $startDate, $endDate);
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,10 +37,12 @@ class InvoiceTransactionEventEntry
|
|||
* @param Invoice $invoice
|
||||
* @return void
|
||||
*/
|
||||
public function run($invoice)
|
||||
public function run(Invoice $invoice, ?string $force_period = null)
|
||||
{
|
||||
$this->setPaidRatio($invoice);
|
||||
|
||||
$period = $force_period ?? now()->endOfMonth()->format('Y-m-d');
|
||||
|
||||
$this->payments = $invoice->payments->flatMap(function ($payment) {
|
||||
return $payment->invoices()->get()->map(function ($invoice) use ($payment) {
|
||||
return [
|
||||
|
|
@ -66,7 +68,7 @@ class InvoiceTransactionEventEntry
|
|||
'event_id' => TransactionEvent::INVOICE_UPDATED,
|
||||
'timestamp' => now()->timestamp,
|
||||
'metadata' => $this->getMetadata($invoice),
|
||||
'period' => now()->endOfMonth()->format('Y-m-d'),
|
||||
'period' => $period,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,227 +0,0 @@
|
|||
<?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 InvoiceTransactionEventEntryAccrual
|
||||
{
|
||||
private Collection $payments;
|
||||
|
||||
private float $paid_ratio;
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param Invoice $invoice
|
||||
* @return void
|
||||
*/
|
||||
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' => now()->endOfMonth()->format('Y-m-d'),
|
||||
]);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,21 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Report\XLS;
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
use Carbon\Carbon;
|
||||
namespace App\Services\Report;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Number;
|
||||
use App\Models\Client;
|
||||
use League\Csv\Writer;
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Export\CSV\BaseExport;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Services\Report\TaxSummaryReport;
|
||||
use App\Services\Template\TemplateService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use App\Models\Invoice;
|
||||
use App\Listeners\Invoice\InvoiceTransactionEventEntry;
|
||||
use App\Models\TransactionEvent;
|
||||
|
||||
class TaxReport
|
||||
|
||||
class TaxPeriodReport extends BaseExport
|
||||
{
|
||||
use MakesDates;
|
||||
|
||||
private Spreadsheet $spreadsheet;
|
||||
|
||||
private array $data = [];
|
||||
|
|
@ -24,37 +44,179 @@ class TaxReport
|
|||
|
||||
private string $number_format;
|
||||
|
||||
public function __construct(public Company $company, private string $start_date, private string $end_date)
|
||||
//is_income_billed = accrual
|
||||
//!is_invoice_billed = cash
|
||||
/**
|
||||
@param array $input
|
||||
[
|
||||
'date_range',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'client_id',
|
||||
]
|
||||
*/
|
||||
public function __construct(public Company $company, public array $input)
|
||||
{
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
|
||||
$this->start_date = Carbon::parse($this->start_date);
|
||||
$this->end_date = Carbon::parse($this->end_date);
|
||||
|
||||
MultiDB::setDb($this->company->db);
|
||||
App::forgetInstance('translator');
|
||||
App::setLocale($this->company->locale());
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
$this->spreadsheet = new Spreadsheet();
|
||||
|
||||
$this->buildData()
|
||||
->setCurrencyFormat()
|
||||
->createSummarySheet()
|
||||
->createInvoiceSummarySheetAccrual()
|
||||
->createInvoiceSummarySheetCash()
|
||||
->createInvoiceItemSummarySheetAccrual()
|
||||
->createInvoiceItemSummarySheetCash();
|
||||
|
||||
|
||||
return $this;
|
||||
$this->calculateDateRange()
|
||||
->initializeData()
|
||||
->buildData();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* initializeData
|
||||
*
|
||||
* Ensure our dataset has the appropriate transaction events.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
private function initializeData(): self
|
||||
{
|
||||
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()
|
||||
->each(function($invoice){
|
||||
|
||||
if($invoice->status_id == Invoice::STATUS_SENT){
|
||||
(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])){
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function resolveQuery()
|
||||
{
|
||||
$query = Invoice::query()
|
||||
->withTrashed()
|
||||
->with('transaction_events')
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
|
||||
if($this->input['is_income_billed']) //acrrual
|
||||
{
|
||||
$query->whereIn('status_id', [2,3,4])
|
||||
->whereHas('transaction_events', function($query){
|
||||
$query->where('event_id', TransactionEvent::INVOICE_UPDATED)
|
||||
->whereBetween('period', [$this->start_date, $this->end_date]);
|
||||
});
|
||||
}
|
||||
else //cash
|
||||
{
|
||||
$query->whereIn('status_id', [3,4])
|
||||
->whereHas('transaction_events', function($query){
|
||||
$query->where('event_id', TransactionEvent::PAYMENT_CASH)
|
||||
->whereBetween('period', [$this->start_date, $this->end_date]);
|
||||
});
|
||||
}
|
||||
|
||||
$query->orderBy('balance', 'desc');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* calculateDateRange
|
||||
*
|
||||
* We only support dates as of the end of the last month.
|
||||
* @return self
|
||||
*/
|
||||
private function calculateDateRange(): self
|
||||
{
|
||||
|
||||
switch ($date_range) {
|
||||
case 'last7':
|
||||
case 'last30':
|
||||
case 'this_month':
|
||||
case 'last_month':
|
||||
$this->start_date = now()->startOfMonth()->subMonth()->format('Y-m-d');
|
||||
$this->end_date = now()->startOfMonth()->subMonth()->endOfMonth()->format('Y-m-d');
|
||||
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');
|
||||
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');
|
||||
case 'last365_days':
|
||||
$this->start_date = now()->startOfDay()->subDays(365)->format('Y-m-d');
|
||||
$this->end_date = now()->startOfDay()->format('Y-m-d');
|
||||
case 'this_year':
|
||||
|
||||
$first_month_of_year = $this->company->first_month_of_year ?? 1;
|
||||
$fin_year_start = now()->createFromDate(now()->year, $first_month_of_year, 1);
|
||||
|
||||
if (now()->lt($fin_year_start)) {
|
||||
$fin_year_start->subYearNoOverflow();
|
||||
}
|
||||
|
||||
$this->start_date = $fin_year_start->format('Y-m-d');
|
||||
$this->end_date = $fin_year_start->copy()->addYear()->subDay()->format('Y-m-d');
|
||||
case 'last_year':
|
||||
|
||||
$first_month_of_year = $this->company->first_month_of_year ?? 1;
|
||||
$fin_year_start = now()->createFromDate(now()->year, $first_month_of_year, 1);
|
||||
$fin_year_start->subYearNoOverflow();
|
||||
|
||||
if (now()->subYear()->lt($fin_year_start)) {
|
||||
$fin_year_start->subYearNoOverflow();
|
||||
}
|
||||
|
||||
$this->start_date = $fin_year_start->format('Y-m-d');
|
||||
$this->end_date = $fin_year_start->copy()->addYear()->subDay()->format('Y-m-d');
|
||||
case 'custom':
|
||||
|
||||
try {
|
||||
$custom_start_date = Carbon::parse($this->input['start_date']);
|
||||
$custom_end_date = Carbon::parse($this->input['end_date']);
|
||||
} catch (\Exception $e) {
|
||||
$custom_start_date = now()->startOfYear();
|
||||
$custom_end_date = now();
|
||||
}
|
||||
|
||||
$this->start_date = $custom_start_date->format('Y-m-d');
|
||||
$this->end_date = $custom_end_date->format('Y-m-d');
|
||||
case 'all':
|
||||
default:
|
||||
$this->start_date = now()->startOfYear()->format('Y-m-d');
|
||||
$this->end_date = now()->format('Y-m-d');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function accrual()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function cash()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function getXlsFile()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function setCurrencyFormat()
|
||||
{
|
||||
$currency = $this->company->currency();
|
||||
|
|
@ -82,7 +244,7 @@ class TaxReport
|
|||
}
|
||||
|
||||
// All invoices within a time period - regardless if they are paid or not!
|
||||
public function createInvoiceSummarySheetAccrual()
|
||||
public function createInvoiceSummarySheet()
|
||||
{
|
||||
|
||||
$worksheet = $this->spreadsheet->createSheet();
|
||||
|
|
@ -98,24 +260,7 @@ class TaxReport
|
|||
return $this;
|
||||
}
|
||||
|
||||
// All paid invoices within a time period
|
||||
public function createInvoiceSummarySheetCash()
|
||||
{
|
||||
|
||||
$worksheet = $this->spreadsheet->createSheet();
|
||||
$worksheet->setTitle(ctrans('texts.invoice')." ".ctrans('texts.cash_accounting'));
|
||||
|
||||
$worksheet->fromArray($this->data['cash']['invoices'], null, 'A1');
|
||||
$worksheet->getStyle('B:B')->getNumberFormat()->setFormatCode($this->company->date_format()); // Invoice date column
|
||||
$worksheet->getStyle('C:C')->getNumberFormat()->setFormatCode($this->currency_format); // Invoice total column
|
||||
$worksheet->getStyle('D:D')->getNumberFormat()->setFormatCode($this->currency_format); // Paid amount column
|
||||
$worksheet->getStyle('E:E')->getNumberFormat()->setFormatCode($this->currency_format); // Total taxes column
|
||||
$worksheet->getStyle('F:F')->getNumberFormat()->setFormatCode($this->currency_format); // Tax paid column
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function createInvoiceItemSummarySheetAccrual()
|
||||
public function createInvoiceItemSummarySheet()
|
||||
{
|
||||
|
||||
$worksheet = $this->spreadsheet->createSheet();
|
||||
|
|
@ -134,30 +279,11 @@ class TaxReport
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function createInvoiceItemSummarySheetCash()
|
||||
{
|
||||
|
||||
$worksheet = $this->spreadsheet->createSheet();
|
||||
$worksheet->setTitle(ctrans('texts.invoice_item')." ".ctrans('texts.cash_accounting'));
|
||||
$worksheet->fromArray($this->data['cash']['invoice_items'], null, 'A1');
|
||||
|
||||
$worksheet->getStyle('B:B')->getNumberFormat()->setFormatCode($this->company->date_format()); // Invoice date column
|
||||
$worksheet->getStyle('C:C')->getNumberFormat()->setFormatCode($this->currency_format); // Invoice total column
|
||||
$worksheet->getStyle('D:D')->getNumberFormat()->setFormatCode($this->currency_format); // Paid amount column
|
||||
$worksheet->getStyle('F:F')->getNumberFormat()->setFormatCode($this->number_format."%"); // Tax rate column
|
||||
$worksheet->getStyle('G:G')->getNumberFormat()->setFormatCode($this->currency_format); // Tax amount column
|
||||
$worksheet->getStyle('H:H')->getNumberFormat()->setFormatCode($this->currency_format); // Tax paid column
|
||||
$worksheet->getStyle('I:I')->getNumberFormat()->setFormatCode($this->currency_format); // Taxable amount column
|
||||
// Column J (tax_nexus) is text, so no special formatting needed
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function buildData()
|
||||
{
|
||||
|
||||
$start_date_instance = $this->start_date;
|
||||
$end_date_instance = $this->end_date;
|
||||
$query = $this->resolveQuery();
|
||||
|
||||
$this->data['invoices'] = [];
|
||||
$this->data['invoices'][] =
|
||||
|
|
@ -192,20 +318,9 @@ class TaxReport
|
|||
$this->data['cash']['invoice_items'] = [$invoice_item_headers];
|
||||
|
||||
|
||||
Invoice::withTrashed()
|
||||
->with('transaction_events')
|
||||
->where('company_id', $this->company->id)
|
||||
->whereHas('transaction_events', function ($query){
|
||||
return $query->where('period', now()->endOfMonth()->format('Y-m-d'));
|
||||
})
|
||||
->cursor()
|
||||
$query->cursor()
|
||||
->each(function($invoice){
|
||||
|
||||
if($invoice->transaction_events->count() == 0){
|
||||
(new InvoiceTransactionEventEntry())->run($invoice);
|
||||
$invoice->load('transaction_events');
|
||||
}
|
||||
|
||||
/** @var TransactionEvent $invoice_state */
|
||||
$invoice_state = $invoice->transaction_events()->where('event_id', TransactionEvent::INVOICE_UPDATED)->where('period', now()->endOfMonth()->format('Y-m-d'))->orderBy('timestamp', 'desc')->first();
|
||||
$payment_state = $invoice->transaction_events()->where('event_id', TransactionEvent::PAYMENT_CASH)->where('period', now()->endOfMonth()->format('Y-m-d'))->orderBy('timestamp', 'desc')->first();
|
||||
|
|
@ -281,4 +396,5 @@ class TaxReport
|
|||
return $fileContent;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -5613,6 +5613,7 @@ $lang = array(
|
|||
'include_project_tasks' => 'Include Project Tasks',
|
||||
'include_project_tasks_help' => 'Also invoice tasks that are part of a project',
|
||||
'tax_nexus' => 'Tax Nexus',
|
||||
'tax_period_report' => 'Tax Period Report',
|
||||
);
|
||||
|
||||
return $lang;
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ use App\Http\Controllers\Reports\ARDetailReportController;
|
|||
use App\Http\Controllers\Reports\DocumentReportController;
|
||||
use App\Http\Controllers\Reports\ARSummaryReportController;
|
||||
use App\Http\Controllers\Reports\QuoteItemReportController;
|
||||
use App\Http\Controllers\Reports\TaxPeriodReportController;
|
||||
use App\Http\Controllers\Reports\UserSalesReportController;
|
||||
use App\Http\Controllers\Reports\TaxSummaryReportController;
|
||||
use App\Http\Controllers\Support\Messages\SendingController;
|
||||
|
|
@ -372,6 +373,7 @@ Route::group(['middleware' => ['throttle:api', 'token_auth', 'valid_json','local
|
|||
Route::post('reports/client_balance_report', ClientBalanceReportController::class);
|
||||
Route::post('reports/client_sales_report', ClientSalesReportController::class);
|
||||
Route::post('reports/tax_summary_report', TaxSummaryReportController::class);
|
||||
Route::post('reports/tax_period_report', TaxPeriodReportController::class);
|
||||
Route::post('reports/user_sales_report', UserSalesReportController::class);
|
||||
Route::post('reports/projects', ProjectReportController::class);
|
||||
Route::post('reports/preview/{hash}', ReportPreviewController::class);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use App\Factory\InvoiceItemFactory;
|
|||
use App\Services\Report\TaxSummaryReport;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use App\Listeners\Invoice\InvoiceTransactionEventEntry;
|
||||
use App\Listeners\Invoice\InvoiceTransactionEventEntryAccrual;
|
||||
use App\Listeners\Invoice\InvoiceTransactionEventEntryCash;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -226,7 +226,7 @@ class TaxSummaryReportTest extends TestCase
|
|||
|
||||
$this->assertEquals($i3->amount, $i3->paid_to_date);
|
||||
|
||||
(new InvoiceTransactionEventEntryAccrual())->run($i3, now()->subDays(30)->format('Y-m-d'), now()->addDays(30)->format('Y-m-d'));
|
||||
(new InvoiceTransactionEventEntryCash())->run($i3, now()->subDays(30)->format('Y-m-d'), now()->addDays(30)->format('Y-m-d'));
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +268,7 @@ class TaxSummaryReportTest extends TestCase
|
|||
|
||||
$i2 = $i2->fresh();
|
||||
|
||||
(new InvoiceTransactionEventEntryAccrual())->run($i2, now()->subDays(30)->format('Y-m-d'), now()->addDays(30)->format('Y-m-d'));
|
||||
(new InvoiceTransactionEventEntryCash())->run($i2, now()->subDays(30)->format('Y-m-d'), now()->addDays(30)->format('Y-m-d'));
|
||||
|
||||
$payment = $i2->payments()->first();
|
||||
|
||||
|
|
@ -294,18 +294,18 @@ class TaxSummaryReportTest extends TestCase
|
|||
|
||||
$payment->refund($data);
|
||||
|
||||
$pl = new \App\Services\Report\XLS\TaxReport($this->company, '2025-01-01', '2025-12-31');
|
||||
// $pl = new \App\Services\Report\XLS\TaxReport($this->company, '2025-01-01', '2025-12-31');
|
||||
|
||||
$response = $pl->run()->getXlsFile();
|
||||
// $response = $pl->run()->getXlsFile();
|
||||
|
||||
$this->assertIsString($response);
|
||||
// $this->assertIsString($response);
|
||||
|
||||
try{
|
||||
file_put_contents('/home/david/ttx.xlsx', $response);
|
||||
}
|
||||
catch(\Throwable $e){
|
||||
nlog($e->getMessage());
|
||||
}
|
||||
// try{
|
||||
// file_put_contents('/home/david/ttx.xlsx', $response);
|
||||
// }
|
||||
// catch(\Throwable $e){
|
||||
// nlog($e->getMessage());
|
||||
// }
|
||||
|
||||
config(['queue.default' => 'redis']);
|
||||
|
||||
|
|
@ -376,7 +376,7 @@ class TaxSummaryReportTest extends TestCase
|
|||
$i2 = $i2->calc()->getInvoice();
|
||||
$i2->service()->markPaid();
|
||||
|
||||
(new InvoiceTransactionEventEntryAccrual())->run($i2, now()->subDays(3000)->format('Y-m-d'), now()->addDays(3000)->format('Y-m-d'));
|
||||
(new InvoiceTransactionEventEntryCash())->run($i2, now()->subDays(3000)->format('Y-m-d'), now()->addDays(3000)->format('Y-m-d'));
|
||||
|
||||
$pl = new TaxSummaryReport($this->company, $this->payload);
|
||||
$response = $pl->run();
|
||||
|
|
@ -449,12 +449,12 @@ class TaxSummaryReportTest extends TestCase
|
|||
$i2 = $i2->calc()->getInvoice();
|
||||
$i2->service()->markPaid()->save();
|
||||
|
||||
(new InvoiceTransactionEventEntryAccrual())->run($i2, now()->subDays(30)->format('Y-m-d'), now()->addDays(30)->format('Y-m-d'));
|
||||
(new InvoiceTransactionEventEntryCash())->run($i2, now()->subDays(30)->format('Y-m-d'), now()->addDays(30)->format('Y-m-d'));
|
||||
|
||||
$tr = new \App\Services\Report\XLS\TaxReport($this->company, '2025-01-01', '2025-12-31');
|
||||
$response = $tr->run()->getXlsFile();
|
||||
// $tr = new \App\Services\Report\XLS\TaxReport($this->company, '2025-01-01', '2025-12-31');
|
||||
// $response = $tr->run()->getXlsFile();
|
||||
|
||||
$this->assertNotEmpty($response);
|
||||
// $this->assertNotEmpty($response);
|
||||
|
||||
$this->assertNotNull(TransactionEvent::where('invoice_id', $i->id)->first());
|
||||
$this->assertNotNull(TransactionEvent::where('invoice_id', $i2->id)->first());
|
||||
|
|
|
|||
Loading…
Reference in New Issue