feat(admin): gift card codes usage report API

Expose codes-usage-report endpoint with per-code usage counts and filter-aware summary for admin reporting.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-10 11:48:23 +03:30
co-authored by Cursor
parent 885e51a79b
commit 487d899ded
4 changed files with 105 additions and 1 deletions
@@ -943,4 +943,106 @@ class GiftCardController extends Controller
return response()->json($records);
}
/** گزارش استفاده از هر کد هدیه/تخفیف — ردیف به‌ردیف giftcard_codes */
public function codesUsageReport(Request $request)
{
$user = auth()->user();
if (! $user) {
throw new UnprocessableEntityHttpException('auth failed!');
}
$records = GiftCardCodes::query()
->leftJoin('giftcards', 'giftcards.id', '=', 'giftcard_codes.giftcard_id')
->with(['giftcard.customer', 'assignedTo'])
->select('giftcard_codes.*');
$engine = TableEngine::create($records, $request->all());
$engine->initSearch([
'giftcard_codes.code',
'giftcards.name',
'giftcards.mobile',
]);
$summary = [
'total_codes' => (clone $records)->count(),
'used_codes' => (clone $records)->where(function ($q) {
$q->where('giftcard_codes.sts', 2)
->orWhereNotNull('giftcard_codes.used_at')
->orWhere(function ($q2) {
$q2->whereNotNull('giftcard_codes.assigned_to')
->where('giftcard_codes.assigned_to', '!=', '');
});
})->count(),
'active_codes' => (clone $records)->where('giftcard_codes.sts', 1)->count(),
'expired_codes' => (clone $records)->where('giftcard_codes.sts', 3)->count(),
];
$summary['unused_codes'] = max(0, $summary['total_codes'] - $summary['used_codes']);
$result = [];
$engine->select(['giftcard_codes.*'])->export($result);
if (! empty($result['rows'])) {
$mapped = [];
foreach ($result['rows'] as $row) {
if (! $row instanceof GiftCardCodes) {
continue;
}
$row->loadMissing(['giftcard', 'assignedTo']);
$gift = $row->giftcard;
$usageCount = $this->resolveGiftCodeUsageCount($row, $gift);
$mapped[] = [
'id' => $row->id,
'giftcard_id' => $row->giftcard_id,
'code' => $row->code,
'sts' => (int) $row->sts,
'sts_str' => GiftCardCodes::$STS[(int) $row->sts] ?? '-',
'unit' => (int) ($row->unit ?? $gift?->unit ?? 0),
'amount' => $row->amount ?? $gift?->amount,
'expiry_date' => $row->expiry_date ?? $gift?->expiry_date,
'used_at' => $row->used_at,
'usage_count' => $usageCount,
'campaign_name' => $gift?->name,
'campaign_count' => (int) ($gift?->count ?? 0),
'campaign_used_count' => (int) ($gift?->used_count ?? 0),
'assigned_to' => $row->assignedTo,
'giftcard' => $gift ? [
'id' => $gift->id,
'name' => $gift->name,
'customer' => $gift->customer,
] : null,
];
}
$result['rows'] = $mapped;
}
$result['summary'] = $summary;
return response()->json($result);
}
private function resolveGiftCodeUsageCount(GiftCardCodes $code, ?GiftCard $gift): int
{
if ($gift && (int) $gift->use_once === 0 && (int) $gift->count > 0) {
return (int) $gift->used_count;
}
if (is_string($code->assigned_to) && str_starts_with(trim($code->assigned_to), '[')) {
$ids = json_decode($code->assigned_to, true);
if (is_array($ids)) {
return count($ids);
}
}
if ((int) $code->sts === 2 || ! empty($code->used_at)) {
return 1;
}
if ($code->assigned_to !== null && $code->assigned_to !== '') {
return 1;
}
return 0;
}
}