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:
@@ -29,6 +29,7 @@
|
|||||||
- 2026-09-09 | رمز ادمین ≠ لاگین اپ | `Customer::setPasswordAttribute` + `AuthController` + `updateAdmin` | قبل از هش/لاگین ارقام فارسی→انگلیسی؛ jwt hash=false؛ آنلاین فقط password غیرخالی بفرست
|
- 2026-09-09 | رمز ادمین ≠ لاگین اپ | `Customer::setPasswordAttribute` + `AuthController` + `updateAdmin` | قبل از هش/لاگین ارقام فارسی→انگلیسی؛ jwt hash=false؛ آنلاین فقط password غیرخالی بفرست
|
||||||
- 2026-10-20 | خرید اقساطی دیجیپی | `DigipayInstallmentService` + جداول `digipay_credits`/`digipay_orders` + migration `2026_10_20_000002` | فلگ پیشفرض خاموش؛ اعتبار را ادمین ست میکند نه وبهوک دیجیپی
|
- 2026-10-20 | خرید اقساطی دیجیپی | `DigipayInstallmentService` + جداول `digipay_credits`/`digipay_orders` + migration `2026_10_20_000002` | فلگ پیشفرض خاموش؛ اعتبار را ادمین ست میکند نه وبهوک دیجیپی
|
||||||
- 2026-10-20 | دسترسی صفحات مدیر | `users.page_access` + `AdminPageAccess` + ادمین `adminPageAccess.ts` / ویرایش مدیران | null=کامل؛ درخت مشاهده/ویرایش/حذف؛ ورود=اولین صفحه مجاز؛ منوی ممنوع=«شما به این صفحه دسترسی ندارید»
|
- 2026-10-20 | دسترسی صفحات مدیر | `users.page_access` + `AdminPageAccess` + ادمین `adminPageAccess.ts` / ویرایش مدیران | null=کامل؛ درخت مشاهده/ویرایش/حذف؛ ورود=اولین صفحه مجاز؛ منوی ممنوع=«شما به این صفحه دسترسی ندارید»
|
||||||
|
- 2026-11-10 | گزارش استفاده کدهای هدیه | `POST /admin/giftcard/codes-usage-report` + ادمین `GiftCardCodesUsageReportSheet.vue` | ردیف=giftcard_codes؛ usage_count؛ خلاصه کل/مصرفشده/فعال؛ اکسل از BaseTable
|
||||||
- 2026-10-20 | ویرایش/حذف کارت هدیه | `GiftCardController::update/destroy` + `giftCards/components/table.vue` | PUT/DELETE `/admin/giftcard/{id}`؛ کارت استفادهشده حذف نمیشود و مقدار/تعدادش عوض نمیشود
|
- 2026-10-20 | ویرایش/حذف کارت هدیه | `GiftCardController::update/destroy` + `giftCards/components/table.vue` | PUT/DELETE `/admin/giftcard/{id}`؛ کارت استفادهشده حذف نمیشود و مقدار/تعدادش عوض نمیشود
|
||||||
- 2026-10-20 | دکمه ویرایش/حذف گیفت | `allowsPageFlag` + `giftCards/components/table.vue` | بدون edit/delete روی giftCardMain دکمه و دیالوگ باز نمیشود
|
- 2026-10-20 | دکمه ویرایش/حذف گیفت | `allowsPageFlag` + `giftCards/components/table.vue` | بدون edit/delete روی giftCardMain دکمه و دیالوگ باز نمیشود
|
||||||
- 2026-10-20 | دسترسی عملیات جدول | `PAGE_TABLE_ACTIONS` + `usePageActionAccess` + `AdminPageAccess::ACTIONS` | هر صفحه فقط عملیات ستون خودش؛ مشاهده جدا؛ رکورد قدیمی بدون actions ویرایش/حذف را از همان فیلد و بقیه را از مشاهده میگیرد
|
- 2026-10-20 | دسترسی عملیات جدول | `PAGE_TABLE_ACTIONS` + `usePageActionAccess` + `AdminPageAccess::ACTIONS` | هر صفحه فقط عملیات ستون خودش؛ مشاهده جدا؛ رکورد قدیمی بدون actions ویرایش/حذف را از همان فیلد و بقیه را از مشاهده میگیرد
|
||||||
|
|||||||
@@ -943,4 +943,106 @@ class GiftCardController extends Controller
|
|||||||
return response()->json($records);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class AdminPageAccess
|
|||||||
'accountingMain' => ['doc', 'register', 'send'],
|
'accountingMain' => ['doc', 'register', 'send'],
|
||||||
'appNotifications' => ['recipients', 'readers', 'edit', 'toggle', 'delete'],
|
'appNotifications' => ['recipients', 'readers', 'edit', 'toggle', 'delete'],
|
||||||
'automations' => ['preview', 'log', 'edit', 'toggle', 'delete'],
|
'automations' => ['preview', 'log', 'edit', 'toggle', 'delete'],
|
||||||
'giftCardMain' => ['info', 'codes', 'edit', 'delete'],
|
'giftCardMain' => ['info', 'codes', 'usageReport', 'edit', 'delete'],
|
||||||
'discounts' => ['edit'],
|
'discounts' => ['edit'],
|
||||||
'usersMain' => ['info', 'edit', 'delete'],
|
'usersMain' => ['info', 'edit', 'delete'],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -345,6 +345,7 @@ Route::middleware(['auth:user', 'admin.page'])->group(function (){
|
|||||||
//Giftcard
|
//Giftcard
|
||||||
Route::post('giftcard/generate/', [GiftCardController::class, 'redeem']);
|
Route::post('giftcard/generate/', [GiftCardController::class, 'redeem']);
|
||||||
Route::post('giftcard/table/', [GiftCardController::class, 'table']);
|
Route::post('giftcard/table/', [GiftCardController::class, 'table']);
|
||||||
|
Route::post('giftcard/codes-usage-report/', [GiftCardController::class, 'codesUsageReport']);
|
||||||
Route::put('giftcard/{id}', [GiftCardController::class, 'update']);
|
Route::put('giftcard/{id}', [GiftCardController::class, 'update']);
|
||||||
Route::delete('giftcard/{id}', [GiftCardController::class, 'destroy']);
|
Route::delete('giftcard/{id}', [GiftCardController::class, 'destroy']);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user