1212use Fleetbase \Traits \HasUuid ;
1313use Fleetbase \Traits \SendsWebhooks ;
1414use Fleetbase \Traits \TracksApiCredential ;
15- use Illuminate \Support \Facades \DB ;
1615
1716class ServiceRate extends Model
1817{
@@ -469,12 +468,11 @@ public static function getServicableForWaypoints($waypoints = [], ?\Closure $que
469468 */
470469 public static function getServicableForPlaces ($ places = [], $ service = null , $ currency = null , ?\Closure $ queryCallback = null ): array
471470 {
472- $ reader = new GeoJSONReader ();
473- $ applicableServiceRates = [];
474- $ serviceRatesQuery = static ::with (['zone ' , 'serviceArea ' , 'rateFees ' , 'parcelFees ' ]);
471+ $ reader = new GeoJSONReader ();
472+ $ serviceRatesQuery = static ::with (['zone ' , 'serviceArea ' , 'rateFees ' , 'parcelFees ' ]);
475473
476474 if ($ currency ) {
477- $ serviceRatesQuery ->where ( DB :: raw ( 'lower(currency) ' ), strtolower ($ currency ));
475+ $ serviceRatesQuery ->whereRaw ( 'lower(currency) = ? ' , [ strtolower ($ currency )] );
478476 }
479477
480478 if ($ service ) {
@@ -487,44 +485,115 @@ public static function getServicableForPlaces($places = [], $service = null, $cu
487485
488486 $ serviceRates = $ serviceRatesQuery ->get ();
489487
490- $ waypoints = collect ($ places )->map (function ($ place ) {
491- $ place = Place::createFromMixed ($ place );
488+ $ waypoints = collect ($ places )
489+ ->map (function ($ place ) {
490+ $ place = Place::createFromMixed ($ place );
491+
492+ if (!$ place instanceof Place) {
493+ return null ;
494+ }
492495
493- if ($ place instanceof Place) {
494496 $ point = $ place ->getLocationAsPoint ();
495497
496- // Conver to brick gis point
497- return \Brick \Geo \Point::fromText (sprintf ('POINT (%F %F) ' , $ point ->getLng (), $ point ->getLat ()), 4326 );
498+ // Brick point: X=lng, Y=lat (WKT order)
499+ return \Brick \Geo \Point::fromText (
500+ sprintf ('POINT (%F %F) ' , $ point ->getLng (), $ point ->getLat ()),
501+ 4326
502+ );
503+ })
504+ ->filter ()
505+ ->values ();
506+
507+ if ($ waypoints ->isEmpty ()) {
508+ return [];
509+ }
510+
511+ /**
512+ * Convert a casted spatial geometry (Zone::border / ServiceArea::border)
513+ * into a Brick geometry using GeoJSONReader.
514+ */
515+ $ toBrickGeometry = function ($ spatialGeometry ) use ($ reader ) {
516+ if (!$ spatialGeometry ) {
517+ return null ;
518+ }
519+
520+ // Most Fleetbase spatial casts/types implement toJson()
521+ if (is_object ($ spatialGeometry ) && method_exists ($ spatialGeometry , 'toJson ' )) {
522+ $ json = $ spatialGeometry ->toJson ();
523+ $ json = is_string ($ json ) ? trim ($ json ) : null ;
524+
525+ if ($ json ) {
526+ return $ reader ->read ($ json );
527+ }
528+
529+ return null ;
530+ }
531+
532+ // Fallback if the cast ever returns array/object
533+ if (is_array ($ spatialGeometry ) || is_object ($ spatialGeometry )) {
534+ $ json = json_encode ($ spatialGeometry , JSON_UNESCAPED_UNICODE );
535+ if ($ json && $ json !== 'null ' ) {
536+ return $ reader ->read ($ json );
537+ }
538+ }
539+
540+ // Fallback if it’s a raw JSON string
541+ if (is_string ($ spatialGeometry ) && trim ($ spatialGeometry ) !== '' ) {
542+ return $ reader ->read ($ spatialGeometry );
543+ }
544+
545+ return null ;
546+ };
547+
548+ /**
549+ * Ensure ALL waypoints are inside the given Brick geometry.
550+ */
551+ $ containsAllWaypoints = function ($ brickGeometry ) use ($ waypoints ): bool {
552+ if (!$ brickGeometry ) {
553+ return false ;
498554 }
499- });
555+
556+ foreach ($ waypoints as $ waypoint ) {
557+ if (!$ brickGeometry ->contains ($ waypoint )) {
558+ return false ;
559+ }
560+ }
561+
562+ return true ;
563+ };
564+
565+ $ applicableServiceRates = [];
500566
501567 foreach ($ serviceRates as $ serviceRate ) {
568+ // If a service area exists, all waypoints must be inside its border
502569 if ($ serviceRate ->hasServiceArea ()) {
503- // make sure all waypoints fall within the service area
504- foreach ( $ serviceRate -> serviceArea -> border as $ polygon ) {
505- $ polygon = $ reader -> read ( $ polygon -> toJson ()) ;
506-
507- /** @var \Brick\Geo\Point $waypoint */
508- foreach ( $ waypoints as $ waypoint ) {
509- if (! $ polygon -> contains ( $ waypoint )) {
510- // waypoint outside of service area, not applicable to route
511- continue ;
512- }
513- }
570+ $ serviceAreaBorder = $ serviceRate -> serviceArea ?->border;
571+
572+ $ serviceAreaGeom = null ;
573+ try {
574+ $ serviceAreaGeom = $ toBrickGeometry ( $ serviceAreaBorder );
575+ } catch ( \ Throwable $ e ) {
576+ continue ; // invalid geojson / geometry -> reject this rate
577+ }
578+
579+ if (! $ containsAllWaypoints ( $ serviceAreaGeom )) {
580+ continue ;
514581 }
515582 }
516583
584+ // If a zone exists, all waypoints must be inside its border
517585 if ($ serviceRate ->hasZone ()) {
518- // make sure all waypoints fall within the service area
519- foreach ($ serviceRate ->zone ->border as $ polygon ) {
520- $ polygon = $ reader ->read ($ polygon ->toJson ());
586+ $ zoneBorder = $ serviceRate ->zone ?->border;
521587
522- foreach ($ waypoints as $ waypoint ) {
523- if (!$ polygon ->contains ($ waypoint )) {
524- // waypoint outside of zone, not applicable to route
525- continue ;
526- }
527- }
588+ $ zoneGeom = null ;
589+ try {
590+ $ zoneGeom = $ toBrickGeometry ($ zoneBorder );
591+ } catch (\Throwable $ e ) {
592+ continue ;
593+ }
594+
595+ if (!$ containsAllWaypoints ($ zoneGeom )) {
596+ continue ;
528597 }
529598 }
530599
0 commit comments