fix(api): force Manila timezone for upcoming pickups evaluation

This commit is contained in:
ramram1515
2026-06-30 13:20:31 +08:00
parent 54de35b03d
commit 88caf14aa1
5 changed files with 111 additions and 39 deletions

View File

@@ -28,11 +28,12 @@ class HouseholdController extends ApiController
$data = $request->validated();
$user = $request->user();
$existing = Household::where('head_user_id', $user->id)->first();
if ($existing) {
$existingHead = Household::where('head_user_id', $user->id)->exists();
$existingMember = HouseholdMember::where('user_id', $user->id)->exists();
if ($existingHead || $existingMember) {
return $this->fail(
'You already have a household. Update it instead.',
['head_user_id' => ['already_has_household']],
'You are already part of a household.',
['head_user_id' => ['You are already in a household.']],
409,
);
}
@@ -173,13 +174,39 @@ class HouseholdController extends ApiController
$this->authorizeOwn($request->user(), $household);
$data = $request->validated();
$member = HouseholdMember::create([
'household_id' => $household->id,
'user_id' => $data['user_id'] ?? null,
'relationship' => $data['relationship'],
'full_name' => $data['full_name'] ?? null,
'date_of_birth' => $data['date_of_birth'] ?? null,
]);
$userToAdd = User::where('email', $data['email'])->first();
// Check if this user is already a member of any household or the head of one
$isMember = HouseholdMember::where('user_id', $userToAdd->id)->exists();
$isHead = Household::where('head_user_id', $userToAdd->id)->exists();
if ($isMember || $isHead) {
return $this->fail('This user is already part of a household.', ['email' => ['Already in a household.']], 422);
}
$existingSoftDeleted = HouseholdMember::withTrashed()
->where('household_id', $household->id)
->where('user_id', $userToAdd->id)
->first();
if ($existingSoftDeleted) {
$existingSoftDeleted->restore();
$existingSoftDeleted->update([
'relationship' => HouseholdMember::RELATIONSHIP_OTHER,
'full_name' => $userToAdd->full_name,
'date_of_birth' => $data['date_of_birth'] ?? null,
]);
$member = $existingSoftDeleted;
} else {
$member = HouseholdMember::create([
'household_id' => $household->id,
'user_id' => $userToAdd->id,
'relationship' => HouseholdMember::RELATIONSHIP_OTHER,
'full_name' => $userToAdd->full_name,
'date_of_birth' => $data['date_of_birth'] ?? null,
]);
}
return $this->created(
new HouseholdResource(

View File

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1\Me;
use App\Http\Controllers\Api\V1\ApiController;
use App\Models\Household;
use App\Models\Trip;
use App\Services\DropOff\DropOffPointFinder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -16,36 +17,70 @@ class UpcomingPickupsController extends ApiController
* always included so the resident sees a "truck on the way"
* card even mid-day.
*/
public function index(Request $request): JsonResponse
public function index(Request $request, DropOffPointFinder $finder): JsonResponse
{
$household = Household::with('assignedDropOffPoint')
->where('head_user_id', $request->user()->id)
->first();
if (! $household || ! $household->assigned_drop_off_point_id) {
if (! $household) {
return $this->ok([
'household_assigned' => false,
'pickups' => [],
]);
}
$dopId = $household->assigned_drop_off_point_id;
$nearbyDopIds = [];
if ($household->coordinates) {
$nearbyDops = $finder->nearby(
$household->coordinates->latitude,
$household->coordinates->longitude,
2.0, // 2km radius
10 // max 10 nearby drop-off points
);
$nearbyDopIds = $nearbyDops->pluck('id')->toArray();
}
if ($household->assigned_drop_off_point_id) {
$nearbyDopIds[] = $household->assigned_drop_off_point_id;
}
$nearbyDopIds = array_unique($nearbyDopIds);
if (empty($nearbyDopIds)) {
return $this->ok([
'household_assigned' => (bool) $household->assigned_drop_off_point_id,
'drop_off_point' => null,
'pickups' => [],
]);
}
$trips = Trip::query()
->with(['route:id,name,code', 'team:id,name,driver_id', 'team.driver:id,first_name,last_name', 'truck:id,plate_number'])
->whereHas('route.stops', fn ($q) => $q->where('drop_off_point_id', $dopId))
->where(function ($q) {
$q->where('scheduled_date', '>=', now()->toDateString())
->orWhereIn('status', [Trip::STATUS_IN_PROGRESS, Trip::STATUS_AT_DUMPSITE]);
})
->whereHas('route.stops', fn ($q) => $q->whereIn('drop_off_point_id', $nearbyDopIds))
->whereNotIn('status', [Trip::STATUS_CANCELLED, Trip::STATUS_COMPLETED])
->where(function ($query) {
// Future dates are always upcoming (evaluated in Manila time)
$query->where('scheduled_date', '>', now('Asia/Manila')->toDateString())
// For today, check the time
->orWhere(function ($q) {
$q->where('scheduled_date', now('Asia/Manila')->toDateString())
->where(function ($sub) {
// Keep if the time hasn't passed yet (evaluated in Manila time)
$sub->where('scheduled_start_time', '>=', now('Asia/Manila')->toTimeString())
// OR if the driver has already started the trip
->orWhereIn('status', [Trip::STATUS_IN_PROGRESS, Trip::STATUS_AT_DUMPSITE]);
});
});
})
->orderBy('scheduled_date')
->orderBy('scheduled_start_time')
->limit(10)
->get();
$pickups = $trips->map(function (Trip $trip) use ($dopId) {
$myStop = $trip->route?->stops()->where('drop_off_point_id', $dopId)->first();
$pickups = $trips->map(function (Trip $trip) use ($nearbyDopIds) {
$myStop = $trip->route?->stops()->whereIn('drop_off_point_id', $nearbyDopIds)->first();
return [
'trip_id' => $trip->uuid,
@@ -61,11 +96,11 @@ class UpcomingPickupsController extends ApiController
});
return $this->ok([
'household_assigned' => true,
'drop_off_point' => [
'id' => $household->assignedDropOffPoint?->uuid,
'name' => $household->assignedDropOffPoint?->name,
],
'household_assigned' => (bool) $household->assigned_drop_off_point_id,
'drop_off_point' => $household->assignedDropOffPoint ? [
'id' => $household->assignedDropOffPoint->uuid,
'name' => $household->assignedDropOffPoint->name,
] : null,
'pickups' => $pickups,
]);
}