migrate to gtea from bistbucket
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\RestaurantDelivery\DTOs;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
readonly class CreateDeliveryDTO
|
||||
{
|
||||
public function __construct(
|
||||
public int $restaurantId,
|
||||
public string $restaurantName,
|
||||
public string $pickupAddress,
|
||||
public float $pickupLatitude,
|
||||
public float $pickupLongitude,
|
||||
public string $customerName,
|
||||
public string $dropAddress,
|
||||
public float $dropLatitude,
|
||||
public float $dropLongitude,
|
||||
public string $dropContactPhone,
|
||||
public ?int $zoneId = null,
|
||||
public ?string $orderableType = null,
|
||||
public ?int $orderableId = null,
|
||||
public ?string $pickupContactName = null,
|
||||
public ?string $pickupContactPhone = null,
|
||||
public ?string $pickupInstructions = null,
|
||||
public ?string $dropContactName = null,
|
||||
public ?string $dropInstructions = null,
|
||||
public ?string $dropFloor = null,
|
||||
public ?string $dropApartment = null,
|
||||
public bool $isScheduled = false,
|
||||
public ?\DateTimeInterface $scheduledFor = null,
|
||||
public bool $isPriority = false,
|
||||
public ?float $orderValue = null,
|
||||
public ?float $tipAmount = null,
|
||||
public ?string $tipType = null,
|
||||
public ?array $meta = null,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create DTO from request.
|
||||
*
|
||||
* IMPORTANT: restaurant_id is NEVER passed from request.
|
||||
* It is always obtained via getUserRestaurantId().
|
||||
*/
|
||||
public static function fromRequest(Request $request): self
|
||||
{
|
||||
return new self(
|
||||
restaurantId: (int) getUserRestaurantId(),
|
||||
restaurantName: $request->string('restaurant_name')->toString(),
|
||||
pickupAddress: $request->string('pickup_address')->toString(),
|
||||
pickupLatitude: (float) $request->input('pickup_latitude'),
|
||||
pickupLongitude: (float) $request->input('pickup_longitude'),
|
||||
customerName: $request->string('customer_name')->toString(),
|
||||
dropAddress: $request->string('drop_address')->toString(),
|
||||
dropLatitude: (float) $request->input('drop_latitude'),
|
||||
dropLongitude: (float) $request->input('drop_longitude'),
|
||||
dropContactPhone: $request->string('drop_contact_phone')->toString(),
|
||||
zoneId: $request->filled('zone_id') ? $request->integer('zone_id') : null,
|
||||
orderableType: $request->filled('orderable_type') ? $request->string('orderable_type')->toString() : null,
|
||||
orderableId: $request->filled('orderable_id') ? $request->integer('orderable_id') : null,
|
||||
pickupContactName: $request->filled('pickup_contact_name') ? $request->string('pickup_contact_name')->toString() : null,
|
||||
pickupContactPhone: $request->filled('pickup_contact_phone') ? $request->string('pickup_contact_phone')->toString() : null,
|
||||
pickupInstructions: $request->filled('pickup_instructions') ? $request->string('pickup_instructions')->toString() : null,
|
||||
dropContactName: $request->filled('drop_contact_name') ? $request->string('drop_contact_name')->toString() : null,
|
||||
dropInstructions: $request->filled('drop_instructions') ? $request->string('drop_instructions')->toString() : null,
|
||||
dropFloor: $request->filled('drop_floor') ? $request->string('drop_floor')->toString() : null,
|
||||
dropApartment: $request->filled('drop_apartment') ? $request->string('drop_apartment')->toString() : null,
|
||||
isScheduled: $request->boolean('is_scheduled'),
|
||||
scheduledFor: $request->filled('scheduled_for') ? new \DateTime($request->input('scheduled_for')) : null,
|
||||
isPriority: $request->boolean('is_priority'),
|
||||
orderValue: $request->filled('order_value') ? (float) $request->input('order_value') : null,
|
||||
tipAmount: $request->filled('tip_amount') ? (float) $request->input('tip_amount') : null,
|
||||
tipType: $request->filled('tip_type') ? $request->string('tip_type')->toString() : null,
|
||||
meta: $request->input('meta'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to array for model creation.
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return array_filter([
|
||||
'restaurant_id' => $this->restaurantId,
|
||||
'restaurant_name' => $this->restaurantName,
|
||||
'pickup_address' => $this->pickupAddress,
|
||||
'pickup_latitude' => $this->pickupLatitude,
|
||||
'pickup_longitude' => $this->pickupLongitude,
|
||||
'customer_name' => $this->customerName,
|
||||
'drop_address' => $this->dropAddress,
|
||||
'drop_latitude' => $this->dropLatitude,
|
||||
'drop_longitude' => $this->dropLongitude,
|
||||
'drop_contact_phone' => $this->dropContactPhone,
|
||||
'zone_id' => $this->zoneId,
|
||||
'orderable_type' => $this->orderableType,
|
||||
'orderable_id' => $this->orderableId,
|
||||
'pickup_contact_name' => $this->pickupContactName,
|
||||
'pickup_contact_phone' => $this->pickupContactPhone,
|
||||
'pickup_instructions' => $this->pickupInstructions,
|
||||
'drop_contact_name' => $this->dropContactName,
|
||||
'drop_instructions' => $this->dropInstructions,
|
||||
'drop_floor' => $this->dropFloor,
|
||||
'drop_apartment' => $this->dropApartment,
|
||||
'is_scheduled' => $this->isScheduled,
|
||||
'scheduled_for' => $this->scheduledFor?->format('Y-m-d H:i:s'),
|
||||
'is_priority' => $this->isPriority,
|
||||
'order_value' => $this->orderValue,
|
||||
'tip_amount' => $this->tipAmount,
|
||||
'tip_type' => $this->tipType,
|
||||
'meta' => $this->meta,
|
||||
], fn ($value) => $value !== null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\RestaurantDelivery\DTOs;
|
||||
|
||||
use Modules\RestaurantDelivery\Enums\DeliveryStatus;
|
||||
use Modules\RestaurantDelivery\Models\Delivery;
|
||||
|
||||
readonly class DeliveryTrackingDTO
|
||||
{
|
||||
public function __construct(
|
||||
public int $deliveryId,
|
||||
public string $trackingCode,
|
||||
public DeliveryStatus $status,
|
||||
public ?RiderLocationDTO $riderLocation = null,
|
||||
public ?float $pickupLatitude = null,
|
||||
public ?float $pickupLongitude = null,
|
||||
public ?float $dropLatitude = null,
|
||||
public ?float $dropLongitude = null,
|
||||
public ?string $pickupAddress = null,
|
||||
public ?string $dropAddress = null,
|
||||
public ?int $eta = null,
|
||||
public ?float $remainingDistance = null,
|
||||
public ?string $routePolyline = null,
|
||||
public ?array $riderInfo = null,
|
||||
public ?array $timeline = null,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create DTO from Delivery model and Firebase data.
|
||||
*/
|
||||
public static function fromDeliveryAndFirebase(Delivery $delivery, ?array $firebaseData = null): self
|
||||
{
|
||||
$riderLocation = null;
|
||||
if ($firebaseData && isset($firebaseData['rider_location'])) {
|
||||
$riderLocation = RiderLocationDTO::fromArray($firebaseData['rider_location']);
|
||||
}
|
||||
|
||||
return new self(
|
||||
deliveryId: $delivery->id,
|
||||
trackingCode: $delivery->tracking_code,
|
||||
status: $delivery->status,
|
||||
riderLocation: $riderLocation,
|
||||
pickupLatitude: (float) $delivery->pickup_latitude,
|
||||
pickupLongitude: (float) $delivery->pickup_longitude,
|
||||
dropLatitude: (float) $delivery->drop_latitude,
|
||||
dropLongitude: (float) $delivery->drop_longitude,
|
||||
pickupAddress: $delivery->pickup_address,
|
||||
dropAddress: $delivery->drop_address,
|
||||
eta: $firebaseData['eta'] ?? null,
|
||||
remainingDistance: $firebaseData['remaining_distance'] ?? null,
|
||||
routePolyline: $firebaseData['route']['polyline'] ?? $delivery->route_polyline,
|
||||
riderInfo: $delivery->rider ? [
|
||||
'id' => $delivery->rider->id,
|
||||
'name' => $delivery->rider->full_name,
|
||||
'phone' => $delivery->rider->phone,
|
||||
'photo' => $delivery->rider->photo_url,
|
||||
'rating' => $delivery->rider->rating,
|
||||
'vehicle_type' => $delivery->rider->vehicle_type,
|
||||
] : null,
|
||||
timeline: $delivery->statusHistory?->map(fn ($h) => [
|
||||
'status' => $h->to_status,
|
||||
'timestamp' => $h->changed_at->toIso8601String(),
|
||||
'label' => DeliveryStatus::from($h->to_status)->label(),
|
||||
])->toArray(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to array.
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'delivery_id' => $this->deliveryId,
|
||||
'tracking_code' => $this->trackingCode,
|
||||
'status' => $this->status->value,
|
||||
'status_label' => $this->status->label(),
|
||||
'status_description' => $this->status->description(),
|
||||
'status_color' => $this->status->color(),
|
||||
'rider_location' => $this->riderLocation?->toArray(),
|
||||
'pickup' => [
|
||||
'latitude' => $this->pickupLatitude,
|
||||
'longitude' => $this->pickupLongitude,
|
||||
'address' => $this->pickupAddress,
|
||||
],
|
||||
'drop' => [
|
||||
'latitude' => $this->dropLatitude,
|
||||
'longitude' => $this->dropLongitude,
|
||||
'address' => $this->dropAddress,
|
||||
],
|
||||
'eta' => $this->eta,
|
||||
'eta_formatted' => $this->eta ? $this->formatEta($this->eta) : null,
|
||||
'remaining_distance' => $this->remainingDistance,
|
||||
'remaining_distance_formatted' => $this->remainingDistance
|
||||
? round($this->remainingDistance, 1).' km'
|
||||
: null,
|
||||
'route_polyline' => $this->routePolyline,
|
||||
'rider' => $this->riderInfo,
|
||||
'timeline' => $this->timeline,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Format ETA in human-readable format.
|
||||
*/
|
||||
protected function formatEta(int $minutes): string
|
||||
{
|
||||
if ($minutes < 60) {
|
||||
return "{$minutes} min";
|
||||
}
|
||||
|
||||
$hours = floor($minutes / 60);
|
||||
$mins = $minutes % 60;
|
||||
|
||||
if ($mins === 0) {
|
||||
return "{$hours} hr";
|
||||
}
|
||||
|
||||
return "{$hours} hr {$mins} min";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\RestaurantDelivery\DTOs;
|
||||
|
||||
readonly class EarningsDTO
|
||||
{
|
||||
public function __construct(
|
||||
public float $baseAmount,
|
||||
public float $distanceAmount,
|
||||
public float $totalBonus,
|
||||
public float $totalPenalty,
|
||||
public float $tipAmount,
|
||||
public float $grossAmount,
|
||||
public float $commissionRate,
|
||||
public float $commissionAmount,
|
||||
public float $netAmount,
|
||||
public string $currency,
|
||||
public array $breakdown = [],
|
||||
public array $bonuses = [],
|
||||
public array $penalties = [],
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create DTO from calculation result.
|
||||
*/
|
||||
public static function fromCalculation(array $data): self
|
||||
{
|
||||
return new self(
|
||||
baseAmount: (float) ($data['base_amount'] ?? 0),
|
||||
distanceAmount: (float) ($data['distance_amount'] ?? 0),
|
||||
totalBonus: (float) ($data['total_bonus'] ?? 0),
|
||||
totalPenalty: (float) ($data['total_penalty'] ?? 0),
|
||||
tipAmount: (float) ($data['tip_amount'] ?? 0),
|
||||
grossAmount: (float) ($data['gross_amount'] ?? 0),
|
||||
commissionRate: (float) ($data['commission_rate'] ?? 0),
|
||||
commissionAmount: (float) ($data['commission_amount'] ?? 0),
|
||||
netAmount: (float) ($data['net_amount'] ?? 0),
|
||||
currency: $data['currency'] ?? config('restaurant-delivery.pricing.currency', 'BDT'),
|
||||
breakdown: $data['breakdown'] ?? [],
|
||||
bonuses: $data['bonuses'] ?? [],
|
||||
penalties: $data['penalties'] ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to array.
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'base_amount' => $this->baseAmount,
|
||||
'distance_amount' => $this->distanceAmount,
|
||||
'total_bonus' => $this->totalBonus,
|
||||
'total_penalty' => $this->totalPenalty,
|
||||
'tip_amount' => $this->tipAmount,
|
||||
'gross_amount' => $this->grossAmount,
|
||||
'commission_rate' => $this->commissionRate,
|
||||
'commission_amount' => $this->commissionAmount,
|
||||
'net_amount' => $this->netAmount,
|
||||
'currency' => $this->currency,
|
||||
'breakdown' => $this->breakdown,
|
||||
'bonuses' => $this->bonuses,
|
||||
'penalties' => $this->penalties,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get formatted amounts for display.
|
||||
*/
|
||||
public function toFormattedArray(): array
|
||||
{
|
||||
$symbol = config('restaurant-delivery.pricing.currency_symbol', '৳');
|
||||
|
||||
return [
|
||||
'base_amount' => $symbol.number_format($this->baseAmount, 2),
|
||||
'distance_amount' => $symbol.number_format($this->distanceAmount, 2),
|
||||
'total_bonus' => $symbol.number_format($this->totalBonus, 2),
|
||||
'total_penalty' => $symbol.number_format($this->totalPenalty, 2),
|
||||
'tip_amount' => $symbol.number_format($this->tipAmount, 2),
|
||||
'gross_amount' => $symbol.number_format($this->grossAmount, 2),
|
||||
'commission_amount' => $symbol.number_format($this->commissionAmount, 2),
|
||||
'net_amount' => $symbol.number_format($this->netAmount, 2),
|
||||
'commission_rate' => $this->commissionRate.'%',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\RestaurantDelivery\DTOs;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
readonly class RatingDTO
|
||||
{
|
||||
public function __construct(
|
||||
public int $overallRating,
|
||||
public ?int $speedRating = null,
|
||||
public ?int $communicationRating = null,
|
||||
public ?int $foodConditionRating = null,
|
||||
public ?int $professionalismRating = null,
|
||||
public ?string $review = null,
|
||||
public array $tags = [],
|
||||
public bool $isAnonymous = false,
|
||||
public bool $isRestaurantRating = false,
|
||||
public ?int $customerId = null,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create DTO from request.
|
||||
*/
|
||||
public static function fromRequest(Request $request): self
|
||||
{
|
||||
return new self(
|
||||
overallRating: $request->integer('overall_rating'),
|
||||
speedRating: $request->filled('speed_rating') ? $request->integer('speed_rating') : null,
|
||||
communicationRating: $request->filled('communication_rating') ? $request->integer('communication_rating') : null,
|
||||
foodConditionRating: $request->filled('food_condition_rating') ? $request->integer('food_condition_rating') : null,
|
||||
professionalismRating: $request->filled('professionalism_rating') ? $request->integer('professionalism_rating') : null,
|
||||
review: $request->filled('review') ? $request->string('review')->toString() : null,
|
||||
tags: $request->input('tags', []),
|
||||
isAnonymous: $request->boolean('is_anonymous'),
|
||||
isRestaurantRating: $request->boolean('is_restaurant_rating'),
|
||||
customerId: $request->filled('customer_id') ? $request->integer('customer_id') : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category ratings as array.
|
||||
*/
|
||||
public function categoryRatings(): array
|
||||
{
|
||||
return array_filter([
|
||||
'speed' => $this->speedRating,
|
||||
'communication' => $this->communicationRating,
|
||||
'food_condition' => $this->foodConditionRating,
|
||||
'professionalism' => $this->professionalismRating,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to array.
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'overall_rating' => $this->overallRating,
|
||||
'speed_rating' => $this->speedRating,
|
||||
'communication_rating' => $this->communicationRating,
|
||||
'food_condition_rating' => $this->foodConditionRating,
|
||||
'professionalism_rating' => $this->professionalismRating,
|
||||
'review' => $this->review,
|
||||
'tags' => $this->tags,
|
||||
'is_anonymous' => $this->isAnonymous,
|
||||
'is_restaurant_rating' => $this->isRestaurantRating,
|
||||
'customer_id' => $this->customerId,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\RestaurantDelivery\DTOs;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
readonly class RiderLocationDTO
|
||||
{
|
||||
public function __construct(
|
||||
public float $latitude,
|
||||
public float $longitude,
|
||||
public ?float $speed = null,
|
||||
public ?float $bearing = null,
|
||||
public ?float $accuracy = null,
|
||||
public ?float $altitude = null,
|
||||
public ?\DateTimeInterface $timestamp = null,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create DTO from request.
|
||||
*/
|
||||
public static function fromRequest(Request $request): self
|
||||
{
|
||||
return new self(
|
||||
latitude: (float) $request->input('latitude'),
|
||||
longitude: (float) $request->input('longitude'),
|
||||
speed: $request->filled('speed') ? (float) $request->input('speed') : null,
|
||||
bearing: $request->filled('bearing') ? (float) $request->input('bearing') : null,
|
||||
accuracy: $request->filled('accuracy') ? (float) $request->input('accuracy') : null,
|
||||
altitude: $request->filled('altitude') ? (float) $request->input('altitude') : null,
|
||||
timestamp: $request->filled('timestamp') ? new \DateTime($request->input('timestamp')) : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create DTO from array.
|
||||
*/
|
||||
public static function fromArray(array $data): self
|
||||
{
|
||||
return new self(
|
||||
latitude: (float) ($data['latitude'] ?? $data['lat'] ?? 0),
|
||||
longitude: (float) ($data['longitude'] ?? $data['lng'] ?? 0),
|
||||
speed: isset($data['speed']) ? (float) $data['speed'] : null,
|
||||
bearing: isset($data['bearing']) ? (float) $data['bearing'] : null,
|
||||
accuracy: isset($data['accuracy']) ? (float) $data['accuracy'] : null,
|
||||
altitude: isset($data['altitude']) ? (float) $data['altitude'] : null,
|
||||
timestamp: isset($data['timestamp']) ? new \DateTime($data['timestamp']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to array.
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return array_filter([
|
||||
'latitude' => $this->latitude,
|
||||
'longitude' => $this->longitude,
|
||||
'speed' => $this->speed,
|
||||
'bearing' => $this->bearing,
|
||||
'accuracy' => $this->accuracy,
|
||||
'altitude' => $this->altitude,
|
||||
'timestamp' => $this->timestamp?->format('Y-m-d H:i:s'),
|
||||
], fn ($value) => $value !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to Firebase format.
|
||||
*/
|
||||
public function toFirebaseFormat(): array
|
||||
{
|
||||
return [
|
||||
'lat' => $this->latitude,
|
||||
'lng' => $this->longitude,
|
||||
'speed' => $this->speed,
|
||||
'bearing' => $this->bearing,
|
||||
'accuracy' => $this->accuracy,
|
||||
'timestamp' => ($this->timestamp ?? now())->getTimestamp() * 1000, // JavaScript timestamp
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if location is valid.
|
||||
*/
|
||||
public function isValid(): bool
|
||||
{
|
||||
return $this->latitude >= -90
|
||||
&& $this->latitude <= 90
|
||||
&& $this->longitude >= -180
|
||||
&& $this->longitude <= 180;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if accuracy is acceptable.
|
||||
*/
|
||||
public function hasAcceptableAccuracy(?float $threshold = null): bool
|
||||
{
|
||||
if ($this->accuracy === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$threshold = $threshold ?? config('restaurant-delivery.firebase.location.accuracy_threshold', 50);
|
||||
|
||||
return $this->accuracy <= $threshold;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user