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
2 changes: 1 addition & 1 deletion addon/models/service-rate-fee.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default class ServiceRateFeeModel extends Model {
@attr('string') service_rate_uuid;

/** @attributes */
@attr('string') distance;
@attr('number') distance;
@attr('string') distance_unit;
@attr('string') unit;
@attr('string') fee;
Expand Down
86 changes: 32 additions & 54 deletions addon/models/service-rate.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Model, { attr, hasMany, belongsTo } from '@ember-data/model';
import { tracked } from '@glimmer/tracking';
import { computed, action } from '@ember/object';
import { getOwner } from '@ember/application';
import { format as formatDate, formatDistanceToNow } from 'date-fns';
Expand All @@ -20,9 +19,6 @@ export default class ServiceRate extends Model {
@belongsTo('zone') zone;
@hasMany('custom-field-value', { async: false }) custom_field_values;

/** @tracked */
@tracked perDropFees = [];

/** @attributes */
@attr('string') service_area_name;
@attr('string') zone_name;
Expand Down Expand Up @@ -120,46 +116,15 @@ export default class ServiceRate extends Model {
return this.cod_calculation_method === 'percentage';
}

@computed('max_distance', 'max_distance_unit', 'currency', 'rate_fees') get rateFees() {
const store = getOwner(this).lookup('service:store');
const unit = this.max_distance_unit;
const currency = this.currency;
@computed('rate_fees.@each.distance', 'max_distance') get rateFees() {
const n = Math.max(0, Number(this.max_distance) || 0);
const existing = (this.rate_fees?.toArray?.() ?? []).slice();
const byDistance = new Map(existing.map((r) => [r.distance, r]));

const rows = [];
for (let d = 0; d < n; d++) {
let rec = byDistance.get(d);
if (!rec) {
rec = store.createRecord('service-rate-fee', {
distance: d,
distance_unit: unit,
fee: 0,
currency,
});
this.rate_fees.addObject(rec);
} else {
rec.setProperties({ distance_unit: unit, currency });
}
rows.push(rec);
}
const existing = (this.rate_fees?.toArray?.() ?? []).filter((r) => r.distance !== null && r.distance !== undefined && !r.isDeleted);

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

/** @methods */
@action syncServiceRateFees() {
if (!this.isFixedRate) return;
this.set('rate_fees', this.rateFees);
}

@action syncPerDropFees() {
if (!this.isPerDrop) return;
this.set('rate_fees', this.perDropFees);
}

@action createDefaultPerDropFee(attributes = {}) {
const store = getOwner(this).lookup('service:store');
return store.createRecord('service-rate-fee', {
Expand All @@ -173,28 +138,41 @@ export default class ServiceRate extends Model {
}

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

this.perDropFees = [
...this.perDropFees,
{
min: min,
max: max,
unit: 'waypoint',
fee: 0,
currency: this.currency,
},
];
const newFee = store.createRecord('service-rate-fee', {
min: min,
max: max,
unit: 'waypoint',
fee: 0,
currency: this.currency,
});

this.rate_fees.addObject(newFee);
}

@action removePerDropFee(index) {
if (index === 0) return;
this.perDropFees = [...this.perDropFees.filter((_, i) => i !== index)];
@action removePerDropFee(fee) {
if (!fee || !fee.destroyRecord) return;
this.rate_fees.removeObject(fee);
fee.destroyRecord();
}

@action resetPerDropFees() {
this.perDropFees = [this.createDefaultPerDropFee()];
// Remove all existing per-drop fees
const existingFees = this.rate_fees?.toArray?.() ?? [];
existingFees.forEach((fee) => {
if (fee.unit === 'waypoint') {
this.rate_fees.removeObject(fee);
fee.destroyRecord();
}
});

// Add a new default per-drop fee
const defaultFee = this.createDefaultPerDropFee();
this.rate_fees.addObject(defaultFee);
}
}
2 changes: 1 addition & 1 deletion addon/models/vehicle.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,4 @@ export default class VehicleModel extends Model {

return devices;
}
}
}
61 changes: 61 additions & 0 deletions addon/serializers/service-rate.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ApplicationSerializer from '@fleetbase/ember-core/serializers/application';
import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest';
import { next } from '@ember/runloop';

export default class ServiceRateSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {
/**
Expand All @@ -16,4 +17,64 @@ export default class ServiceRateSerializer extends ApplicationSerializer.extend(
rate_fees: { embedded: 'always' },
};
}

/**
* Normalize single response - called after save/create
* Directly set rate_fees relationship to only saved records from backend
*/
normalizeSaveResponse(store, primaryModelClass, payload, id, requestType) {
const normalized = super.normalizeSaveResponse(store, primaryModelClass, payload, id, requestType);

// After normalization, replace rate_fees with only the saved records from backend
if (normalized.data && normalized.data.type === 'service-rate') {
const serviceRateId = normalized.data.id;

// Schedule after store update using Ember run loop
next(() => {
const serviceRate = store.peekRecord('service-rate', serviceRateId);
if (serviceRate) {
// Cleanup rate_fees duplicates (for Fixed Rate and Per-Drop)
const allRateFees = serviceRate.get('rate_fees').toArray();
const savedRateFees = allRateFees.filter((f) => !f.isNew);
const unsavedRateFees = allRateFees.filter((f) => f.isNew);

// Create a map of saved fees by distance
const savedByDistance = new Map(savedRateFees.map((f) => [f.distance, f]));

// Only remove unsaved fees that duplicate saved fees
unsavedRateFees.forEach((fee) => {
if (savedByDistance.has(fee.distance)) {
serviceRate.get('rate_fees').removeObject(fee);
fee.unloadRecord();
}
});

// Cleanup parcel_fees duplicates
const allParcelFees = serviceRate.get('parcel_fees').toArray();
const savedParcelFees = allParcelFees.filter((f) => !f.isNew);
const unsavedParcelFees = allParcelFees.filter((f) => f.isNew);

// // Create a map of saved parcel fees by uuid (or another unique key)
// const savedParcelByUuid = new Map(savedParcelFees.map((f) => [f.get('id'), f]));

// Remove unsaved parcel fees that have a saved version
unsavedParcelFees.forEach((fee) => {
// For parcel fees, we need to check if there's a duplicate based on attributes
// Since they don't have a simple key like distance, we'll just remove all unsaved ones
// that don't have unique identifying attributes
const hasDuplicate = savedParcelFees.some(
(saved) => saved.size === fee.size && saved.length === fee.length && saved.width === fee.width && saved.height === fee.height
);

if (hasDuplicate) {
serviceRate.get('parcel_fees').removeObject(fee);
fee.unloadRecord();
}
});
}
});
}

return normalized;
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fleetbase/fleetops-data",
"version": "0.1.23",
"version": "0.1.24",
"description": "Fleetbase Fleet-Ops based models, serializers, transforms, adapters and GeoJson utility functions.",
"keywords": [
"fleetbase-data",
Expand Down Expand Up @@ -36,7 +36,7 @@
"publish:github": "npm config set '@fleetbase:registry' https://npm.pkg.github.com/ && npm publish"
},
"dependencies": {
"@fleetbase/ember-core": "latest",
"@fleetbase/ember-core": "^0.3.9",
"@babel/core": "^7.23.2",
"date-fns": "^2.29.3",
"ember-cli-babel": "^8.2.0",
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading