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/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default class DriverModel extends Model {
@attr('number') heading;
@attr('string') country;
@attr('string') city;
@attr('string') status;
@attr('string', { defaultValue: 'available' }) status;
@attr('boolean') online;
@attr('raw') meta;

Expand Down
28 changes: 27 additions & 1 deletion addon/models/service-rate-fee.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import Model, { attr } from '@ember-data/model';
import Model, { attr, belongsTo } from '@ember-data/model';
import { computed } from '@ember/object';
import { format as formatDate, isValid as isValidDate, formatDistanceToNow } from 'date-fns';

export default class ServiceRateFeeModel extends Model {
/** @ids */
@attr('string') uuid;
@attr('string') service_rate_uuid;
@attr('string') service_area_uuid;
@attr('string') zone_uuid;

/** @relationships */
@belongsTo('service-area') service_area;
@belongsTo('zone') zone;

/** @attributes */
@attr('string') label;
@attr('number') priority;
@attr('boolean', { defaultValue: false }) is_fallback;
@attr('number') distance;
@attr('string') distance_unit;
@attr('string') unit;
Expand Down Expand Up @@ -64,11 +73,28 @@ export default class ServiceRateFeeModel extends Model {
return formatDate(this.created_at, 'dd, MMM');
}

@computed('is_fallback', 'zone_uuid', 'service_area_uuid', 'zone.id', 'service_area.id') get geography_type() {
if (this.is_fallback) {
return 'fallback';
}

if (this.zone_uuid || this.zone?.id) {
return 'zone';
}

return 'service_area';
}

/** @methods */
toJSON() {
return {
uuid: this.uuid,
service_rate_uuid: this.service_rate_uuid,
service_area_uuid: this.service_area_uuid,
zone_uuid: this.zone_uuid,
label: this.label,
priority: this.priority,
is_fallback: this.is_fallback,
distance: this.distance,
distance_unit: this.distance_unit,
min: this.min,
Expand Down
97 changes: 96 additions & 1 deletion addon/models/service-rate.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ export default class ServiceRate extends Model {
return this.rate_calculation_method === 'per_meter';
}

@computed('rate_calculation_method') get isMultiZoneDistance() {
return this.rate_calculation_method === 'multi_zone_distance';
}

@computed('rate_calculation_method') get isPerDrop() {
return this.rate_calculation_method === 'per_drop';
}
Expand Down Expand Up @@ -116,9 +120,59 @@ export default class ServiceRate extends Model {
return this.cod_calculation_method === 'percentage';
}

@computed('rate_fees.@each.{distance,min,max,unit}', 'max_distance', 'rate_calculation_method', 'isPerDrop') get rateFees() {
@computed(
'rate_fees.@each.{id,uuid,distance,min,max,unit,fee,label,priority,is_fallback,zone_uuid,service_area_uuid,updated_at}',
'max_distance',
'rate_calculation_method',
'isPerDrop',
'isMultiZoneDistance'
)
get rateFees() {
const existing = (this.rate_fees?.toArray?.() ?? []).filter((r) => !r.isDeleted);

if (this.isMultiZoneDistance) {
const deduped = new Map();
const rankFee = (fee) => {
if (fee.id && !fee.isNew) {
return 3;
}

if (!fee.isNew) {
return 2;
}

return 1;
};
const updatedAtMs = (fee) => {
const value = fee.updated_at instanceof Date ? fee.updated_at : new Date(fee.updated_at);
const timestamp = value.getTime();

return Number.isNaN(timestamp) ? 0 : timestamp;
};
const geographyId = (fee) => {
if (fee.is_fallback) {
return 'fallback';
}

return fee.zone_uuid || fee.zone?.id || fee.service_area_uuid || fee.service_area?.id || 'unassigned';
};

existing
.filter((r) => r.unit === 'multi_zone_distance')
.forEach((fee) => {
const key = `multi-zone:${fee.is_fallback}:${geographyId(fee)}:${fee.priority}:${fee.label}`;
const current = deduped.get(key);
const feeRank = rankFee(fee);
const currentRank = current ? rankFee(current) : 0;

if (!current || feeRank > currentRank || (feeRank === currentRank && updatedAtMs(fee) >= updatedAtMs(current))) {
deduped.set(key, fee);
}
});

return Array.from(deduped.values()).sort((a, b) => (Number(b.priority) || 0) - (Number(a.priority) || 0));
}

if (this.isPerDrop) {
const deduped = new Map();
const rankFee = (fee) => {
Expand Down Expand Up @@ -214,6 +268,8 @@ export default class ServiceRate extends Model {
});

this.rate_fees.addObject(newFee);

return newFee;
}

@action removePerDropFee(fee) {
Expand All @@ -236,4 +292,43 @@ export default class ServiceRate extends Model {
const defaultFee = this.createDefaultPerDropFee();
this.rate_fees.addObject(defaultFee);
}

@action addMultiZoneDistanceRule(attributes = {}) {
const store = getOwner(this).lookup('service:store');
const existingFees = this.rate_fees?.toArray?.() ?? [];
const nextPriority = existingFees.filter((fee) => fee.unit === 'multi_zone_distance').reduce((highest, fee) => Math.max(highest, Number(fee.priority) || 0), 0) + 10;

const newFee = store.createRecord('service-rate-fee', {
label: 'Distance rule',
priority: nextPriority,
is_fallback: false,
distance_unit: 'km',
unit: 'multi_zone_distance',
fee: 0,
currency: this.currency,
...attributes,
});

this.rate_fees.addObject(newFee);
}

@action addMultiZoneDistanceFallbackRule() {
const existingFallback = (this.rate_fees?.toArray?.() ?? []).find((fee) => fee.unit === 'multi_zone_distance' && fee.is_fallback && !fee.isDeleted);

if (existingFallback) {
return existingFallback;
}

return this.addMultiZoneDistanceRule({
label: 'Fallback distance',
priority: 0,
is_fallback: true,
});
}

@action removeMultiZoneDistanceRule(fee) {
if (!fee || !fee.destroyRecord) return;
this.rate_fees.removeObject(fee);
fee.destroyRecord();
}
}
2 changes: 1 addition & 1 deletion addon/models/vehicle.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export default class VehicleModel extends Model {

/** Misc text / meta */
@attr('string') notes;
@attr('string') status;
@attr('string', { defaultValue: 'available' }) status;
@attr('string') slug;
@attr('boolean') online;
@attr('raw') vin_data;
Expand Down
9 changes: 8 additions & 1 deletion addon/serializers/service-rate-fee.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import ApplicationSerializer from '@fleetbase/ember-core/serializers/application';
import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest';

export default class ServiceRateFeeSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {}
export default class ServiceRateFeeSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {
get attrs() {
return {
service_area: { embedded: 'always', serialize: false },
zone: { embedded: 'always', serialize: false },
};
}
}
7 changes: 6 additions & 1 deletion addon/serializers/service-rate.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,19 @@ export default class ServiceRateSerializer extends ApplicationSerializer.extend(
return `drop:${fee.min}:${fee.max}:${fee.unit}`;
}

if (fee.unit === 'multi_zone_distance') {
return `multi-zone:${fee.service_area_uuid}:${fee.zone_uuid}:${fee.is_fallback}:${fee.priority}:${fee.label}`;
}

return `distance:${fee.distance}`;
};

const savedByKey = new Map(savedRateFees.map((f) => [savedFeeKey(f), f]));
const hasSavedMultiZoneFees = savedRateFees.some((fee) => fee.unit === 'multi_zone_distance');

// Only remove unsaved fees that duplicate saved fees
unsavedRateFees.forEach((fee) => {
if (savedByKey.has(savedFeeKey(fee))) {
if ((hasSavedMultiZoneFees && fee.unit === 'multi_zone_distance') || savedByKey.has(savedFeeKey(fee))) {
serviceRate.get('rate_fees').removeObject(fee);
fee.unloadRecord();
}
Expand Down
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.33",
"version": "0.1.34",
"description": "Fleetbase Fleet-Ops based models, serializers, transforms, adapters and GeoJson utility functions.",
"keywords": [
"fleetbase-data",
Expand Down Expand Up @@ -37,7 +37,7 @@
},
"dependencies": {
"@babel/core": "^7.23.2",
"@fleetbase/ember-core": "^0.3.18",
"@fleetbase/ember-core": "^0.3.19",
"date-fns": "^2.29.3",
"ember-cli-babel": "^8.2.0",
"ember-cli-htmlbars": "^6.3.0"
Expand Down
17 changes: 7 additions & 10 deletions pnpm-lock.yaml

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

Loading
Loading