162 lines
5.4 KiB
PHP
162 lines
5.4 KiB
PHP
<?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\Jobs\Cron;
|
|
|
|
use App\Models\Invoice;
|
|
use App\Models\Webhook;
|
|
use App\Models\Company;
|
|
use App\Models\Timezone;
|
|
use App\Libraries\MultiDB;
|
|
use Illuminate\Bus\Queueable;
|
|
use App\Jobs\Entity\EmailEntity;
|
|
use Illuminate\Queue\SerializesModels;
|
|
use Illuminate\Queue\InteractsWithQueue;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|
use Carbon\Carbon;
|
|
use App\Listeners\Invoice\InvoiceTransactionEventEntry;
|
|
|
|
class InvoiceTaxSummary implements ShouldQueue
|
|
{
|
|
use Dispatchable;
|
|
use InteractsWithQueue;
|
|
use Queueable;
|
|
use SerializesModels;
|
|
|
|
public $tries = 1;
|
|
|
|
public function __construct()
|
|
{
|
|
|
|
}
|
|
|
|
public function handle()
|
|
{
|
|
$currentUtcHour = now()->hour;
|
|
$transitioningTimezones = $this->getTransitioningTimezones($currentUtcHour);
|
|
|
|
foreach(MultiDB::$dbs as $db) {
|
|
MultiDB::setDB($db);
|
|
// Only process companies in timezones that just transitioned
|
|
$companies = $this->getCompaniesInTimezones($transitioningTimezones);
|
|
|
|
foreach ($companies as $company) {
|
|
|
|
$this->processCompanyTaxSummary($company);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function getTransitioningTimezones($utcHour)
|
|
{
|
|
$transitioningTimezones = [];
|
|
|
|
// Get all timezones from the database
|
|
$timezones = app('timezones');
|
|
|
|
foreach ($timezones as $timezone) {
|
|
// Calculate the current UTC offset for this timezone (accounting for DST)
|
|
$currentOffset = $this->getCurrentUtcOffset($timezone->name);
|
|
|
|
// Calculate when this timezone transitions to the next day
|
|
$transitionHour = $this->getTimezoneTransitionHour($currentOffset);
|
|
|
|
// If this timezone transitions at the current UTC hour, include it
|
|
if ($transitionHour === $utcHour) {
|
|
$transitioningTimezones[] = $timezone->id;
|
|
}
|
|
}
|
|
|
|
return $transitioningTimezones;
|
|
}
|
|
|
|
private function getCurrentUtcOffset($timezoneName)
|
|
{
|
|
try {
|
|
$dateTime = new \DateTime('now', new \DateTimeZone($timezoneName));
|
|
return $dateTime->getOffset();
|
|
} catch (\Exception $e) {
|
|
// Fallback to UTC if timezone is invalid
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private function getTimezoneTransitionHour($utcOffset)
|
|
{
|
|
// Calculate which UTC hour this timezone transitions to the next day
|
|
// A timezone with UTC offset +X transitions at UTC hour (24 - X)
|
|
// For example: UTC+14 transitions at UTC 10:00 (24 - 14 = 10)
|
|
// UTC-12 transitions at UTC 12:00 (24 - (-12) = 36, but we use modulo 24)
|
|
|
|
$transitionHour = (24 - ($utcOffset / 3600)) % 24;
|
|
|
|
// Handle negative offsets properly
|
|
if ($transitionHour < 0) {
|
|
$transitionHour += 24;
|
|
}
|
|
|
|
return (int) $transitionHour;
|
|
}
|
|
|
|
private function getCompaniesInTimezones($timezoneIds)
|
|
{
|
|
if (empty($timezoneIds)) {
|
|
return collect(); // No companies to process
|
|
}
|
|
|
|
// Get companies that have timezone_id in their JSON settings matching the transitioning timezones
|
|
return Company::whereRaw("JSON_EXTRACT(settings, '$.timezone_id') IN (" . implode(',', $timezoneIds) . ")")->get();
|
|
}
|
|
|
|
private function processCompanyTaxSummary($company)
|
|
{
|
|
// Your existing tax summary logic here
|
|
// This will only run for companies in timezones that just transitioned
|
|
|
|
$startDate = now()->subMonth()->startOfMonth()->format('Y-m-d');
|
|
$endDate = now()->subMonth()->endOfMonth()->format('Y-m-d');
|
|
|
|
// Process tax summary for the company
|
|
$this->generateTaxSummary($company, $startDate, $endDate);
|
|
}
|
|
|
|
private function generateTaxSummary($company, $startDate, $endDate)
|
|
{
|
|
$todayStart = now()->subHours(15)->timestamp;
|
|
$todayEnd = now()->endOfDay()->timestamp;
|
|
|
|
Invoice::withTrashed()
|
|
->with('payments')
|
|
->where('company_id', $company->id)
|
|
->whereIn('status_id', [2,3,4,5])
|
|
->where('is_deleted', 0)
|
|
->whereHas('client', function ($query) {
|
|
$query->where('is_deleted', false);
|
|
})
|
|
->whereHas('company', function ($query) {
|
|
$query->where('is_disabled', 0)
|
|
->whereHas('account', function ($q) {
|
|
$q->where('is_flagged', false);
|
|
});
|
|
})
|
|
->whereBetween('date', [$startDate, $endDate])
|
|
->whereDoesntHave('transaction_events', function ($query) use ($todayStart, $todayEnd) {
|
|
$query->where('timestamp', '>=', $todayStart)
|
|
->where('timestamp', '<=', $todayEnd);
|
|
})
|
|
->cursor()
|
|
->each(function (Invoice $invoice) {
|
|
(new InvoiceTransactionEventEntry())->run($invoice);
|
|
});
|
|
}
|
|
} |