Files
Verde-Web/app/Services/Report/Aggregator.php

152 lines
5.7 KiB
PHP

<?php
namespace App\Services\Report;
use App\Models\DailyCollectionStat;
use App\Models\MonthlyStoreSale;
use App\Models\WeeklyRoutePerformance;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
class Aggregator
{
/**
* Recompute daily_collection_stats for a single date. Idempotent.
* Groups by barangay (derived via DOP -> barangay).
*/
public function rebuildDailyCollectionStats(\DateTimeInterface $date): int
{
$day = Carbon::parse($date)->toDateString();
$rows = DB::table('collection_logs')
->join('drop_off_points', 'collection_logs.drop_off_point_id', '=', 'drop_off_points.id')
->whereDate('collection_logs.scanned_at', $day)
->where('collection_logs.verification_status', 'valid')
->groupBy('drop_off_points.tenant_id', 'drop_off_points.barangay_id')
->selectRaw('
drop_off_points.tenant_id as tenant_id,
drop_off_points.barangay_id as barangay_id,
COUNT(*) as total_scans,
COALESCE(SUM(collection_logs.weight_kg), 0) as total_weight_kg,
COUNT(DISTINCT collection_logs.household_id) as unique_households
')
->get();
DailyCollectionStat::where('date', $day)->delete();
$count = 0;
foreach ($rows as $r) {
DailyCollectionStat::create([
'tenant_id' => $r->tenant_id,
'date' => $day,
'barangay_id' => $r->barangay_id,
'total_scans' => (int) $r->total_scans,
'total_weight_kg' => (int) $r->total_weight_kg,
'unique_households' => (int) $r->unique_households,
'missed_pickups' => 0, // populated by trip-stop analysis below in future
]);
$count++;
}
return $count;
}
/**
* Recompute weekly_route_performance for the week containing $date.
*/
public function rebuildWeeklyRoutePerformance(\DateTimeInterface $date): int
{
$weekStart = Carbon::parse($date)->startOfWeek()->toDateString();
$weekEnd = Carbon::parse($date)->endOfWeek()->toDateString();
$rows = DB::table('trips')
->whereBetween('scheduled_date', [$weekStart, $weekEnd])
->whereIn('status', ['completed', 'cancelled'])
->groupBy('tenant_id', 'route_id')
->selectRaw('
tenant_id,
route_id,
COUNT(*) as trips_count,
SUM(CASE WHEN status = "completed" THEN 1 ELSE 0 END) as completed_count,
AVG(CASE
WHEN actual_end_time IS NOT NULL AND actual_start_time IS NOT NULL
THEN TIMESTAMPDIFF(MINUTE, actual_start_time, actual_end_time)
ELSE NULL
END) as avg_minutes,
SUM(CASE
WHEN status = "completed"
AND actual_start_time IS NOT NULL
AND scheduled_start_time IS NOT NULL
AND TIMESTAMPDIFF(
MINUTE,
TIMESTAMP(scheduled_date, scheduled_start_time),
actual_start_time
) <= 15
THEN 1 ELSE 0
END) as on_time_count
')
->get();
WeeklyRoutePerformance::where('week_start_date', $weekStart)->delete();
$count = 0;
foreach ($rows as $r) {
WeeklyRoutePerformance::create([
'tenant_id' => $r->tenant_id,
'week_start_date' => $weekStart,
'route_id' => $r->route_id,
'on_time_rate_percent' => $r->trips_count > 0
? (int) round(($r->on_time_count / $r->trips_count) * 100)
: 0,
'avg_trip_duration_minutes' => (int) round((float) ($r->avg_minutes ?? 0)),
'completion_rate_percent' => $r->trips_count > 0
? (int) round(($r->completed_count / $r->trips_count) * 100)
: 0,
'trips_count' => (int) $r->trips_count,
]);
$count++;
}
return $count;
}
/**
* Recompute monthly_store_sales for the month containing $date.
*/
public function rebuildMonthlyStoreSales(\DateTimeInterface $date): int
{
$monthStart = Carbon::parse($date)->startOfMonth()->toDateString();
$monthEnd = Carbon::parse($date)->endOfMonth()->toDateString();
$rows = DB::table('store_sales')
->join('partner_stores', 'store_sales.store_id', '=', 'partner_stores.id')
->whereBetween('store_sales.sold_at', [$monthStart.' 00:00:00', $monthEnd.' 23:59:59'])
->groupBy('partner_stores.tenant_id', 'store_sales.store_id')
->selectRaw('
partner_stores.tenant_id as tenant_id,
store_sales.store_id,
SUM(store_sales.quantity) as qty,
SUM(store_sales.retail_price_centavos) as retail,
SUM(store_sales.commission_centavos) as commission
')
->get();
MonthlyStoreSale::where('month_start_date', $monthStart)->delete();
$count = 0;
foreach ($rows as $r) {
MonthlyStoreSale::create([
'tenant_id' => $r->tenant_id,
'month_start_date' => $monthStart,
'store_id' => $r->store_id,
'total_quantity_sold' => (int) $r->qty,
'total_retail_centavos' => (int) $r->retail,
'total_commission_centavos' => (int) $r->commission,
]);
$count++;
}
return $count;
}
}