@fleetbase/fleetops-engine 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +14 -0
- package/.php-cs-fixer.cache +1 -0
- package/.php-cs-fixer.php +29 -0
- package/LICENSE.md +16 -4
- package/README.md +103 -13
- package/composer.json +85 -0
- package/extension.json +10 -0
- package/package.json +3 -3
- package/phpstan.neon.dist +8 -0
- package/phpunit.xml.dist +16 -0
- package/server/config/api.php +765 -0
- package/server/config/cache.stores.php +18 -0
- package/server/config/dompdf.php +242 -0
- package/server/config/fleetops.php +19 -0
- package/server/config/geocoder.php +108 -0
- package/server/data/globe.json +261 -0
- package/server/migrations/2023_04_27_053456_create_contacts_table.php +49 -0
- package/server/migrations/2023_04_27_053456_create_drivers_table.php +62 -0
- package/server/migrations/2023_04_27_053456_create_entities_table.php +67 -0
- package/server/migrations/2023_04_27_053456_create_fleet_drivers_table.php +39 -0
- package/server/migrations/2023_04_27_053456_create_fleets_table.php +47 -0
- package/server/migrations/2023_04_27_053456_create_fuel_reports_table.php +50 -0
- package/server/migrations/2023_04_27_053456_create_integrated_vendors_table.php +44 -0
- package/server/migrations/2023_04_27_053456_create_issues_table.php +53 -0
- package/server/migrations/2023_04_27_053456_create_orders_table.php +70 -0
- package/server/migrations/2023_04_27_053456_create_payloads_table.php +51 -0
- package/server/migrations/2023_04_27_053456_create_places_table.php +62 -0
- package/server/migrations/2023_04_27_053456_create_positions_table.php +46 -0
- package/server/migrations/2023_04_27_053456_create_proofs_table.php +44 -0
- package/server/migrations/2023_04_27_053456_create_purchase_rates_table.php +46 -0
- package/server/migrations/2023_04_27_053456_create_routes_table.php +40 -0
- package/server/migrations/2023_04_27_053456_create_service_areas_table.php +47 -0
- package/server/migrations/2023_04_27_053456_create_service_quote_items_table.php +42 -0
- package/server/migrations/2023_04_27_053456_create_service_quotes_table.php +47 -0
- package/server/migrations/2023_04_27_053456_create_service_rate_fees_table.php +45 -0
- package/server/migrations/2023_04_27_053456_create_service_rate_parcel_fees_table.php +47 -0
- package/server/migrations/2023_04_27_053456_create_service_rates_table.php +61 -0
- package/server/migrations/2023_04_27_053456_create_tracking_numbers_table.php +46 -0
- package/server/migrations/2023_04_27_053456_create_tracking_statuses_table.php +54 -0
- package/server/migrations/2023_04_27_053456_create_vehicles_table.php +84 -0
- package/server/migrations/2023_04_27_053456_create_vendors_table.php +56 -0
- package/server/migrations/2023_04_27_053456_create_waypoints_table.php +45 -0
- package/server/migrations/2023_04_27_053456_create_zones_table.php +46 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_companies_table.php +32 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_contacts_table.php +34 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_drivers_table.php +40 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_entities_table.php +40 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_fleet_drivers_table.php +34 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_fleets_table.php +36 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_fuel_reports_table.php +36 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_integrated_vendors_table.php +34 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_issues_table.php +38 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_orders_table.php +48 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_payloads_table.php +40 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_places_table.php +32 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_positions_table.php +36 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_proofs_table.php +34 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_purchase_rates_table.php +38 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_routes_table.php +34 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_service_areas_table.php +34 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_service_quote_items_table.php +32 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_service_quotes_table.php +38 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_service_rate_fees_table.php +32 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_service_rate_parcel_fees_table.php +32 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_service_rates_table.php +36 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_tracking_numbers_table.php +34 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_tracking_statuses_table.php +34 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_vehicles_table.php +36 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_vendors_table.php +40 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_waypoints_table.php +38 -0
- package/server/migrations/2023_04_27_053459_add_foreign_keys_to_zones_table.php +34 -0
- package/server/migrations/2023_09_14_074512_update_vehicles_table.php +100 -0
- package/server/migrations/2023_09_14_074619_create_vehicle_devices_table.php +44 -0
- package/server/migrations/2023_09_26_071205_add_online_column_to_vehicles_table.php +32 -0
- package/server/migrations/2023_09_26_083957_create_fleet_vehicles_table.php +45 -0
- package/server/migrations/2023_09_27_023757_add_column_to_fleets_table.php +46 -0
- package/server/migrations/2023_10_05_074811_update_vehicle.php +42 -0
- package/server/resources/views/labels/default.php +165 -0
- package/server/src/Auth/Schemas/FleetOps.php +75 -0
- package/server/src/Casts/MultiPolygon.php +50 -0
- package/server/src/Casts/Point.php +109 -0
- package/server/src/Casts/Polygon.php +46 -0
- package/server/src/Console/Commands/DispatchAdhocOrders.php +137 -0
- package/server/src/Console/Commands/DispatchOrders.php +71 -0
- package/server/src/Console/Commands/TrackOrderDistanceAndTime.php +62 -0
- package/server/src/Events/DriverLocationChanged.php +174 -0
- package/server/src/Events/DriverSimulatedLocationChanged.php +183 -0
- package/server/src/Events/EntityDriverAssigned.php +36 -0
- package/server/src/Events/OrderCanceled.php +15 -0
- package/server/src/Events/OrderCompleted.php +15 -0
- package/server/src/Events/OrderDispatchFailed.php +42 -0
- package/server/src/Events/OrderDispatched.php +15 -0
- package/server/src/Events/OrderDriverAssigned.php +15 -0
- package/server/src/Events/OrderReady.php +15 -0
- package/server/src/Events/OrderStarted.php +15 -0
- package/server/src/Exceptions/IntegratedVendorException.php +18 -0
- package/server/src/Expansions/UserExpansion.php +63 -0
- package/server/src/Exports/ContactExport.php +56 -0
- package/server/src/Exports/DriverExport.php +61 -0
- package/server/src/Exports/FleetExport.php +53 -0
- package/server/src/Exports/PlaceExport.php +55 -0
- package/server/src/Exports/ServiceAreaExport.php +58 -0
- package/server/src/Exports/VehicleExport.php +57 -0
- package/server/src/Exports/VendorExport.php +57 -0
- package/server/src/Http/Controllers/Api/v1/ContactController.php +133 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +691 -0
- package/server/src/Http/Controllers/Api/v1/EntityController.php +230 -0
- package/server/src/Http/Controllers/Api/v1/FleetController.php +146 -0
- package/server/src/Http/Controllers/Api/v1/LabelController.php +63 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +1114 -0
- package/server/src/Http/Controllers/Api/v1/PayloadController.php +195 -0
- package/server/src/Http/Controllers/Api/v1/PlaceController.php +333 -0
- package/server/src/Http/Controllers/Api/v1/PurchaseRateController.php +199 -0
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +133 -0
- package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +381 -0
- package/server/src/Http/Controllers/Api/v1/ServiceRateController.php +223 -0
- package/server/src/Http/Controllers/Api/v1/TrackingNumberController.php +146 -0
- package/server/src/Http/Controllers/Api/v1/TrackingStatusController.php +138 -0
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +191 -0
- package/server/src/Http/Controllers/Api/v1/VendorController.php +159 -0
- package/server/src/Http/Controllers/Api/v1/ZoneController.php +146 -0
- package/server/src/Http/Controllers/FleetOpsController.php +13 -0
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +69 -0
- package/server/src/Http/Controllers/Internal/v1/DriverController.php +261 -0
- package/server/src/Http/Controllers/Internal/v1/EntityController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/FleetController.php +152 -0
- package/server/src/Http/Controllers/Internal/v1/FleetOpsLookupController.php +80 -0
- package/server/src/Http/Controllers/Internal/v1/FuelReportController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/GeocoderController.php +96 -0
- package/server/src/Http/Controllers/Internal/v1/IntegratedVendorController.php +63 -0
- package/server/src/Http/Controllers/Internal/v1/IssueController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +127 -0
- package/server/src/Http/Controllers/Internal/v1/MetricsController.php +75 -0
- package/server/src/Http/Controllers/Internal/v1/MorphController.php +77 -0
- package/server/src/Http/Controllers/Internal/v1/OrderConfigController.php +269 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +564 -0
- package/server/src/Http/Controllers/Internal/v1/PayloadController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/PlaceController.php +163 -0
- package/server/src/Http/Controllers/Internal/v1/ProofController.php +139 -0
- package/server/src/Http/Controllers/Internal/v1/PurchaseRateController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/RouteController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/ServiceAreaController.php +32 -0
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +345 -0
- package/server/src/Http/Controllers/Internal/v1/ServiceRateController.php +47 -0
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +30 -0
- package/server/src/Http/Controllers/Internal/v1/TrackingNumberController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/TrackingStatusController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/VehicleController.php +65 -0
- package/server/src/Http/Controllers/Internal/v1/VehicleDeviceController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/VendorController.php +120 -0
- package/server/src/Http/Controllers/Internal/v1/ZoneController.php +15 -0
- package/server/src/Http/Filter/ContactFilter.php +86 -0
- package/server/src/Http/Filter/DriverFilter.php +148 -0
- package/server/src/Http/Filter/FleetFilter.php +108 -0
- package/server/src/Http/Filter/IntegratedVendorFilter.php +13 -0
- package/server/src/Http/Filter/OrderFilter.php +242 -0
- package/server/src/Http/Filter/PlaceFilter.php +111 -0
- package/server/src/Http/Filter/ServiceAreaFilter.php +13 -0
- package/server/src/Http/Filter/ServiceQuoteFilter.php +13 -0
- package/server/src/Http/Filter/ServiceRateFilter.php +33 -0
- package/server/src/Http/Filter/TrackingStatusFilter.php +43 -0
- package/server/src/Http/Filter/VehicleFilter.php +91 -0
- package/server/src/Http/Filter/VendorFilter.php +83 -0
- package/server/src/Http/Filter/ZoneFilter.php +13 -0
- package/server/src/Http/Requests/CancelOrderRequest.php +31 -0
- package/server/src/Http/Requests/CreateContactRequest.php +33 -0
- package/server/src/Http/Requests/CreateDriverRequest.php +55 -0
- package/server/src/Http/Requests/CreateEntityRequest.php +43 -0
- package/server/src/Http/Requests/CreateFleetRequest.php +32 -0
- package/server/src/Http/Requests/CreateOrderRequest.php +66 -0
- package/server/src/Http/Requests/CreatePayloadRequest.php +37 -0
- package/server/src/Http/Requests/CreatePlaceRequest.php +38 -0
- package/server/src/Http/Requests/CreatePurchaseRateRequest.php +33 -0
- package/server/src/Http/Requests/CreateServiceAreaRequest.php +35 -0
- package/server/src/Http/Requests/CreateServiceQuoteRequest.php +29 -0
- package/server/src/Http/Requests/CreateServiceRateRequest.php +56 -0
- package/server/src/Http/Requests/CreateTrackingNumberRequest.php +34 -0
- package/server/src/Http/Requests/CreateTrackingStatusRequest.php +42 -0
- package/server/src/Http/Requests/CreateVehicleRequest.php +32 -0
- package/server/src/Http/Requests/CreateVendorRequest.php +35 -0
- package/server/src/Http/Requests/CreateZoneRequest.php +35 -0
- package/server/src/Http/Requests/DecodeTrackingNumberQR.php +30 -0
- package/server/src/Http/Requests/DriverSimulationRequest.php +46 -0
- package/server/src/Http/Requests/Internal/AssignOrderRequest.php +31 -0
- package/server/src/Http/Requests/Internal/CreateDriverRequest.php +42 -0
- package/server/src/Http/Requests/Internal/FleetActionRequest.php +32 -0
- package/server/src/Http/Requests/Internal/UpdateDriverRequest.php +7 -0
- package/server/src/Http/Requests/QueryServiceQuotesRequest.php +41 -0
- package/server/src/Http/Requests/UpdateContactRequest.php +20 -0
- package/server/src/Http/Requests/UpdateDriverRequest.php +7 -0
- package/server/src/Http/Requests/UpdateEntityRequest.php +7 -0
- package/server/src/Http/Requests/UpdateFleetRequest.php +7 -0
- package/server/src/Http/Requests/UpdateOrderRequest.php +7 -0
- package/server/src/Http/Requests/UpdatePayloadRequest.php +7 -0
- package/server/src/Http/Requests/UpdatePlaceRequest.php +7 -0
- package/server/src/Http/Requests/UpdatePurchaseRateRequest.php +7 -0
- package/server/src/Http/Requests/UpdateServiceAreaRequest.php +29 -0
- package/server/src/Http/Requests/UpdateServiceQuoteRequest.php +29 -0
- package/server/src/Http/Requests/UpdateServiceRateRequest.php +29 -0
- package/server/src/Http/Requests/UpdateTrackingNumberRequest.php +7 -0
- package/server/src/Http/Requests/UpdateTrackingStatusRequest.php +32 -0
- package/server/src/Http/Requests/UpdateVehicleRequest.php +7 -0
- package/server/src/Http/Requests/UpdateVendorRequest.php +7 -0
- package/server/src/Http/Requests/UpdateZoneRequest.php +7 -0
- package/server/src/Http/Resources/Internal/v1/IntegratedVendorFacilitator.php +37 -0
- package/server/src/Http/Resources/Internal/v1/Vehicle.php +33 -0
- package/server/src/Http/Resources/Internal/v1/Waypoint.php +34 -0
- package/server/src/Http/Resources/v1/Contact.php +62 -0
- package/server/src/Http/Resources/v1/CurrentJob.php +36 -0
- package/server/src/Http/Resources/v1/DeletedResource.php +61 -0
- package/server/src/Http/Resources/v1/Driver.php +89 -0
- package/server/src/Http/Resources/v1/Entity.php +107 -0
- package/server/src/Http/Resources/v1/Fleet.php +60 -0
- package/server/src/Http/Resources/v1/FuelReport.php +67 -0
- package/server/src/Http/Resources/v1/Order.php +131 -0
- package/server/src/Http/Resources/v1/Payload.php +94 -0
- package/server/src/Http/Resources/v1/Place.php +84 -0
- package/server/src/Http/Resources/v1/Proof.php +36 -0
- package/server/src/Http/Resources/v1/PurchaseRate.php +77 -0
- package/server/src/Http/Resources/v1/ServiceArea.php +55 -0
- package/server/src/Http/Resources/v1/ServiceQuote.php +57 -0
- package/server/src/Http/Resources/v1/ServiceRate.php +94 -0
- package/server/src/Http/Resources/v1/ServiceRateFee.php +58 -0
- package/server/src/Http/Resources/v1/ServiceRateParcelFee.php +48 -0
- package/server/src/Http/Resources/v1/TrackingNumber.php +62 -0
- package/server/src/Http/Resources/v1/TrackingStatus.php +84 -0
- package/server/src/Http/Resources/v1/Vehicle.php +75 -0
- package/server/src/Http/Resources/v1/VehicleDevice.php +44 -0
- package/server/src/Http/Resources/v1/VehicleWithoutDriver.php +72 -0
- package/server/src/Http/Resources/v1/Vendor.php +72 -0
- package/server/src/Http/Resources/v1/Zone.php +60 -0
- package/server/src/Imports/OrdersImport.php +18 -0
- package/server/src/Integrations/Lalamove/Lalamove.php +678 -0
- package/server/src/Integrations/Lalamove/LalamoveDeliveryStop.php +97 -0
- package/server/src/Integrations/Lalamove/LalamoveMarket.php +123 -0
- package/server/src/Integrations/Lalamove/LalamoveServiceType.php +161 -0
- package/server/src/Jobs/SimulateDrivingRoute.php +81 -0
- package/server/src/Jobs/SimulateWaypointReached.php +79 -0
- package/server/src/Listeners/HandleOrderCanceled.php +46 -0
- package/server/src/Listeners/HandleOrderCreated.php +23 -0
- package/server/src/Listeners/HandleOrderDispatchFailed.php +35 -0
- package/server/src/Listeners/HandleOrderDispatched.php +108 -0
- package/server/src/Listeners/HandleOrderDriverAssigned.php +42 -0
- package/server/src/Listeners/HandleOrderReady.php +27 -0
- package/server/src/Listeners/HandleUserRemovedFromCompany.php +31 -0
- package/server/src/Models/Contact.php +239 -0
- package/server/src/Models/Driver.php +543 -0
- package/server/src/Models/Entity.php +416 -0
- package/server/src/Models/Fleet.php +203 -0
- package/server/src/Models/FleetDriver.php +73 -0
- package/server/src/Models/FleetVehicle.php +73 -0
- package/server/src/Models/FuelReport.php +125 -0
- package/server/src/Models/IntegratedVendor.php +219 -0
- package/server/src/Models/Issue.php +111 -0
- package/server/src/Models/Order.php +1132 -0
- package/server/src/Models/Payload.php +699 -0
- package/server/src/Models/Place.php +656 -0
- package/server/src/Models/Position.php +119 -0
- package/server/src/Models/Proof.php +94 -0
- package/server/src/Models/PurchaseRate.php +208 -0
- package/server/src/Models/Route.php +128 -0
- package/server/src/Models/ServiceArea.php +161 -0
- package/server/src/Models/ServiceQuote.php +207 -0
- package/server/src/Models/ServiceQuoteItem.php +46 -0
- package/server/src/Models/ServiceRate.php +953 -0
- package/server/src/Models/ServiceRateFee.php +96 -0
- package/server/src/Models/ServiceRateParcelFee.php +116 -0
- package/server/src/Models/TrackingNumber.php +310 -0
- package/server/src/Models/TrackingStatus.php +177 -0
- package/server/src/Models/Vehicle.php +452 -0
- package/server/src/Models/VehicleDevice.php +79 -0
- package/server/src/Models/Vendor.php +244 -0
- package/server/src/Models/Waypoint.php +226 -0
- package/server/src/Models/Zone.php +95 -0
- package/server/src/Notifications/OrderAssigned.php +140 -0
- package/server/src/Notifications/OrderCanceled.php +129 -0
- package/server/src/Notifications/OrderDispatchFailed.php +81 -0
- package/server/src/Notifications/OrderDispatched.php +173 -0
- package/server/src/Notifications/OrderPing.php +162 -0
- package/server/src/Notifications/OrderSplit.php +56 -0
- package/server/src/Observers/DriverObserver.php +34 -0
- package/server/src/Observers/OrderObserver.php +34 -0
- package/server/src/Observers/PayloadObserver.php +30 -0
- package/server/src/Observers/PlaceObserver.php +26 -0
- package/server/src/Observers/PurchaseRateObserver.php +61 -0
- package/server/src/Observers/ServiceAreaObserver.php +35 -0
- package/server/src/Observers/ServiceRateObserver.php +78 -0
- package/server/src/Observers/TrackingNumberObserver.php +44 -0
- package/server/src/Observers/UserObserver.php +20 -0
- package/server/src/Observers/VehicleObserver.php +66 -0
- package/server/src/Providers/EventServiceProvider.php +30 -0
- package/server/src/Providers/FleetOpsServiceProvider.php +94 -0
- package/server/src/Rules/ResolvablePoint.php +37 -0
- package/server/src/Scopes/DriverScope.php +21 -0
- package/server/src/Support/Algo.php +55 -0
- package/server/src/Support/DistanceMatrix.php +27 -0
- package/server/src/Support/Encoding/Polyline.php +168 -0
- package/server/src/Support/Flow.php +622 -0
- package/server/src/Support/Geocoding.php +216 -0
- package/server/src/Support/IntegratedVendors.php +287 -0
- package/server/src/Support/Metrics.php +305 -0
- package/server/src/Support/OSRM.php +191 -0
- package/server/src/Support/ParsePhone.php +21 -0
- package/server/src/Support/Utils.php +1145 -0
- package/server/src/Traits/HasTrackingNumber.php +185 -0
- package/server/src/routes.php +381 -0
- package/server/tests/Feature.php +5 -0
|
@@ -0,0 +1,953 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Models;
|
|
4
|
+
|
|
5
|
+
use Brick\Geo\IO\GeoJSONReader;
|
|
6
|
+
use Fleetbase\Casts\Money;
|
|
7
|
+
use Fleetbase\FleetOps\Support\Algo;
|
|
8
|
+
use Fleetbase\FleetOps\Support\Utils;
|
|
9
|
+
use Fleetbase\Models\Model;
|
|
10
|
+
use Fleetbase\Traits\HasApiModelBehavior;
|
|
11
|
+
use Fleetbase\Traits\HasPublicId;
|
|
12
|
+
use Fleetbase\Traits\HasUuid;
|
|
13
|
+
use Fleetbase\Traits\SendsWebhooks;
|
|
14
|
+
use Fleetbase\Traits\TracksApiCredential;
|
|
15
|
+
use Illuminate\Support\Facades\DB;
|
|
16
|
+
|
|
17
|
+
class ServiceRate extends Model
|
|
18
|
+
{
|
|
19
|
+
use HasUuid;
|
|
20
|
+
use HasPublicId;
|
|
21
|
+
use TracksApiCredential;
|
|
22
|
+
use SendsWebhooks;
|
|
23
|
+
use HasApiModelBehavior;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The database table used by the model.
|
|
27
|
+
*
|
|
28
|
+
* @var string
|
|
29
|
+
*/
|
|
30
|
+
protected $table = 'service_rates';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The type of public Id to generate.
|
|
34
|
+
*
|
|
35
|
+
* @var string
|
|
36
|
+
*/
|
|
37
|
+
protected $publicIdType = 'service';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* These attributes that can be queried.
|
|
41
|
+
*
|
|
42
|
+
* @var array
|
|
43
|
+
*/
|
|
44
|
+
protected $searchableColumns = [];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The attributes that are mass assignable.
|
|
48
|
+
*
|
|
49
|
+
* @var array
|
|
50
|
+
*/
|
|
51
|
+
protected $fillable = [
|
|
52
|
+
'_key',
|
|
53
|
+
'company_uuid',
|
|
54
|
+
'service_area_uuid',
|
|
55
|
+
'zone_uuid',
|
|
56
|
+
'service_name',
|
|
57
|
+
'service_type',
|
|
58
|
+
'per_meter_flat_rate_fee',
|
|
59
|
+
'per_meter_unit',
|
|
60
|
+
'base_fee',
|
|
61
|
+
'algorithm',
|
|
62
|
+
'rate_calculation_method',
|
|
63
|
+
'has_cod_fee',
|
|
64
|
+
'cod_calculation_method',
|
|
65
|
+
'cod_flat_fee',
|
|
66
|
+
'cod_percent',
|
|
67
|
+
'has_peak_hours_fee',
|
|
68
|
+
'peak_hours_calculation_method',
|
|
69
|
+
'peak_hours_flat_fee',
|
|
70
|
+
'peak_hours_percent',
|
|
71
|
+
'peak_hours_start',
|
|
72
|
+
'peak_hours_end',
|
|
73
|
+
'currency',
|
|
74
|
+
'duration_terms',
|
|
75
|
+
'estimated_days',
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The attributes that should be cast to native types.
|
|
80
|
+
*
|
|
81
|
+
* @var array
|
|
82
|
+
*/
|
|
83
|
+
protected $casts = [
|
|
84
|
+
'base_fee' => Money::class . ':currency',
|
|
85
|
+
'per_meter_flat_rate_fee' => Money::class . ':currency',
|
|
86
|
+
'cod_flat_fee' => Money::class . ':currency',
|
|
87
|
+
'peak_hours_flat_fee' => Money::class . ':currency',
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Dynamic attributes that are appended to object.
|
|
92
|
+
*
|
|
93
|
+
* @var array
|
|
94
|
+
*/
|
|
95
|
+
protected $appends = ['service_area_name', 'zone_name'];
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The attributes excluded from the model's JSON form.
|
|
99
|
+
*
|
|
100
|
+
* @var array
|
|
101
|
+
*/
|
|
102
|
+
protected $hidden = ['serviceArea', 'zone'];
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Attributes that is filterable on this model.
|
|
106
|
+
*
|
|
107
|
+
* @var array
|
|
108
|
+
*/
|
|
109
|
+
protected $filterParams = [];
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
113
|
+
*/
|
|
114
|
+
public function rateFees()
|
|
115
|
+
{
|
|
116
|
+
return $this->hasMany(ServiceRateFee::class);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
121
|
+
*/
|
|
122
|
+
public function parcelFees()
|
|
123
|
+
{
|
|
124
|
+
return $this->hasMany(ServiceRateParcelFee::class);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
129
|
+
*/
|
|
130
|
+
public function serviceArea()
|
|
131
|
+
{
|
|
132
|
+
return $this->belongsTo(ServiceArea::class)->whereNull('deleted_at');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
137
|
+
*/
|
|
138
|
+
public function zone()
|
|
139
|
+
{
|
|
140
|
+
return $this->belongsTo(Zone::class);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get the service area name attribute.
|
|
145
|
+
*/
|
|
146
|
+
public function getServiceAreaNameAttribute(): ?string
|
|
147
|
+
{
|
|
148
|
+
return data_get($this, 'serviceArea.name');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get the zone name attribute.
|
|
153
|
+
*/
|
|
154
|
+
public function getZoneNameAttribute(): ?string
|
|
155
|
+
{
|
|
156
|
+
return data_get($this, 'zone.name');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Set the number of estimated days for the service to complete.
|
|
161
|
+
*
|
|
162
|
+
* @param int $estimatedDays
|
|
163
|
+
*
|
|
164
|
+
* @return void
|
|
165
|
+
*/
|
|
166
|
+
public function setEstimatedDaysAttribute($estimatedDays = 0)
|
|
167
|
+
{
|
|
168
|
+
$this->attributes['estimated_days'] = $estimatedDays ?? 0;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Check if the rate calculation method matches the given method.
|
|
173
|
+
*
|
|
174
|
+
* @param string $method
|
|
175
|
+
*/
|
|
176
|
+
public function isRateCalculationMethod($method): bool
|
|
177
|
+
{
|
|
178
|
+
return $this->rate_calculation_method === $method;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Check if the rate calculation method is "fixed_meter".
|
|
183
|
+
*/
|
|
184
|
+
public function isFixedMeter(): bool
|
|
185
|
+
{
|
|
186
|
+
return $this->rate_calculation_method === 'fixed_meter';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Check if the rate calculation method is "per_meter".
|
|
191
|
+
*/
|
|
192
|
+
public function isPerMeter(): bool
|
|
193
|
+
{
|
|
194
|
+
return $this->rate_calculation_method === 'per_meter';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Check if the rate calculation method is "per_drop".
|
|
199
|
+
*/
|
|
200
|
+
public function isPerDrop(): bool
|
|
201
|
+
{
|
|
202
|
+
return $this->rate_calculation_method === 'per_drop';
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Check if the rate calculation method is "algo".
|
|
207
|
+
*/
|
|
208
|
+
public function isAlgorithm(): bool
|
|
209
|
+
{
|
|
210
|
+
return $this->rate_calculation_method === 'algo';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Check if the service type is "parcel".
|
|
215
|
+
*/
|
|
216
|
+
public function isParcelService(): bool
|
|
217
|
+
{
|
|
218
|
+
return $this->service_type === 'parcel';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Check if the object has a peak hours fee.
|
|
223
|
+
*/
|
|
224
|
+
public function hasPeakHoursFee(): bool
|
|
225
|
+
{
|
|
226
|
+
return (bool) $this->has_peak_hours_fee;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Check if the current time is within peak hours.
|
|
231
|
+
*/
|
|
232
|
+
public function isWithinPeakHours(): bool
|
|
233
|
+
{
|
|
234
|
+
$currentTime = strtotime(date('H:i'));
|
|
235
|
+
$startTime = strtotime($this->peak_hours_start);
|
|
236
|
+
$endTime = strtotime($this->peak_hours_end);
|
|
237
|
+
|
|
238
|
+
return $currentTime >= $startTime && $currentTime <= $endTime;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Check if the peak hours calculation method is "flat".
|
|
243
|
+
*/
|
|
244
|
+
public function hasPeakHoursFlatFee(): bool
|
|
245
|
+
{
|
|
246
|
+
return $this->peak_hours_calculation_method === 'flat';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Check if the peak hours calculation method is "percentage".
|
|
251
|
+
*/
|
|
252
|
+
public function hasPeakHoursPercentageFee(): bool
|
|
253
|
+
{
|
|
254
|
+
return $this->peak_hours_calculation_method === 'percentage';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Check if the object has a COD fee.
|
|
259
|
+
*/
|
|
260
|
+
public function hasCodFee(): bool
|
|
261
|
+
{
|
|
262
|
+
return (bool) $this->has_cod_fee;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Check if the COD calculation method is "flat".
|
|
267
|
+
*/
|
|
268
|
+
public function hasCodFlatFee(): bool
|
|
269
|
+
{
|
|
270
|
+
return $this->cod_calculation_method === 'flat';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Check if the COD calculation method is "percentage".
|
|
275
|
+
*/
|
|
276
|
+
public function hasCodPercentageFee(): bool
|
|
277
|
+
{
|
|
278
|
+
return $this->cod_calculation_method === 'percentage';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Check if the object has a related zone.
|
|
283
|
+
*/
|
|
284
|
+
public function hasZone(): bool
|
|
285
|
+
{
|
|
286
|
+
return $this->loadMissing('zone')->zone instanceof Zone;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Check if the object has a related service area.
|
|
291
|
+
*/
|
|
292
|
+
public function hasServiceArea(): bool
|
|
293
|
+
{
|
|
294
|
+
return $this->loadMissing('serviceArea')->serviceArea instanceof ServiceArea;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Set the service rate fees for the current object.
|
|
299
|
+
*
|
|
300
|
+
* @param array|null $serviceRateFees an optional array of service rate fees
|
|
301
|
+
*
|
|
302
|
+
* @return $this
|
|
303
|
+
*/
|
|
304
|
+
public function setServiceRateFees(?array $serviceRateFees = [])
|
|
305
|
+
{
|
|
306
|
+
if (!$serviceRateFees) {
|
|
307
|
+
return $this;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
$iterate = count($serviceRateFees);
|
|
311
|
+
|
|
312
|
+
for ($i = 0; $i < $iterate; $i++) {
|
|
313
|
+
// if already has uuid then we just update the record and remove from insert array
|
|
314
|
+
if (!empty($serviceRateFees[$i]['uuid'])) {
|
|
315
|
+
$id = $serviceRateFees[$i]['uuid'];
|
|
316
|
+
$updateableAttributes = collect($serviceRateFees[$i])->except(['uuid', 'created_at', 'updated_at'])->toArray();
|
|
317
|
+
|
|
318
|
+
if ($updateableAttributes) {
|
|
319
|
+
ServiceRateFee::where('uuid', $id)->update($updateableAttributes);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
unset($serviceRateFees[$i]);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
$serviceRateFees[$i]['service_rate_uuid'] = $this->uuid;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
$serviceRateFees = collect($serviceRateFees)->filter()->values()->toArray();
|
|
330
|
+
ServiceRateFee::bulkInsert($serviceRateFees);
|
|
331
|
+
|
|
332
|
+
return $this;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Set the service rate parcel fees for the current object.
|
|
337
|
+
*
|
|
338
|
+
* @param array|null $serviceRateParcelFees an optional array of service rate parcel fees
|
|
339
|
+
*
|
|
340
|
+
* @return $this
|
|
341
|
+
*/
|
|
342
|
+
public function setServiceRateParcelFees(?array $serviceRateParcelFees = [])
|
|
343
|
+
{
|
|
344
|
+
if (!$serviceRateParcelFees) {
|
|
345
|
+
return $this;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
$iterate = count($serviceRateParcelFees);
|
|
349
|
+
|
|
350
|
+
for ($i = 0; $i < $iterate; $i++) {
|
|
351
|
+
// if already has uuid then we just update the record and remove from insert array
|
|
352
|
+
if (isset($serviceRateParcelFees[$i]['uuid'])) {
|
|
353
|
+
$id = $serviceRateParcelFees[$i]['uuid'];
|
|
354
|
+
$updateableAttributes = collect($serviceRateParcelFees[$i])->except(['uuid', 'created_at', 'updated_at'])->toArray();
|
|
355
|
+
|
|
356
|
+
if ($updateableAttributes) {
|
|
357
|
+
ServiceRateParcelFee::where('uuid', $id)->update($updateableAttributes);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
unset($serviceRateParcelFees[$i]);
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
$serviceRateParcelFees[$i]['service_rate_uuid'] = $this->uuid;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
$serviceRateParcelFees = collect($serviceRateParcelFees)->filter()->values()->toArray();
|
|
368
|
+
ServiceRateParcelFee::bulkInsert($serviceRateParcelFees);
|
|
369
|
+
|
|
370
|
+
return $this;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Get the service rates applicable for the given waypoints.
|
|
375
|
+
*
|
|
376
|
+
* @param array $waypoints an array of waypoints to check against service areas and zones
|
|
377
|
+
* @param \Closure|null $queryCallback an optional closure to modify the service rates query
|
|
378
|
+
*
|
|
379
|
+
* @return array an array of applicable service rates
|
|
380
|
+
*/
|
|
381
|
+
public static function getServicableForWaypoints($waypoints = [], \Closure $queryCallback = null): array
|
|
382
|
+
{
|
|
383
|
+
$reader = new GeoJSONReader();
|
|
384
|
+
$applicableServiceRates = [];
|
|
385
|
+
$serviceRatesQuery = static::with(['zone', 'serviceArea']);
|
|
386
|
+
|
|
387
|
+
if (is_callable($queryCallback)) {
|
|
388
|
+
$queryCallback($serviceRatesQuery);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// get service rates
|
|
392
|
+
$serviceRates = $serviceRatesQuery->get();
|
|
393
|
+
|
|
394
|
+
foreach ($serviceRates as $serviceRate) {
|
|
395
|
+
if ($serviceRate->hasServiceArea()) {
|
|
396
|
+
if (Utils::exists($serviceRate, 'serviceArea.border')) {
|
|
397
|
+
// make sure all waypoints fall within the service area
|
|
398
|
+
foreach ($serviceRate->serviceArea->border as $polygon) {
|
|
399
|
+
$polygon = $reader->read($polygon->toJson());
|
|
400
|
+
|
|
401
|
+
foreach ($waypoints as $waypoint) {
|
|
402
|
+
if (!$polygon->contains($waypoint)) {
|
|
403
|
+
// waypoint outside of service area, not applicable to route
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if ($serviceRate->hasZone()) {
|
|
412
|
+
// make sure all waypoints fall within the service area
|
|
413
|
+
if (Utils::exists($serviceRate, 'zone.border')) {
|
|
414
|
+
foreach ($serviceRate->zone->border as $polygon) {
|
|
415
|
+
$polygon = $reader->read($polygon->toJson());
|
|
416
|
+
|
|
417
|
+
foreach ($waypoints as $waypoint) {
|
|
418
|
+
if (!$polygon->contains($waypoint)) {
|
|
419
|
+
// waypoint outside of zone, not applicable to route
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
$applicableServiceRates[] = $serviceRate;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return $applicableServiceRates;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Get the service rates applicable for the given places based on service type and currency.
|
|
435
|
+
*
|
|
436
|
+
* @param array $places an array of places to check against service areas and zones
|
|
437
|
+
* @param string|null $service an optional service type to filter service rates
|
|
438
|
+
* @param string|null $currency an optional currency to filter service rates
|
|
439
|
+
* @param \Closure|null $queryCallback an optional closure to modify the service rates query
|
|
440
|
+
*
|
|
441
|
+
* @return array an array of applicable service rates
|
|
442
|
+
*/
|
|
443
|
+
public static function getServicableForPlaces($places = [], $service = null, $currency = null, \Closure $queryCallback = null): array
|
|
444
|
+
{
|
|
445
|
+
$reader = new GeoJSONReader();
|
|
446
|
+
$applicableServiceRates = [];
|
|
447
|
+
$serviceRatesQuery = static::with(['zone', 'serviceArea', 'rateFees', 'parcelFees']);
|
|
448
|
+
|
|
449
|
+
if ($currency) {
|
|
450
|
+
$serviceRatesQuery->where(DB::raw('lower(currency)'), strtolower($currency));
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if ($service) {
|
|
454
|
+
$serviceRatesQuery->where('service_type', $service);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (is_callable($queryCallback)) {
|
|
458
|
+
$queryCallback($serviceRatesQuery);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
$serviceRates = $serviceRatesQuery->get();
|
|
462
|
+
|
|
463
|
+
$waypoints = collect($places)->map(function ($place) {
|
|
464
|
+
return $place->getLocationAsPoint();
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
foreach ($serviceRates as $serviceRate) {
|
|
468
|
+
if ($serviceRate->hasServiceArea()) {
|
|
469
|
+
// make sure all waypoints fall within the service area
|
|
470
|
+
foreach ($serviceRate->serviceArea->border as $polygon) {
|
|
471
|
+
$polygon = $reader->read($polygon->toJson());
|
|
472
|
+
|
|
473
|
+
foreach ($waypoints as $waypoint) {
|
|
474
|
+
if (!$polygon->contains($waypoint)) {
|
|
475
|
+
// waypoint outside of service area, not applicable to route
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if ($serviceRate->hasZone()) {
|
|
483
|
+
// make sure all waypoints fall within the service area
|
|
484
|
+
foreach ($serviceRate->zone->border as $polygon) {
|
|
485
|
+
$polygon = $reader->read($polygon->toJson());
|
|
486
|
+
|
|
487
|
+
foreach ($waypoints as $waypoint) {
|
|
488
|
+
if (!$polygon->contains($waypoint)) {
|
|
489
|
+
// waypoint outside of zone, not applicable to route
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
$applicableServiceRates[] = $serviceRate;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return $applicableServiceRates;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Generate a quote for a given pickup and dropoff point and entities.
|
|
504
|
+
*
|
|
505
|
+
* @param string $pickupPoint the coordinates of the pickup point
|
|
506
|
+
* @param string $dropoffPoint the coordinates of the dropoff point
|
|
507
|
+
* @param array $entities an array of entities to be considered for the quote
|
|
508
|
+
*
|
|
509
|
+
* @return mixed the calculated quote based on the preliminary data
|
|
510
|
+
*/
|
|
511
|
+
public function pointQuote($pickupPoint, $dropoffPoint, $entities = [])
|
|
512
|
+
{
|
|
513
|
+
$payload = new Payload();
|
|
514
|
+
$payload->entities = $entities;
|
|
515
|
+
$payload->pickup = $pickup = new Place([
|
|
516
|
+
'location' => Utils::getPointFromCoordinates($pickupPoint),
|
|
517
|
+
]);
|
|
518
|
+
$payload->dropoff = $dropoff = new Place([
|
|
519
|
+
'location' => Utils::getPointFromCoordinates($dropoffPoint),
|
|
520
|
+
]);
|
|
521
|
+
|
|
522
|
+
// calculate distance and time
|
|
523
|
+
$matrix = Utils::getDrivingDistanceAndTime($payload->pickup, $payload->dropoff);
|
|
524
|
+
|
|
525
|
+
return $this->quoteFromPreliminaryData($entities, [$pickup, $dropoff], $matrix->distance, $matrix->time);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Generate a quote based on the preliminary data provided.
|
|
530
|
+
*
|
|
531
|
+
* @param array $entities an array of entities to be considered for the quote
|
|
532
|
+
* @param array $waypoints an array of waypoints to be considered for the quote
|
|
533
|
+
* @param int|null $totalDistance the total distance for the service in meters
|
|
534
|
+
* @param int|null $totalTime the total time for the service in seconds
|
|
535
|
+
* @param bool|null $isCashOnDelivery flag indicating if the payment method is Cash on Delivery
|
|
536
|
+
*
|
|
537
|
+
* @return array an array containing the calculated quote and line items
|
|
538
|
+
*/
|
|
539
|
+
public function quoteFromPreliminaryData($entities = [], $waypoints = [], ?int $totalDistance = 0, ?int $totalTime = 0, ?bool $isCashOnDelivery = false)
|
|
540
|
+
{
|
|
541
|
+
$lines = collect();
|
|
542
|
+
$subTotal = data_get($this, 'base_fee', 0);
|
|
543
|
+
|
|
544
|
+
$lines->push([
|
|
545
|
+
'details' => 'Base Fee',
|
|
546
|
+
'raw_amount' => $subTotal,
|
|
547
|
+
'amount' => Utils::numbersOnly($subTotal),
|
|
548
|
+
'formatted_amount' => Utils::moneyFormat($subTotal, $this->currency),
|
|
549
|
+
'currency' => $this->currency,
|
|
550
|
+
'code' => 'BASE_FEE',
|
|
551
|
+
]);
|
|
552
|
+
|
|
553
|
+
if ($this->isFixedMeter()) {
|
|
554
|
+
$distanceFee = $this->findServiceRateFeeByDistance($totalDistance);
|
|
555
|
+
|
|
556
|
+
if ($distanceFee) {
|
|
557
|
+
$subTotal += Utils::numbersOnly($distanceFee->fee);
|
|
558
|
+
|
|
559
|
+
$lines->push([
|
|
560
|
+
'details' => 'Service Fee',
|
|
561
|
+
'raw_amount' => $distanceFee->fee,
|
|
562
|
+
'amount' => Utils::numbersOnly($distanceFee->fee),
|
|
563
|
+
'formatted_amount' => Utils::moneyFormat($distanceFee->fee, $this->currency),
|
|
564
|
+
'currency' => $this->currency,
|
|
565
|
+
'code' => 'BASE_FEE',
|
|
566
|
+
]);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if ($this->isPerDrop()) {
|
|
571
|
+
$rateFee = $this->findServiceRateFeeByMinMax(count($waypoints));
|
|
572
|
+
|
|
573
|
+
if ($rateFee) {
|
|
574
|
+
$subTotal += Utils::numbersOnly($rateFee->fee);
|
|
575
|
+
|
|
576
|
+
$lines->push([
|
|
577
|
+
'details' => 'Service Fee',
|
|
578
|
+
'amount' => Utils::numbersOnly($rateFee->fee),
|
|
579
|
+
'formatted_amount' => Utils::moneyFormat($rateFee->fee, $this->currency),
|
|
580
|
+
'currency' => $this->currency,
|
|
581
|
+
'code' => 'BASE_FEE',
|
|
582
|
+
]);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if ($this->isPerMeter()) {
|
|
587
|
+
$perMeterDistance = $this->per_meter_unit === 'km' ? round($totalDistance / 1000) : $totalDistance;
|
|
588
|
+
$rateFee = $perMeterDistance * $this->per_meter_flat_rate_fee;
|
|
589
|
+
$subTotal += $rateFee;
|
|
590
|
+
|
|
591
|
+
$lines->push([
|
|
592
|
+
'details' => 'Service Fee',
|
|
593
|
+
'amount' => Utils::numbersOnly($rateFee),
|
|
594
|
+
'formatted_amount' => Utils::moneyFormat($rateFee, $this->currency),
|
|
595
|
+
'currency' => $this->currency,
|
|
596
|
+
'code' => 'BASE_FEE',
|
|
597
|
+
]);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if ($this->isAlgorithm()) {
|
|
601
|
+
$rateFee = Algo::exec(
|
|
602
|
+
$this->algorithm,
|
|
603
|
+
[
|
|
604
|
+
'distance' => $totalDistance,
|
|
605
|
+
'time' => $totalTime,
|
|
606
|
+
],
|
|
607
|
+
true
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
$subTotal += Utils::numbersOnly($rateFee);
|
|
611
|
+
|
|
612
|
+
$lines->push([
|
|
613
|
+
'details' => 'Service Fee',
|
|
614
|
+
'amount' => Utils::numbersOnly($rateFee),
|
|
615
|
+
'formatted_amount' => Utils::moneyFormat($rateFee, $this->currency),
|
|
616
|
+
'currency' => $this->currency,
|
|
617
|
+
'code' => 'BASE_FEE',
|
|
618
|
+
]);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// if parcel fee's add into the base rate
|
|
622
|
+
if ($this->isParcelService()) {
|
|
623
|
+
$parcels = collect($entities)->where('type', 'parcel')->all();
|
|
624
|
+
|
|
625
|
+
foreach ($parcels as $parcel) {
|
|
626
|
+
// convert all length units to cm and weight units to grams
|
|
627
|
+
$length = $parcel->length_unit->toUnit('cm');
|
|
628
|
+
$width = $parcel->width_unit->toUnit('cm');
|
|
629
|
+
$height = $parcel->height_unit->toUnit('cm');
|
|
630
|
+
$weight = $parcel->mass_unit->toUnit('g');
|
|
631
|
+
$serviceParcelFee = null;
|
|
632
|
+
|
|
633
|
+
// iterate through parcel fees to find where it fits
|
|
634
|
+
foreach ($this->parcelFees as $parcelFee) {
|
|
635
|
+
$feeLength = $parcelFee->length_unit->toUnit('cm');
|
|
636
|
+
$feeWidth = $parcelFee->width_unit->toUnit('cm');
|
|
637
|
+
$feeHeight = $parcelFee->height_unit->toUnit('cm');
|
|
638
|
+
$feeWeight = $parcelFee->mass_unit->toUnit('g');
|
|
639
|
+
|
|
640
|
+
$previousParcelFee = $parcelFee;
|
|
641
|
+
|
|
642
|
+
if ($length > $feeLength && $width > $feeWidth && $height > $feeHeight && $weight > $feeWeight) {
|
|
643
|
+
continue;
|
|
644
|
+
} elseif ($length < $feeLength && $width < $feeWidth && $height < $feeHeight && $weight < $feeWeight) {
|
|
645
|
+
$serviceParcelFee = $previousParcelFee;
|
|
646
|
+
} else {
|
|
647
|
+
$serviceParcelFee = $parcelFee;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// if no distance fee use the last
|
|
652
|
+
if ($serviceParcelFee === null) {
|
|
653
|
+
$serviceParcelFee = $this->parcelFees->sortByDesc()->first();
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
$subTotal += $serviceParcelFee->fee;
|
|
657
|
+
|
|
658
|
+
$lines->push([
|
|
659
|
+
'details' => $serviceParcelFee->name . ' parcel fee',
|
|
660
|
+
'amount' => Utils::numbersOnly($serviceParcelFee->fee),
|
|
661
|
+
'formatted_amount' => Utils::moneyFormat($serviceParcelFee->fee, $this->currency),
|
|
662
|
+
'currency' => $this->currency,
|
|
663
|
+
'code' => 'PARCEL_FEE',
|
|
664
|
+
]);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// set the base rate
|
|
669
|
+
$baseRate = $subTotal;
|
|
670
|
+
|
|
671
|
+
// if the rate has cod add this into the quote price
|
|
672
|
+
if ($this->hasCodFee() && $isCashOnDelivery) {
|
|
673
|
+
if ($this->hasCodFlatFee()) {
|
|
674
|
+
$subTotal += $codFee = $this->cod_flat_fee;
|
|
675
|
+
} elseif ($this->hasCodPercentageFee()) {
|
|
676
|
+
$subTotal += $codFee = Utils::calculatePercentage($this->cod_percent, $baseRate);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
$lines->push([
|
|
680
|
+
'details' => 'Cash on delivery fee',
|
|
681
|
+
'amount' => Utils::numbersOnly($codFee),
|
|
682
|
+
'formatted_amount' => Utils::moneyFormat($codFee, $this->currency),
|
|
683
|
+
'currency' => $this->currency,
|
|
684
|
+
'code' => 'COD_FEE',
|
|
685
|
+
]);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// if this has peak hour fee add in
|
|
689
|
+
if ($this->hasPeakHoursFee() && $this->isWithinPeakHours()) {
|
|
690
|
+
if ($this->hasPeakHoursFlatFee()) {
|
|
691
|
+
$subTotal += $peakHoursFee = $this->peak_hours_flat_fee;
|
|
692
|
+
} elseif ($this->hasPeakHoursPercentageFee()) {
|
|
693
|
+
$subTotal += $peakHoursFee = Utils::calculatePercentage($this->peak_hours_percent, $baseRate);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
$lines->push([
|
|
697
|
+
'details' => 'Peak hours fee',
|
|
698
|
+
'amount' => Utils::numbersOnly($peakHoursFee),
|
|
699
|
+
'formatted_amount' => Utils::moneyFormat($peakHoursFee, $this->currency),
|
|
700
|
+
'currency' => $this->currency,
|
|
701
|
+
'code' => 'PEAK_HOUR_FEE',
|
|
702
|
+
]);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return [$subTotal, $lines];
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Generate a quote based on the payload provided.
|
|
710
|
+
*
|
|
711
|
+
* @param Payload $payload an instance of the Payload class containing all necessary data for the quote calculation
|
|
712
|
+
*
|
|
713
|
+
* @return array an array containing the calculated quote and line items
|
|
714
|
+
*/
|
|
715
|
+
public function quote(Payload $payload)
|
|
716
|
+
{
|
|
717
|
+
$lines = collect();
|
|
718
|
+
$subTotal = $this->base_fee ?? 0;
|
|
719
|
+
|
|
720
|
+
$lines->push([
|
|
721
|
+
'details' => 'Base Fee',
|
|
722
|
+
'amount' => Utils::numbersOnly($subTotal),
|
|
723
|
+
'formatted_amount' => Utils::moneyFormat($subTotal, $this->currency),
|
|
724
|
+
'currency' => $this->currency,
|
|
725
|
+
'code' => 'BASE_FEE',
|
|
726
|
+
]);
|
|
727
|
+
|
|
728
|
+
// Prepare all waypoints and origin and destination
|
|
729
|
+
$waypoints = $payload->getAllStops()->mapInto(Place::class);
|
|
730
|
+
$origin = $waypoints->first();
|
|
731
|
+
$destinations = $waypoints->skip(1)->toArray();
|
|
732
|
+
|
|
733
|
+
// Lookup distance matrix for total distance and time
|
|
734
|
+
$distanceMatrix = Utils::distanceMatrix([$origin], $destinations);
|
|
735
|
+
$totalDistance = $distanceMatrix->distance;
|
|
736
|
+
$totalTime = $distanceMatrix->time;
|
|
737
|
+
|
|
738
|
+
if ($this->isFixedMeter()) {
|
|
739
|
+
$distanceFee = $this->findServiceRateFeeByDistance($totalDistance);
|
|
740
|
+
|
|
741
|
+
if ($distanceFee) {
|
|
742
|
+
$subTotal += Utils::numbersOnly($distanceFee->fee);
|
|
743
|
+
|
|
744
|
+
$lines->push([
|
|
745
|
+
'details' => 'Service Fee',
|
|
746
|
+
'amount' => Utils::numbersOnly($distanceFee->fee),
|
|
747
|
+
'formatted_amount' => Utils::moneyFormat($distanceFee->fee, $this->currency),
|
|
748
|
+
'currency' => $this->currency,
|
|
749
|
+
'code' => 'BASE_FEE',
|
|
750
|
+
]);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if ($this->isPerDrop()) {
|
|
755
|
+
$rateFee = $this->findServiceRateFeeByMinMax(count($waypoints));
|
|
756
|
+
|
|
757
|
+
if ($rateFee) {
|
|
758
|
+
$subTotal += Utils::numbersOnly($rateFee->fee);
|
|
759
|
+
|
|
760
|
+
$lines->push([
|
|
761
|
+
'details' => 'Service Fee',
|
|
762
|
+
'amount' => Utils::numbersOnly($rateFee->fee),
|
|
763
|
+
'formatted_amount' => Utils::moneyFormat($rateFee->fee, $this->currency),
|
|
764
|
+
'currency' => $this->currency,
|
|
765
|
+
'code' => 'BASE_FEE',
|
|
766
|
+
]);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if ($this->isPerMeter()) {
|
|
771
|
+
$perMeterDistance = $this->per_meter_unit === 'km' ? round($totalDistance / 1000) : $totalDistance;
|
|
772
|
+
$rateFee = $perMeterDistance * $this->per_meter_flat_rate_fee;
|
|
773
|
+
$subTotal += $rateFee;
|
|
774
|
+
|
|
775
|
+
$lines->push([
|
|
776
|
+
'details' => 'Service Fee',
|
|
777
|
+
'amount' => Utils::numbersOnly($rateFee),
|
|
778
|
+
'formatted_amount' => Utils::moneyFormat($rateFee, $this->currency),
|
|
779
|
+
'currency' => $this->currency,
|
|
780
|
+
'code' => 'BASE_FEE',
|
|
781
|
+
]);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if ($this->isAlgorithm()) {
|
|
785
|
+
$rateFee = Algo::exec(
|
|
786
|
+
$this->algorithm,
|
|
787
|
+
[
|
|
788
|
+
'distance' => $totalDistance,
|
|
789
|
+
'time' => $totalTime,
|
|
790
|
+
],
|
|
791
|
+
true
|
|
792
|
+
);
|
|
793
|
+
|
|
794
|
+
$subTotal += Utils::numbersOnly($rateFee);
|
|
795
|
+
|
|
796
|
+
$lines->push([
|
|
797
|
+
'details' => 'Service Fee',
|
|
798
|
+
'amount' => Utils::numbersOnly($rateFee),
|
|
799
|
+
'formatted_amount' => Utils::moneyFormat($rateFee, $this->currency),
|
|
800
|
+
'currency' => $this->currency,
|
|
801
|
+
'code' => 'BASE_FEE',
|
|
802
|
+
]);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// if parcel fee's add into the base rate
|
|
806
|
+
if ($this->isParcelService()) {
|
|
807
|
+
$parcels = $payload->entities->where('type', 'parcel')->all();
|
|
808
|
+
|
|
809
|
+
foreach ($parcels as $parcel) {
|
|
810
|
+
// convert all length units to cm and weight units to grams
|
|
811
|
+
$length = $parcel->length_unit->toUnit('cm');
|
|
812
|
+
$width = $parcel->width_unit->toUnit('cm');
|
|
813
|
+
$height = $parcel->height_unit->toUnit('cm');
|
|
814
|
+
$weight = $parcel->mass_unit->toUnit('g');
|
|
815
|
+
$serviceParcelFee = null;
|
|
816
|
+
|
|
817
|
+
// iterate through parcel fees to find where it fits
|
|
818
|
+
foreach ($this->parcelFees as $parcelFee) {
|
|
819
|
+
$feeLength = $parcelFee->length_unit->toUnit('cm');
|
|
820
|
+
$feeWidth = $parcelFee->width_unit->toUnit('cm');
|
|
821
|
+
$feeHeight = $parcelFee->height_unit->toUnit('cm');
|
|
822
|
+
$feeWeight = $parcelFee->mass_unit->toUnit('g');
|
|
823
|
+
|
|
824
|
+
$previousParcelFee = $parcelFee;
|
|
825
|
+
|
|
826
|
+
if ($length > $feeLength && $width > $feeWidth && $height > $feeHeight && $weight > $feeWeight) {
|
|
827
|
+
continue;
|
|
828
|
+
} elseif ($length < $feeLength && $width < $feeWidth && $height < $feeHeight && $weight < $feeWeight) {
|
|
829
|
+
$serviceParcelFee = $previousParcelFee;
|
|
830
|
+
} else {
|
|
831
|
+
$serviceParcelFee = $parcelFee;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// if no distance fee use the last
|
|
836
|
+
if ($serviceParcelFee === null) {
|
|
837
|
+
$serviceParcelFee = $this->parcelFees->sortByDesc()->first();
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
$subTotal += $serviceParcelFee->fee;
|
|
841
|
+
|
|
842
|
+
$lines->push([
|
|
843
|
+
'details' => $serviceParcelFee->name . ' parcel fee',
|
|
844
|
+
'amount' => Utils::numbersOnly($serviceParcelFee->fee),
|
|
845
|
+
'formatted_amount' => Utils::moneyFormat($serviceParcelFee->fee, $this->currency),
|
|
846
|
+
'currency' => $this->currency,
|
|
847
|
+
'code' => 'PARCEL_FEE',
|
|
848
|
+
]);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// set the base rate
|
|
853
|
+
$baseRate = $subTotal;
|
|
854
|
+
|
|
855
|
+
// if the rate has cod add this into the quote price
|
|
856
|
+
if ($this->hasCodFee() && $payload->cod_amount !== null) {
|
|
857
|
+
if ($this->hasCodFlatFee()) {
|
|
858
|
+
$subTotal += $codFee = $this->cod_flat_fee;
|
|
859
|
+
} elseif ($this->hasCodPercentageFee()) {
|
|
860
|
+
$subTotal += $codFee = Utils::calculatePercentage($this->cod_percent, $baseRate);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
$lines->push([
|
|
864
|
+
'details' => 'Cash on delivery fee',
|
|
865
|
+
'amount' => Utils::numbersOnly($codFee),
|
|
866
|
+
'formatted_amount' => Utils::moneyFormat($codFee, $this->currency),
|
|
867
|
+
'currency' => $this->currency,
|
|
868
|
+
'code' => 'COD_FEE',
|
|
869
|
+
]);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// if this has peak hour fee add in
|
|
873
|
+
if ($this->hasPeakHoursFee() && $this->isWithinPeakHours()) {
|
|
874
|
+
if ($this->hasPeakHoursFlatFee()) {
|
|
875
|
+
$subTotal += $peakHoursFee = $this->peak_hours_flat_fee;
|
|
876
|
+
} elseif ($this->hasPeakHoursPercentageFee()) {
|
|
877
|
+
$subTotal += $peakHoursFee = Utils::calculatePercentage($this->peak_hours_percent, $baseRate);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
$lines->push([
|
|
881
|
+
'details' => 'Peak hours fee',
|
|
882
|
+
'amount' => Utils::numbersOnly($peakHoursFee),
|
|
883
|
+
'formatted_amount' => Utils::moneyFormat($peakHoursFee, $this->currency),
|
|
884
|
+
'currency' => $this->currency,
|
|
885
|
+
'code' => 'PEAK_HOUR_FEE',
|
|
886
|
+
]);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
return [$subTotal, $lines];
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Find the ServiceRateFee based on the total distance.
|
|
894
|
+
*
|
|
895
|
+
* @param int $totalDistance the total distance in meters
|
|
896
|
+
*
|
|
897
|
+
* @return ServiceRateFee|null the ServiceRateFee instance if found, otherwise null
|
|
898
|
+
*/
|
|
899
|
+
public function findServiceRateFeeByDistance(int $totalDistance): ?ServiceRateFee
|
|
900
|
+
{
|
|
901
|
+
$this->load('rateFees');
|
|
902
|
+
|
|
903
|
+
$distanceInKms = round($totalDistance / 1000);
|
|
904
|
+
$distanceFee = null;
|
|
905
|
+
|
|
906
|
+
foreach ($this->rateFees as $rateFee) {
|
|
907
|
+
$previousRateFee = $rateFee;
|
|
908
|
+
|
|
909
|
+
if ($distanceInKms > $rateFee->distance) {
|
|
910
|
+
continue;
|
|
911
|
+
} elseif ($rateFee->distance > $distanceInKms) {
|
|
912
|
+
$distanceFee = $previousRateFee;
|
|
913
|
+
} else {
|
|
914
|
+
$distanceFee = $rateFee;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// if no distance fee use the last
|
|
919
|
+
if ($distanceFee === null) {
|
|
920
|
+
$distanceFee = $this->rateFees->sortByDesc('distance')->first();
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
return $distanceFee;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Find the ServiceRateFee based on the given number within the min and max range.
|
|
928
|
+
*
|
|
929
|
+
* @param int $number the number to check within the ServiceRateFee's min and max range
|
|
930
|
+
*
|
|
931
|
+
* @return ServiceRateFee|null the ServiceRateFee instance if found, otherwise null
|
|
932
|
+
*/
|
|
933
|
+
public function findServiceRateFeeByMinMax(int $number): ?ServiceRateFee
|
|
934
|
+
{
|
|
935
|
+
$this->load('rateFees');
|
|
936
|
+
|
|
937
|
+
$serviceRateFee = null;
|
|
938
|
+
|
|
939
|
+
foreach ($this->rateFees as $rateFee) {
|
|
940
|
+
if ($rateFee->isWithinMinMax($number)) {
|
|
941
|
+
$serviceRateFee = $rateFee;
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// if no distance fee use the last
|
|
947
|
+
if ($serviceRateFee === null) {
|
|
948
|
+
$serviceRateFee = $this->rateFees->sortByDesc('max')->first();
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
return $serviceRateFee;
|
|
952
|
+
}
|
|
953
|
+
}
|