inspiren-sem-tool/app/Console/Commands/UpdateLatestClientInvoicePaymentItemSpending.php

124 lines
4.9 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\Client;
use App\Models\ClientInvoicePaymentItem;
use App\Services\GoogleAdsSpendService;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Log;
class UpdateLatestClientInvoicePaymentItemSpending extends Command
{
protected $signature = 'customer:update-latest-invoice-item-spending
{--dry-run : Calculate without saving changes}';
protected $description = 'Update spending on all billing type 1 invoice payment items.';
public function handle(GoogleAdsSpendService $spendService): int
{
$dryRun = (bool) $this->option('dry-run');
$today = today()->toDateString();
$updated = 0;
$skipped = 0;
$failed = 0;
Client::query()
->whereHas('invoices.payments.items', fn (Builder $query) => $this->eligibleItems($query))
->orderBy('id')
->chunkById(50, function ($clients) use (
$spendService,
$dryRun,
$today,
&$updated,
&$skipped,
&$failed,
) {
foreach ($clients as $client) {
if (empty($client->customer_id)) {
$skipped++;
$this->warn("Skipping client {$client->id}: missing customer ID.");
continue;
}
ClientInvoicePaymentItem::query()
->whereHas(
'payment.invoice',
fn (Builder $query) => $query->where('client_id', $client->id),
)
->where(fn (Builder $query) => $this->eligibleItems($query))
->orderBy('id')
->chunkById(50, function ($items) use (
$client,
$spendService,
$dryRun,
$today,
&$updated,
&$skipped,
&$failed,
) {
foreach ($items as $item) {
$startDate = $item->start_date->format('Y-m-d');
$endDate = $item->end_date?->format('Y-m-d') ?? $today;
if ($endDate < $startDate) {
$skipped++;
$this->warn("Skipping item {$item->id}: end date is before start date.");
continue;
}
try {
$spending = $spendService->forDateRange(
$client->customer_id,
$startDate,
$endDate,
);
if (! $dryRun) {
$item->forceFill(['spending' => $spending])->save();
}
$updated++;
$this->line(sprintf(
'%s client %d item %d: RM %.2f (%s to %s)',
$dryRun ? 'Calculated' : 'Updated',
$client->id,
$item->id,
$spending,
$startDate,
$endDate,
));
} catch (\Throwable $exception) {
$failed++;
Log::error('Unable to update invoice payment item spending.', [
'client_id' => $client->id,
'client_invoice_payment_item_id' => $item->id,
'customer_id' => $client->customer_id,
'start_date' => $startDate,
'end_date' => $endDate,
'message' => $exception->getMessage(),
]);
$this->error("Failed item {$item->id}: {$exception->getMessage()}");
}
}
});
}
});
$this->info("Done. {$updated} calculated, {$skipped} skipped, {$failed} failed.");
return $failed > 0 ? self::FAILURE : self::SUCCESS;
}
private function eligibleItems(Builder $query): Builder
{
return $query
->where('billing_item_types_id', 1)
->whereNotNull('start_date');
}
}