Skip to content

Commit 2dc6202

Browse files
committed
Add promo code report
1 parent 79b5580 commit 2dc6202

File tree

12 files changed

+379
-49
lines changed

12 files changed

+379
-49
lines changed

backend/app/DomainObjects/Enums/ReportTypes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ enum ReportTypes: string
88

99
case PRODUCT_SALES = 'product_sales';
1010
case DAILY_SALES_REPORT = 'daily_sales_report';
11+
case PROMO_CODES_REPORT = 'promo_codes_report';
1112
}

backend/app/Http/Actions/Reports/GetReportAction.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ private function validateDateRange(GetReportRequest $request): void
5454

5555
$diffInDays = Carbon::parse($startDate)->diffInDays(Carbon::parse($endDate));
5656

57-
if ($diffInDays > 366) {
58-
throw ValidationException::withMessages(['start_date' => 'Date range must be less than 365 days.']);
57+
if ($diffInDays > 370) {
58+
throw ValidationException::withMessages(['start_date' => 'Date range must be less than 370 days.']);
5959
}
6060
}
6161
}

backend/app/Resources/ProductCategory/ProductCategoryResourcePublic.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace HiEvents\Resources\ProductCategory;
44

55
use HiEvents\DomainObjects\ProductCategoryDomainObject;
6-
use HiEvents\Resources\Product\ProductResource;
6+
use HiEvents\Resources\Product\ProductResourcePublic;
77
use Illuminate\Http\Resources\Json\JsonResource;
88

99
/**
@@ -21,7 +21,7 @@ public function toArray($request): array
2121
'order' => $this->getOrder(),
2222
'no_products_message' => $this->getNoProductsMessage(),
2323
$this->mergeWhen((bool)$this->getProducts(), fn() => [
24-
'products' => ProductResource::collection($this->getProducts()),
24+
'products' => ProductResourcePublic::collection($this->getProducts()),
2525
]),
2626
];
2727
}

backend/app/Services/Domain/Report/Factory/ReportServiceFactory.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use HiEvents\Services\Domain\Report\AbstractReportService;
77
use HiEvents\Services\Domain\Report\Reports\DailySalesReport;
88
use HiEvents\Services\Domain\Report\Reports\ProductSalesReport;
9+
use HiEvents\Services\Domain\Report\Reports\PromoCodesReport;
910
use Illuminate\Support\Facades\App;
1011

1112
class ReportServiceFactory
@@ -15,6 +16,7 @@ public function create(ReportTypes $reportType): AbstractReportService
1516
return match ($reportType) {
1617
ReportTypes::PRODUCT_SALES => App::make(ProductSalesReport::class),
1718
ReportTypes::DAILY_SALES_REPORT => App::make(DailySalesReport::class),
19+
ReportTypes::PROMO_CODES_REPORT => App::make(PromoCodesReport::class),
1820
};
1921
}
2022
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
namespace HiEvents\Services\Domain\Report\Reports;
4+
5+
use HiEvents\Services\Domain\Report\AbstractReportService;
6+
use Illuminate\Support\Carbon;
7+
8+
class PromoCodesReport extends AbstractReportService
9+
{
10+
protected function getSqlQuery(Carbon $startDate, Carbon $endDate): string
11+
{
12+
$startDateString = $startDate->format('Y-m-d H:i:s');
13+
$endDateString = $endDate->format('Y-m-d H:i:s');
14+
15+
return <<<SQL
16+
WITH promo_metrics AS (
17+
SELECT
18+
COALESCE(pc.code, o.promo_code) as promo_code,
19+
COUNT(DISTINCT o.id) as times_used,
20+
COUNT(DISTINCT o.email) as unique_customers,
21+
COALESCE(SUM(o.total_gross), 0) as total_gross_sales,
22+
COALESCE(SUM(o.total_before_additions), 0) as total_before_discounts,
23+
COALESCE(SUM(o.total_before_additions - o.total_gross), 0) as total_discount_amount,
24+
CASE
25+
WHEN COUNT(o.id) > 0 THEN ROUND(AVG(o.total_before_additions - o.total_gross)::numeric, 2)
26+
ELSE 0
27+
END as avg_discount_per_order,
28+
CASE
29+
WHEN COUNT(o.id) > 0 THEN ROUND(AVG(o.total_gross)::numeric, 2)
30+
ELSE 0
31+
END as avg_order_value,
32+
MIN(o.created_at AT TIME ZONE 'UTC') as first_used_at,
33+
MAX(o.created_at AT TIME ZONE 'UTC') as last_used_at,
34+
pc.discount as configured_discount,
35+
pc.discount_type,
36+
pc.max_allowed_usages,
37+
pc.expiry_date AT TIME ZONE 'UTC' as expiry_date,
38+
CASE
39+
WHEN pc.max_allowed_usages IS NOT NULL
40+
THEN pc.max_allowed_usages - COUNT(o.id)::integer
41+
END as remaining_uses,
42+
CASE
43+
WHEN pc.expiry_date < CURRENT_TIMESTAMP THEN 'Expired'
44+
WHEN pc.max_allowed_usages IS NOT NULL AND COUNT(o.id) >= pc.max_allowed_usages THEN 'Limit Reached'
45+
WHEN pc.deleted_at IS NOT NULL THEN 'Deleted'
46+
ELSE 'Active'
47+
END as status
48+
FROM promo_codes pc
49+
LEFT JOIN orders o ON
50+
pc.id = o.promo_code_id
51+
AND o.deleted_at IS NULL
52+
AND o.status NOT IN ('RESERVED')
53+
AND o.event_id = :event_id
54+
AND o.created_at >= '$startDateString'
55+
AND o.created_at <= '$endDateString'
56+
WHERE
57+
pc.deleted_at IS NULL
58+
AND pc.event_id = :event_id
59+
GROUP BY
60+
pc.id,
61+
COALESCE(pc.code, o.promo_code),
62+
pc.discount,
63+
pc.discount_type,
64+
pc.max_allowed_usages,
65+
pc.expiry_date,
66+
pc.deleted_at
67+
)
68+
SELECT
69+
promo_code,
70+
times_used,
71+
unique_customers,
72+
configured_discount,
73+
discount_type,
74+
total_gross_sales,
75+
total_before_discounts,
76+
total_discount_amount,
77+
avg_discount_per_order,
78+
avg_order_value,
79+
first_used_at,
80+
last_used_at,
81+
max_allowed_usages,
82+
remaining_uses,
83+
expiry_date,
84+
status
85+
FROM promo_metrics
86+
ORDER BY
87+
total_gross_sales DESC,
88+
promo_code;
89+
SQL;
90+
}
91+
}

0 commit comments

Comments
 (0)