Skip to content

Commit 621a876

Browse files
authored
Merge pull request #43 from fleetbase/fix/service-rate-fee-persistence
Fix service rate fee persistence by using proper Ember Data records
2 parents f20408d + 65f86e9 commit 621a876

File tree

6 files changed

+102
-63
lines changed

6 files changed

+102
-63
lines changed

addon/models/service-rate-fee.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default class ServiceRateFeeModel extends Model {
77
@attr('string') service_rate_uuid;
88

99
/** @attributes */
10-
@attr('string') distance;
10+
@attr('number') distance;
1111
@attr('string') distance_unit;
1212
@attr('string') unit;
1313
@attr('string') fee;

addon/models/service-rate.js

Lines changed: 32 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import Model, { attr, hasMany, belongsTo } from '@ember-data/model';
2-
import { tracked } from '@glimmer/tracking';
32
import { computed, action } from '@ember/object';
43
import { getOwner } from '@ember/application';
54
import { format as formatDate, formatDistanceToNow } from 'date-fns';
@@ -20,9 +19,6 @@ export default class ServiceRate extends Model {
2019
@belongsTo('zone') zone;
2120
@hasMany('custom-field-value', { async: false }) custom_field_values;
2221

23-
/** @tracked */
24-
@tracked perDropFees = [];
25-
2622
/** @attributes */
2723
@attr('string') service_area_name;
2824
@attr('string') zone_name;
@@ -120,46 +116,15 @@ export default class ServiceRate extends Model {
120116
return this.cod_calculation_method === 'percentage';
121117
}
122118

123-
@computed('max_distance', 'max_distance_unit', 'currency', 'rate_fees') get rateFees() {
124-
const store = getOwner(this).lookup('service:store');
125-
const unit = this.max_distance_unit;
126-
const currency = this.currency;
119+
@computed('rate_fees.@each.distance', 'max_distance') get rateFees() {
127120
const n = Math.max(0, Number(this.max_distance) || 0);
128-
const existing = (this.rate_fees?.toArray?.() ?? []).slice();
129-
const byDistance = new Map(existing.map((r) => [r.distance, r]));
130-
131-
const rows = [];
132-
for (let d = 0; d < n; d++) {
133-
let rec = byDistance.get(d);
134-
if (!rec) {
135-
rec = store.createRecord('service-rate-fee', {
136-
distance: d,
137-
distance_unit: unit,
138-
fee: 0,
139-
currency,
140-
});
141-
this.rate_fees.addObject(rec);
142-
} else {
143-
rec.setProperties({ distance_unit: unit, currency });
144-
}
145-
rows.push(rec);
146-
}
121+
const existing = (this.rate_fees?.toArray?.() ?? []).filter((r) => r.distance !== null && r.distance !== undefined && !r.isDeleted);
147122

148-
// note: do NOT prune here in a getter; do it via an explicit action
149-
return rows;
123+
// Return existing fees sorted by distance, filtered by max_distance
124+
return existing.filter((r) => r.distance >= 0 && r.distance < n).sort((a, b) => a.distance - b.distance);
150125
}
151126

152127
/** @methods */
153-
@action syncServiceRateFees() {
154-
if (!this.isFixedRate) return;
155-
this.set('rate_fees', this.rateFees);
156-
}
157-
158-
@action syncPerDropFees() {
159-
if (!this.isPerDrop) return;
160-
this.set('rate_fees', this.perDropFees);
161-
}
162-
163128
@action createDefaultPerDropFee(attributes = {}) {
164129
const store = getOwner(this).lookup('service:store');
165130
return store.createRecord('service-rate-fee', {
@@ -173,28 +138,41 @@ export default class ServiceRate extends Model {
173138
}
174139

175140
@action addPerDropRateFee() {
176-
const last = this.perDropFees[this.perDropFees.length - 1];
141+
const store = getOwner(this).lookup('service:store');
142+
const existingFees = this.rate_fees?.toArray?.() ?? [];
143+
const last = existingFees[existingFees.length - 1];
177144
const min = last ? last.max + 1 : 1;
178145
const max = min + 5;
179146

180-
this.perDropFees = [
181-
...this.perDropFees,
182-
{
183-
min: min,
184-
max: max,
185-
unit: 'waypoint',
186-
fee: 0,
187-
currency: this.currency,
188-
},
189-
];
147+
const newFee = store.createRecord('service-rate-fee', {
148+
min: min,
149+
max: max,
150+
unit: 'waypoint',
151+
fee: 0,
152+
currency: this.currency,
153+
});
154+
155+
this.rate_fees.addObject(newFee);
190156
}
191157

192-
@action removePerDropFee(index) {
193-
if (index === 0) return;
194-
this.perDropFees = [...this.perDropFees.filter((_, i) => i !== index)];
158+
@action removePerDropFee(fee) {
159+
if (!fee || !fee.destroyRecord) return;
160+
this.rate_fees.removeObject(fee);
161+
fee.destroyRecord();
195162
}
196163

197164
@action resetPerDropFees() {
198-
this.perDropFees = [this.createDefaultPerDropFee()];
165+
// Remove all existing per-drop fees
166+
const existingFees = this.rate_fees?.toArray?.() ?? [];
167+
existingFees.forEach((fee) => {
168+
if (fee.unit === 'waypoint') {
169+
this.rate_fees.removeObject(fee);
170+
fee.destroyRecord();
171+
}
172+
});
173+
174+
// Add a new default per-drop fee
175+
const defaultFee = this.createDefaultPerDropFee();
176+
this.rate_fees.addObject(defaultFee);
199177
}
200178
}

addon/models/vehicle.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,4 +272,4 @@ export default class VehicleModel extends Model {
272272

273273
return devices;
274274
}
275-
}
275+
}

addon/serializers/service-rate.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ApplicationSerializer from '@fleetbase/ember-core/serializers/application';
22
import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest';
3+
import { next } from '@ember/runloop';
34

45
export default class ServiceRateSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {
56
/**
@@ -16,4 +17,64 @@ export default class ServiceRateSerializer extends ApplicationSerializer.extend(
1617
rate_fees: { embedded: 'always' },
1718
};
1819
}
20+
21+
/**
22+
* Normalize single response - called after save/create
23+
* Directly set rate_fees relationship to only saved records from backend
24+
*/
25+
normalizeSaveResponse(store, primaryModelClass, payload, id, requestType) {
26+
const normalized = super.normalizeSaveResponse(store, primaryModelClass, payload, id, requestType);
27+
28+
// After normalization, replace rate_fees with only the saved records from backend
29+
if (normalized.data && normalized.data.type === 'service-rate') {
30+
const serviceRateId = normalized.data.id;
31+
32+
// Schedule after store update using Ember run loop
33+
next(() => {
34+
const serviceRate = store.peekRecord('service-rate', serviceRateId);
35+
if (serviceRate) {
36+
// Cleanup rate_fees duplicates (for Fixed Rate and Per-Drop)
37+
const allRateFees = serviceRate.get('rate_fees').toArray();
38+
const savedRateFees = allRateFees.filter((f) => !f.isNew);
39+
const unsavedRateFees = allRateFees.filter((f) => f.isNew);
40+
41+
// Create a map of saved fees by distance
42+
const savedByDistance = new Map(savedRateFees.map((f) => [f.distance, f]));
43+
44+
// Only remove unsaved fees that duplicate saved fees
45+
unsavedRateFees.forEach((fee) => {
46+
if (savedByDistance.has(fee.distance)) {
47+
serviceRate.get('rate_fees').removeObject(fee);
48+
fee.unloadRecord();
49+
}
50+
});
51+
52+
// Cleanup parcel_fees duplicates
53+
const allParcelFees = serviceRate.get('parcel_fees').toArray();
54+
const savedParcelFees = allParcelFees.filter((f) => !f.isNew);
55+
const unsavedParcelFees = allParcelFees.filter((f) => f.isNew);
56+
57+
// // Create a map of saved parcel fees by uuid (or another unique key)
58+
// const savedParcelByUuid = new Map(savedParcelFees.map((f) => [f.get('id'), f]));
59+
60+
// Remove unsaved parcel fees that have a saved version
61+
unsavedParcelFees.forEach((fee) => {
62+
// For parcel fees, we need to check if there's a duplicate based on attributes
63+
// Since they don't have a simple key like distance, we'll just remove all unsaved ones
64+
// that don't have unique identifying attributes
65+
const hasDuplicate = savedParcelFees.some(
66+
(saved) => saved.size === fee.size && saved.length === fee.length && saved.width === fee.width && saved.height === fee.height
67+
);
68+
69+
if (hasDuplicate) {
70+
serviceRate.get('parcel_fees').removeObject(fee);
71+
fee.unloadRecord();
72+
}
73+
});
74+
}
75+
});
76+
}
77+
78+
return normalized;
79+
}
1980
}

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@fleetbase/fleetops-data",
3-
"version": "0.1.23",
3+
"version": "0.1.24",
44
"description": "Fleetbase Fleet-Ops based models, serializers, transforms, adapters and GeoJson utility functions.",
55
"keywords": [
66
"fleetbase-data",
@@ -36,7 +36,7 @@
3636
"publish:github": "npm config set '@fleetbase:registry' https://npm.pkg.github.com/ && npm publish"
3737
},
3838
"dependencies": {
39-
"@fleetbase/ember-core": "latest",
39+
"@fleetbase/ember-core": "^0.3.9",
4040
"@babel/core": "^7.23.2",
4141
"date-fns": "^2.29.3",
4242
"ember-cli-babel": "^8.2.0",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)