Files
Verde-Web/database/seeders/ReportDataSeeder.php
Developer 7f3357206f feat: implement fixed dashboard layout, custom geofencing shapes, and QC seeder
- UI: Refactored admin layout to a fixed 'App Shell' design with independent scrollbars for sidebar and content area.
- UI: Cleaned up sidebar partial structure to support nested scrolling.
- GEOFENCING: Added support for Circle and Rectangle tools in LGU configuration.
- GEOFENCING: Implemented high-precision (128-point) circle-to-polygon conversion for database storage.
- PAGINATION: Integrated standard Laravel pagination across Teams and Reports tables.
- PAGINATION: Added a global 'renderPagination' helper and 'Per Page' selector (25, 50, 100).
- DATA: Created QuezonCitySeeder with 142 official barangays and 3 months of historical collection/sales data.
- FIX: Corrected API total aggregations in reports to remain accurate during pagination.
- FIX: Resolved unique constraint and axis-order issues in spatial data seeders.
2026-07-03 11:19:08 +08:00

152 lines
6.0 KiB
PHP

<?php
namespace Database\Seeders;
use App\Models\Barangay;
use App\Models\DailyCollectionStat;
use App\Models\Dumpsite;
use App\Models\DumpsiteRelease;
use App\Models\MonthlyStoreSale;
use App\Models\PartnerStore;
use App\Models\Route;
use App\Models\Trip;
use App\Models\User;
use App\Models\WeeklyRoutePerformance;
use Carbon\Carbon;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;
use MatanYadaev\EloquentSpatial\Objects\Point;
class ReportDataSeeder extends Seeder
{
public function run(): void
{
$tenantId = 4; // GenSan
// Truncate existing data to avoid unique constraint issues
DailyCollectionStat::where('tenant_id', $tenantId)->delete();
WeeklyRoutePerformance::where('tenant_id', $tenantId)->delete();
MonthlyStoreSale::where('tenant_id', $tenantId)->delete();
$barangays = Barangay::limit(5)->get();
$routes = Route::where('tenant_id', $tenantId)->get();
$stores = PartnerStore::where('tenant_id', $tenantId)->get();
$dumpsite = Dumpsite::where('tenant_id', $tenantId)->first();
$driver = User::where('role', User::ROLE_DRIVER)->where('tenant_id', $tenantId)->first();
if ($barangays->isEmpty() || $routes->isEmpty() || $stores->isEmpty() || !$dumpsite || !$driver) {
$this->command->warn('Prerequisites for ReportDataSeeder missing. Run SimulationContinuationSeeder first.');
return;
}
$this->seedDailyStats($tenantId, $barangays);
$this->seedWeeklyPerformance($tenantId, $routes);
$this->seedMonthlySales($tenantId, $stores);
$this->seedDumpsiteReleases($tenantId, $dumpsite, $driver);
$this->command->info('Report data seeded successfully!');
}
private function seedDailyStats($tenantId, $barangays): void
{
$this->command->info('Seeding Daily Collection Stats...');
for ($i = 30; $i >= 0; $i--) {
$date = Carbon::now()->subDays($i);
foreach ($barangays as $barangay) {
$baseScans = 150 + rand(-50, 100);
$baseWeight = $baseScans * 5 + rand(-50, 50);
DailyCollectionStat::withoutGlobalScopes()->updateOrCreate(
['date' => $date->toDateString(), 'barangay_id' => $barangay->id],
[
'tenant_id' => $tenantId,
'total_scans' => $baseScans,
'total_weight_kg' => $baseWeight,
'unique_households' => (int)($baseScans * 0.8),
'missed_pickups' => rand(0, 10),
]
);
}
}
}
private function seedWeeklyPerformance($tenantId, $routes): void
{
$this->command->info('Seeding Weekly Route Performance...');
for ($i = 8; $i >= 0; $i--) {
$weekStart = Carbon::now()->subWeeks($i)->startOfWeek();
foreach ($routes as $route) {
WeeklyRoutePerformance::withoutGlobalScopes()->updateOrCreate(
['week_start_date' => $weekStart->toDateString(), 'route_id' => $route->id],
[
'tenant_id' => $tenantId,
'on_time_rate_percent' => rand(85, 98),
'avg_trip_duration_minutes' => rand(150, 210),
'completion_rate_percent' => rand(95, 100),
'trips_count' => 6,
]
);
}
}
}
private function seedMonthlySales($tenantId, $stores): void
{
$this->command->info('Seeding Monthly Store Sales...');
for ($i = 6; $i >= 0; $i--) {
$monthStart = Carbon::now()->subMonths($i)->startOfMonth();
foreach ($stores as $store) {
$qty = 200 + rand(-50, 300);
$retail = $qty * 1500;
$commission = (int)($retail * ($store->commission_rate_percent / 100));
MonthlyStoreSale::withoutGlobalScopes()->updateOrCreate(
['month_start_date' => $monthStart->toDateString(), 'store_id' => $store->id],
[
'tenant_id' => $tenantId,
'total_quantity_sold' => $qty,
'total_retail_centavos' => $retail,
'total_commission_centavos' => $commission,
]
);
}
}
}
private function seedDumpsiteReleases($tenantId, $dumpsite, $driver): void
{
$this->command->info('Seeding Dumpsite Releases...');
for ($i = 20; $i >= 1; $i--) {
$releasedAt = Carbon::now()->subDays($i)->setTime(11, 30);
$trip = Trip::firstOrCreate(
['trip_number' => "HIST-TRIP-" . $releasedAt->format('Ymd')],
[
'uuid' => (string) Str::uuid(),
'tenant_id' => $tenantId,
'route_id' => Route::where('tenant_id', $tenantId)->first()->id,
'team_id' => 1,
'truck_id' => 1,
'scheduled_date' => $releasedAt->toDateString(),
'status' => Trip::STATUS_COMPLETED,
'actual_end_time' => $releasedAt,
'end_reason' => 'completed'
]
);
DumpsiteRelease::updateOrCreate(
['tenant_id' => $tenantId, 'trip_id' => $trip->id],
[
'dumpsite_id' => $dumpsite->id,
'released_at' => $releasedAt,
'released_by_driver_id' => $driver->id,
'weight_kg' => 4500 + rand(-500, 500),
'waste_type_breakdown' => ['mixed' => 70, 'recyclable' => 20, 'organic' => 10],
'gate_pass_number' => "GP-" . strtoupper(Str::random(8)),
'dumpsite_attendant_name' => "Attendant " . rand(1, 5),
'coordinates_at_release' => new Point(6.1136, 125.1719, 4326),
]
);
}
}
}