Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions addon/components/order/form/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { action, get } from '@ember/object';
import { task } from 'ember-concurrency';
import { colorForId, routeColorForStatus, routeStyleForStatus } from '../../../utils/route-colors';
import { buildRoutePointMarkerPresentation, buildRoutePointsFromPayload, describeRoutePoint } from '../../../utils/route-visualization';
import preparePlaceForSave from '../../../utils/prepare-place-for-save';

const ORDER_ROUTE_PREVIEW_PADDING_BOTTOM_RIGHT = [420, 0];
const ORDER_ROUTE_PREVIEW_MAX_ZOOM_TWO_POINTS = 13;
Expand Down Expand Up @@ -153,6 +154,7 @@ export default class OrderFormRouteComponent extends Component {
@action setWaypointPlace(index, place) {
if (!this.args.resource.payload.waypoints[index]) return;

place = preparePlaceForSave(this.store, place);
this.args.resource.payload.waypoints[index].place = place;
this.args.resource.payload.waypoints[index]?.setProperties({
street1: place.street1,
Expand Down Expand Up @@ -186,6 +188,7 @@ export default class OrderFormRouteComponent extends Component {
}

@action setPayloadPlace(prop, place) {
place = preparePlaceForSave(this.store, place);
this.args.resource.payload[prop] = place;
this.previewRoute();
this.requestServiceQuoteRefresh(`route.${prop}.changed`);
Expand Down
13 changes: 13 additions & 0 deletions addon/utils/prepare-place-for-save.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default function preparePlaceForSave(store, place) {
if (!place || place.isNew || place.public_id) {
return place;
}

const attributes = {};

place.constructor.eachAttribute((attribute) => {
attributes[attribute] = place[attribute];
});

return store.createRecord('place', attributes);
}
2 changes: 1 addition & 1 deletion server/src/Models/Place.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class Place extends Model
*
* @var array
*/
protected $filterParams = ['vendor', 'contact', 'vendor_uuid', 'vendor_name'];
protected $filterParams = ['vendor', 'contact', 'vendor_uuid', 'vendor_name', 'avatar_value', 'eta'];

/**
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
Expand Down
45 changes: 45 additions & 0 deletions server/tests/Feature/Http/Internal/PlaceControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@
Request::macro('resolveFilesFromIds', fn () => FleetOpsInternalPlaceEndpointsState::$files);
}

if (!Request::hasMacro('or')) {
Request::macro('or', function (array $params = [], $default = null) {
foreach ($params as $param) {
if ($this->has($param)) {
return $this->input($param);
}
}

return $default;
});
}

class FleetOpsInternalPlaceEndpointsState
{
public static array $files = [];
Expand Down Expand Up @@ -81,6 +93,14 @@ public function syncCustomFieldValues(array $payload, array $options = []): arra
}
}

class FleetOpsInternalPlaceUpdateFake extends Place
{
public function applyDirectivesToQuery(Request $request, $builder)
{
return $builder;
}
}

class FleetOpsInternalPlaceEndpointsProbe extends PlaceController
{
public function callProtected(string $method, array $arguments = []): mixed
Expand Down Expand Up @@ -128,6 +148,7 @@ public function __call($method, $arguments)
$table->string('postal_code')->nullable();
$table->string('location')->nullable();
$table->timestamp('created_at')->nullable();
$table->timestamp('updated_at')->nullable();
$table->timestamp('deleted_at')->nullable();
});
$schema->create('files', function ($table) {
Expand Down Expand Up @@ -250,3 +271,27 @@ function fleetopsInternalPlaceEndpointsExcel(): FleetOpsInternalPlaceEndpointsEx

expect($place->syncedValues)->toBe([['priority' => 'high']]);
});

test('place updates accept serialized display-only attributes', function () {
$connection = fleetopsInternalPlaceEndpointsBoot();
$connection->table('places')->insert([
'uuid' => '77777777-7777-4777-8777-777777777777',
'public_id' => 'place_display1',
'company_uuid' => 'company-1',
'name' => 'Original place',
'street1' => '205 Dostyk Avenue',
]);

$place = (new FleetOpsInternalPlaceUpdateFake())->updateRecordFromRequest(Request::create('/int/v1/places/place_display1', 'PUT', [
'place' => [
'name' => 'Updated place',
'street2' => 'Entrance 2',
'avatar_value' => 'basic-building',
'eta' => '12 minutes',
],
]), 'place_display1', options: ['return_object' => true]);

expect($place->name)->toBe('Updated place')
->and($place->street2)->toBe('Entrance 2')
->and($connection->table('places')->where('public_id', 'place_display1')->value('name'))->toBe('Updated place');
});
54 changes: 54 additions & 0 deletions tests/unit/utils/prepare-place-for-save-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { module, test } from 'qunit';
import preparePlaceForSave from 'dummy/utils/prepare-place-for-save';

class PlaceRecordStub {
static eachAttribute(callback) {
['public_id', 'name', 'street1', 'street2', 'location', 'eta'].forEach(callback);
}

constructor(attributes) {
Object.assign(this, attributes);
}
}

module('Unit | Utility | prepare-place-for-save', function () {
test('it converts a geocoder-only record into a locally createable place', function (assert) {
const geocodedPlace = new PlaceRecordStub({
isNew: false,
public_id: null,
name: 'Delivery entrance',
street1: '205 Dostyk Avenue',
street2: 'Entrance 2',
location: { type: 'Point', coordinates: [76.9585057, 43.2343247] },
eta: null,
});
const store = {
createRecord(modelName, attributes) {
return { modelName, isNew: true, ...attributes };
},
};

const preparedPlace = preparePlaceForSave(store, geocodedPlace);

assert.notStrictEqual(preparedPlace, geocodedPlace);
assert.true(preparedPlace.isNew, 'the edit modal will save the place with POST');
assert.strictEqual(preparedPlace.modelName, 'place');
assert.strictEqual(preparedPlace.street2, 'Entrance 2');
assert.deepEqual(preparedPlace.location, geocodedPlace.location);
});

test('it preserves persisted, new, and empty place selections', function (assert) {
const store = {
createRecord() {
assert.step('createRecord');
},
};
const persistedPlace = new PlaceRecordStub({ isNew: false, public_id: 'place_existing' });
const newPlace = new PlaceRecordStub({ isNew: true, public_id: null });

assert.strictEqual(preparePlaceForSave(store, persistedPlace), persistedPlace);
assert.strictEqual(preparePlaceForSave(store, newPlace), newPlace);
assert.strictEqual(preparePlaceForSave(store, null), null);
assert.verifySteps([]);
});
});
Loading