- **Backend**: Introduced `EUTaxationApiService` for TARIC data retrieval and integrated tariff rate lookup functionality. Added supporting DTOs and updated services to handle custom measures and updated logic for material and supplier changes. - **Frontend**: Enhanced mass and single edit calculation components to include tariff rate lookup functionality and warnings. Introduced `useCustomsStore` for state management of customs data. - **Database**: Added `nomenclature` table for storing HS code-related information. - **Other**: Configured SOAP client for TARIC API and added logging for debugging.
911 lines
No EOL
35 KiB
JavaScript
911 lines
No EOL
35 KiB
JavaScript
import {defineStore} from 'pinia'
|
|
import {config} from '@/config'
|
|
import {toRaw} from "vue";
|
|
import {useErrorStore} from "@/store/error.js";
|
|
import logger from "@/logger.js"
|
|
import performRequest from '@/backend.js'
|
|
|
|
export const usePremiseEditStore = defineStore('premiseEdit', {
|
|
state() {
|
|
return {
|
|
premisses: null,
|
|
|
|
/**
|
|
* set to true while the store is loading the premises.
|
|
*/
|
|
loading: false,
|
|
|
|
/**
|
|
* set to true while the store sets the selected/deselected field in the premises.
|
|
*/
|
|
selectedLoading: false,
|
|
|
|
|
|
destinations: null,
|
|
processDestinationMassEdit: false,
|
|
|
|
selectedDestination: null,
|
|
|
|
throwsException: true,
|
|
|
|
}
|
|
},
|
|
getters: {
|
|
|
|
getCountryIdByPremiseIds(state) {
|
|
return function (ids) {
|
|
if (state.loading) {
|
|
if (state.throwsException)
|
|
throw new Error("Premises are accessed while still loading.");
|
|
return null;
|
|
}
|
|
|
|
const premiss = state.premisses?.filter(p => ids.some(id => id === p.id));
|
|
|
|
const premiseCountryMap = new Map();
|
|
premiss?.forEach(premise => {
|
|
premiseCountryMap.set(premise.id, premise.supplier?.country?.id);
|
|
});
|
|
|
|
return premiseCountryMap;
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
/**
|
|
* Returns the ids of all premises.
|
|
* @param state
|
|
* @returns {*}
|
|
*/
|
|
getPremiseIds(state) {
|
|
if (state.loading) {
|
|
if (state.throwsException)
|
|
throw new Error("Premises are accessed while still loading.");
|
|
|
|
return null;
|
|
}
|
|
|
|
return state.premisses?.map(p => p.id);
|
|
},
|
|
|
|
/**
|
|
* Returns the premises.
|
|
* @param state
|
|
* @returns {null|*[]|*}
|
|
*/
|
|
getPremisses(state) {
|
|
if (state.loading) {
|
|
if (state.throwsException)
|
|
throw new Error("Premises are accessed while still loading.");
|
|
|
|
return null;
|
|
}
|
|
|
|
return state.premisses;
|
|
},
|
|
|
|
/**
|
|
* Returns the premise with the given id.
|
|
* @param state
|
|
* @returns {function(*): *}
|
|
*/
|
|
getById(state) {
|
|
return function (id) {
|
|
if (state.loading) {
|
|
if (state.throwsException)
|
|
throw new Error("Premises are accessed while still loading.");
|
|
|
|
return null;
|
|
}
|
|
|
|
if ((id ?? null) === null) {
|
|
return null;
|
|
}
|
|
|
|
return state.premisses.find(p => p.id === id);
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Returns all premises that are selected.
|
|
* @param state
|
|
* @returns {T[]}
|
|
*/
|
|
getSelectedPremisses(state) {
|
|
if (state.loading || state.selectedLoading) {
|
|
if (state.throwsException)
|
|
throw new Error("Premises are accessed while still loading.");
|
|
|
|
return null;
|
|
}
|
|
|
|
return state.premisses.filter(p => p.selected);
|
|
},
|
|
|
|
|
|
/**
|
|
* Returns all premise ids that are selected.
|
|
* @param state
|
|
* @returns {T[]}
|
|
*/
|
|
getSelectedPremissesIds(state) {
|
|
if (state.loading || state.selectedLoading) {
|
|
if (state.throwsException)
|
|
throw new Error("Premises are accessed while still loading.");
|
|
|
|
return null;
|
|
}
|
|
|
|
return state.premisses.filter(p => p.selected).map(p => p.id);
|
|
},
|
|
|
|
/**
|
|
* Getters for controlling frontend views
|
|
* ======================================
|
|
*/
|
|
|
|
/**
|
|
* Returns true if no premises are loaded. The frontend can show a message that no premises are found.
|
|
* @param state
|
|
* @returns {*|boolean}
|
|
*/
|
|
showEmpty(state) {
|
|
return (!state.loading && (((state.premisses ?? null) === null) || state.premisses.length === 0))
|
|
},
|
|
|
|
/**
|
|
* Returns true if the premises are loaded and not empty. The frontend can show a the loaded premisses.
|
|
* @param state
|
|
* @returns {boolean}
|
|
*/
|
|
showData(state) {
|
|
return (!state.loading && !((state.premisses ?? null) === null) && state.premisses.length !== 0)
|
|
},
|
|
|
|
/**
|
|
* Returns true if the premises are loaded and not empty. The frontend can show a the loaded premisses.
|
|
* @param state
|
|
* @returns {boolean}
|
|
*/
|
|
isLoading(state) {
|
|
return (state.loading);
|
|
},
|
|
|
|
showProcessingModal(state) {
|
|
return state.processDestinationMassEdit;
|
|
},
|
|
|
|
/**
|
|
* Getters for single edit view
|
|
* ============================
|
|
*/
|
|
|
|
/**
|
|
* Returns true if only one premise is selected.
|
|
* @param state
|
|
* @returns {boolean|null}
|
|
*/
|
|
isSingleSelect(state) {
|
|
if (state.loading || state.selectedLoading) {
|
|
if (state.throwsException)
|
|
throw new Error("Premises are accessed while still loading.");
|
|
|
|
return null;
|
|
}
|
|
|
|
return state.premisses.filter(p => p.selected).length === 1;
|
|
},
|
|
|
|
/**
|
|
* Returns the id of the single selected premise.
|
|
* @param state
|
|
* @returns {*}
|
|
*/
|
|
singleSelectId(state) {
|
|
if (state.loading || state.selectedLoading) {
|
|
if (state.throwsException)
|
|
throw new Error("Premises are accessed while still loading.");
|
|
|
|
return null;
|
|
}
|
|
|
|
if (!state.isSingleSelect) {
|
|
return null;
|
|
// throw new Error("Single selected premise accessed, but not in single select mode");
|
|
}
|
|
|
|
|
|
return state.premisses.find(p => p.selected)?.id;
|
|
},
|
|
|
|
|
|
/**
|
|
* Returns the single selected premise.
|
|
* @param state
|
|
* @returns {*}
|
|
*/
|
|
singleSelectedPremise(state) {
|
|
if (state.loading || state.selectedLoading) {
|
|
if (state.throwsException)
|
|
throw new Error("Premises are accessed while still loading.");
|
|
|
|
return null;
|
|
}
|
|
|
|
if (!state.isSingleSelect) {
|
|
return null;
|
|
// throw new Error("Single selected premise accessed, but not in single select mode");
|
|
}
|
|
|
|
return state.premisses?.find(p => p.selected);
|
|
},
|
|
|
|
/**
|
|
* Getters for destination editing
|
|
* ===============================
|
|
*/
|
|
|
|
getDestinationsView(state) {
|
|
return state.destinations?.destinations ?? [];
|
|
},
|
|
|
|
getDestinationById(state) {
|
|
return function (id) {
|
|
if (state.loading || state.selectedLoading) {
|
|
if (state.throwsException)
|
|
throw new Error("Premises are accessed while still loading.");
|
|
|
|
return null;
|
|
}
|
|
|
|
for (const p of state.premisses) {
|
|
const d = p.destinations.find(d => d.id === id);
|
|
if ((d ?? null) !== null) return d;
|
|
}
|
|
}
|
|
},
|
|
|
|
getSelectedDestinationsData: (state) => state.selectedDestination,
|
|
|
|
|
|
},
|
|
actions: {
|
|
async batchUpdatePrice(ids, priceData) {
|
|
const updatedPremises = this.premisses.map(p => {
|
|
if (ids.includes(p.id)) {
|
|
return {
|
|
...p,
|
|
...(priceData.price !== null && {material_cost: priceData.price}),
|
|
...(priceData.overSeaShare !== null && {oversea_share: priceData.overSeaShare}),
|
|
...(priceData.includeFcaFee !== null && {is_fca_enabled: priceData.includeFcaFee})
|
|
};
|
|
}
|
|
return p;
|
|
});
|
|
this.premisses = updatedPremises;
|
|
|
|
return await this.savePrice(ids, priceData);
|
|
},
|
|
async batchUpdateMaterial(ids, materialData, tariffRates = null) {
|
|
const updatedPremises = this.premisses.map(p => {
|
|
if (ids.includes(p.id)) {
|
|
return {
|
|
...p,
|
|
material: {
|
|
...p.material,
|
|
},
|
|
...(materialData.hsCode !== null && {hs_code: materialData.hsCode}),
|
|
...((tariffRates !== null) ? tariffRates.get(p.id) : (materialData.tariffRate !== null && {tariff_rate: materialData.tariffRate}))
|
|
};
|
|
}
|
|
return p;
|
|
});
|
|
this.premisses = updatedPremises;
|
|
|
|
|
|
return await this.saveMaterial(ids, materialData, tariffRates);
|
|
},
|
|
async batchUpdatePackaging(ids, packagingData) {
|
|
|
|
|
|
const updatedPremises = this.premisses.map(p => {
|
|
if (ids.includes(p.id)) {
|
|
return {
|
|
...p,
|
|
handling_unit: {
|
|
...p.handling_unit,
|
|
...(packagingData.weight !== null && {weight: packagingData.weight}),
|
|
...(packagingData.width !== null && {width: packagingData.width}),
|
|
...(packagingData.length !== null && {length: packagingData.length}),
|
|
...(packagingData.height !== null && {height: packagingData.height}),
|
|
...(packagingData.weightUnit !== null && {weight_unit: packagingData.weightUnit}),
|
|
...(packagingData.dimensionUnit !== null && {dimension_unit: packagingData.dimensionUnit}),
|
|
...(packagingData.unitCount !== null && {content_unit_count: packagingData.unitCount})
|
|
},
|
|
...(packagingData.stackable !== null && {is_stackable: packagingData.stackable}),
|
|
...(packagingData.mixable !== null && {is_mixable: packagingData.mixable})
|
|
};
|
|
}
|
|
return p;
|
|
});
|
|
this.premisses = updatedPremises;
|
|
|
|
logger.info("packaging data:", toRaw(packagingData), "update result", toRaw(updatedPremises));
|
|
|
|
return await this.savePackaging(ids, packagingData);
|
|
},
|
|
async startCalculation() {
|
|
|
|
const body = this.premisses.map(p => p.id);
|
|
const url = `${config.backendUrl}/calculation/start/`;
|
|
let error = null;
|
|
|
|
await performRequest(this, 'PUT', url, body, false, ['Premiss validation error', 'Internal Server Error']).catch(e => {
|
|
logger.log("startCalculation exception", e.errorObj);
|
|
error = e.errorObj;
|
|
})
|
|
|
|
return error;
|
|
},
|
|
|
|
/**
|
|
* DESTINATION stuff
|
|
* =================
|
|
*/
|
|
|
|
prepareDestinations(dataSourcePremiseId, editedPremiseIds, massEdit = false, fromMassEditView = false) {
|
|
if (this.premisses === null) return;
|
|
if (!editedPremiseIds || !dataSourcePremiseId || editedPremiseIds.length === 0) return;
|
|
|
|
this.destinations = {
|
|
premise_ids: editedPremiseIds,
|
|
massEdit: massEdit,
|
|
fromMassEditView: fromMassEditView,
|
|
destinations: this.premisses.find(p => String(p.id) === String(dataSourcePremiseId))?.destinations.map(d => this.copyAllFromPremises(d, !massEdit)) ?? [],
|
|
};
|
|
|
|
this.selectedDestination = null;
|
|
|
|
},
|
|
async executeDestinationsMassEdit() {
|
|
|
|
if (!this.destinations.massEdit) {
|
|
|
|
this.destinations.premise_ids.forEach(premiseId => {
|
|
const toPremise = this.getById(premiseId);
|
|
|
|
this.destinations.destinations.forEach(fromDest => {
|
|
const toDest = toPremise.destinations.find(to => fromDest.id.substring(1) === String(to.id));
|
|
|
|
if ((toDest ?? null) === null) {
|
|
throw new Error("Destination not found in premise: " + premiseId + " -> " + d.id);
|
|
}
|
|
|
|
this.copyAllToPremise(fromDest, toDest);
|
|
|
|
const body = {
|
|
annual_amount: toDest.annual_amount,
|
|
repackaging_costs: toDest.repackaging_costs,
|
|
handling_costs: toDest.handling_costs,
|
|
disposal_costs: toDest.disposal_costs,
|
|
is_d2d: toDest.is_d2d,
|
|
rate_d2d: toDest.rate_d2d,
|
|
lead_time_d2d: toDest.lead_time_d2d,
|
|
route_selected_id: toDest.routes.find(r => r.is_selected)?.id ?? null,
|
|
};
|
|
|
|
const url = `${config.backendUrl}/calculation/destination/${toDest.id}`;
|
|
performRequest(this, 'PUT', url, body, false);
|
|
|
|
});
|
|
});
|
|
|
|
|
|
} else {
|
|
this.processDestinationMassEdit = true;
|
|
|
|
const destinations = [];
|
|
|
|
this.destinations.destinations.forEach(d => {
|
|
const dest = {
|
|
destination_node_id: d.destination_node.id,
|
|
annual_amount: d.annual_amount,
|
|
disposal_costs: d.userDefinedHandlingCosts ? d.disposal_costs : null,
|
|
repackaging_costs: d.userDefinedHandlingCosts ? d.repackaging_costs : null,
|
|
handling_costs: d.userDefinedHandlingCosts ? d.handling_costs : null,
|
|
}
|
|
destinations.push(dest);
|
|
})
|
|
|
|
const body = {destinations: destinations, premise_id: this.destinations.premise_ids};
|
|
const url = `${config.backendUrl}/calculation/destination/`;
|
|
|
|
const {data: data, headers: headers} = await performRequest(this, 'PUT', url, body).catch(e => {
|
|
this.destinations = null;
|
|
this.processDestinationMassEdit = false;
|
|
});
|
|
|
|
if (data) {
|
|
for (const id of Object.keys(data)) {
|
|
this.premisses.find(p => String(p.id) === id).destinations = data[id];
|
|
}
|
|
}
|
|
|
|
this.destinations = null;
|
|
this.processDestinationMassEdit = false;
|
|
}
|
|
},
|
|
cancelMassEdit() {
|
|
this.destinations = null;
|
|
},
|
|
|
|
copyAllFromPremises(from, fullCopy = true) {
|
|
|
|
const d = {};
|
|
|
|
d.id = `e${from.id}`;
|
|
d.destination_node = structuredClone(toRaw(from.destination_node));
|
|
d.routes = fullCopy ? structuredClone(toRaw(from.routes)) : null;
|
|
|
|
d.annual_amount = from.annual_amount;
|
|
d.is_d2d = from.is_d2d;
|
|
d.rate_d2d = from.is_d2d ? from.rate_d2d : null;
|
|
d.lead_time_d2d = from.is_d2d ? from.lead_time_d2d : null;
|
|
d.handling_costs = from.handling_costs;
|
|
d.disposal_costs = from.disposal_costs;
|
|
d.repackaging_costs = from.repackaging_costs;
|
|
d.userDefinedHandlingCosts = from.handling_costs !== null || from.disposal_costs !== null || from.repackaging_costs !== null;
|
|
|
|
return d;
|
|
},
|
|
copyAllToPremise(from, to, fullCopy = true) {
|
|
|
|
const d = to ?? {};
|
|
|
|
d.annual_amount = from.annual_amount;
|
|
d.is_d2d = from.is_d2d;
|
|
d.rate_d2d = from.is_d2d ? from.rate_d2d : null;
|
|
d.lead_time_d2d = from.is_d2d ? from.lead_time_d2d : null;
|
|
|
|
if (from.userDefinedHandlingCosts) {
|
|
d.disposal_costs = from.disposal_costs;
|
|
d.repackaging_costs = from.repackaging_costs;
|
|
d.handling_costs = from.handling_costs;
|
|
} else {
|
|
d.disposal_costs = null;
|
|
d.repackaging_costs = null;
|
|
d.handling_costs = null;
|
|
}
|
|
|
|
if (fullCopy && (from.routes ?? null) !== null) {
|
|
to.routes.forEach(route => route.is_selected = from.routes.find(r => r.id === route.id)?.is_selected ?? false);
|
|
}
|
|
|
|
return d;
|
|
},
|
|
|
|
/**
|
|
* Selects all destinations for the given "ids" for editing.
|
|
* This creates a copy of the destination with id "id".
|
|
* They are written back as soon as the user closes the dialog.
|
|
*/
|
|
selectDestination(id) {
|
|
if (this.premisses === null) return;
|
|
|
|
logger.info("selectDestination:", id)
|
|
|
|
const dest = this.destinations.destinations.find(d => d.id === id);
|
|
|
|
|
|
if ((dest ?? null) == null) {
|
|
const error = {
|
|
code: 'Frontend error.',
|
|
message: `Destination not found: ${id}. Please contact support.`,
|
|
trace: null
|
|
}
|
|
throw new Error("Internal frontend error: Destination not found: " + id);
|
|
}
|
|
|
|
this.selectedDestination = structuredClone(toRaw(dest));
|
|
},
|
|
async deselectDestinations(save = false) {
|
|
if (this.premisses === null) return;
|
|
|
|
|
|
if (save) {
|
|
const idx = this.destinations.destinations.findIndex(d => d.id === this.selectedDestination.id);
|
|
this.destinations.destinations.splice(idx, 1, this.selectedDestination);
|
|
|
|
if (!this.destinations.fromMassEditView) {
|
|
//TODO write trough backend if no massEdit
|
|
|
|
const toDest = this.singleSelectedPremise.destinations.find(to => this.selectedDestination.id.substring(1) === String(to.id));
|
|
this.copyAllToPremise(this.selectedDestination, toDest);
|
|
|
|
const body = {
|
|
annual_amount: toDest.annual_amount,
|
|
repackaging_costs: toDest.repackaging_costs,
|
|
handling_costs: toDest.handling_costs,
|
|
disposal_costs: toDest.disposal_costs,
|
|
is_d2d: toDest.is_d2d,
|
|
rate_d2d: toDest.rate_d2d,
|
|
lead_time_d2d: toDest.lead_time_d2d,
|
|
route_selected_id: toDest.routes.find(r => r.is_selected)?.id ?? null,
|
|
};
|
|
|
|
logger.info(body)
|
|
|
|
const url = `${config.backendUrl}/calculation/destination/${toDest.id}`;
|
|
await performRequest(this, 'PUT', url, body, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.selectedDestination = null;
|
|
},
|
|
async deleteDestination(id) {
|
|
|
|
|
|
/*
|
|
* 1. delete from destinations copy
|
|
*/
|
|
const idx = this.destinations.destinations.findIndex(d => d.id === id);
|
|
|
|
if (idx === -1) {
|
|
logger.info("Destination not found in mass edit: , id)");
|
|
return;
|
|
}
|
|
|
|
this.destinations.destinations.splice(idx, 1);
|
|
|
|
/*
|
|
* 2. delete from backend if not mass edit
|
|
*/
|
|
|
|
if (!this.destinations.massEdit && id.startsWith('e')) { /* 'v'-ids cannot be deleted because they only exist in the frontend */
|
|
if (this.premisses === null) return;
|
|
|
|
const origId = id.substring(1);
|
|
|
|
const url = `${config.backendUrl}/calculation/destination/${origId}`;
|
|
await performRequest(this, 'DELETE', url, null, false).catch(async e => {
|
|
logger.error("Unable to delete destination: " + origId + "");
|
|
logger.error(e);
|
|
await this.loadPremissesIfNeeded(this.premisses.map(p => p.id));
|
|
});
|
|
|
|
for (const p of this.premisses) {
|
|
const toBeDeleted = p.destinations.findIndex(d => String(d.id) === String(origId))
|
|
|
|
logger.info(toBeDeleted)
|
|
|
|
if (toBeDeleted !== -1) {
|
|
p.destinations.splice(toBeDeleted, 1)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
async addDestination(node) {
|
|
|
|
if (this.destinations.massEdit) {
|
|
|
|
const existing = this.destinations.destinations.find(d => d.destination_node.id === node.id);
|
|
logger.info(existing)
|
|
|
|
if ((existing ?? null) !== null) {
|
|
logger.info("Destination already exists", node.id);
|
|
return [existing.id];
|
|
}
|
|
|
|
const destination = {
|
|
id: `v${node.id}`,
|
|
destination_node: structuredClone(toRaw(node)),
|
|
massEdit: true,
|
|
annual_amount: 0,
|
|
is_d2d: false,
|
|
rate_d2d: null,
|
|
lead_time_d2d: null,
|
|
disposal_costs: null,
|
|
repackaging_costs: null,
|
|
handling_costs: null,
|
|
userDefinedHandlingCosts: false,
|
|
};
|
|
|
|
this.destinations.destinations.push(destination);
|
|
|
|
return [destination.id];
|
|
|
|
} else {
|
|
const id = node.id;
|
|
|
|
this.processDestinationMassEdit = true;
|
|
|
|
|
|
const toBeUpdated = this.destinations.fromMassEditView ? this.destinations.premise_ids : this.premisses?.filter(p => p.selected).map(p => p.id);
|
|
|
|
if (toBeUpdated === null || toBeUpdated.length === 0) return;
|
|
|
|
const body = {destination_node_id: id, premise_id: toBeUpdated};
|
|
const url = `${config.backendUrl}/calculation/destination/`;
|
|
|
|
|
|
const {data: destinations} = await performRequest(this, 'POST', url, body).catch(e => {
|
|
this.loading = false;
|
|
this.selectedLoading = false;
|
|
this.processDestinationMassEdit = false;
|
|
throw e;
|
|
});
|
|
|
|
const mappedIds = []
|
|
|
|
for (const id of Object.keys(destinations)) {
|
|
const premise = this.premisses.find(p => String(p.id) === id)
|
|
premise.destinations.push(destinations[id]);
|
|
const mappedDestination = this.copyAllFromPremises(destinations[id], true);
|
|
mappedIds.push(mappedDestination.id);
|
|
this.destinations.destinations.push(mappedDestination);
|
|
}
|
|
|
|
this.processDestinationMassEdit = false;
|
|
|
|
return mappedIds;
|
|
}
|
|
},
|
|
|
|
|
|
/**
|
|
* Set methods
|
|
* (these are more extensive changes. The edited premises are replaced by the one returned by the backend)
|
|
*/
|
|
|
|
async setSupplier(id, updateMasterData, ids = null) {
|
|
logger.info("setSupplier");
|
|
|
|
const selectedId = this.singleSelectId;
|
|
|
|
this.processDestinationMassEdit = true;
|
|
|
|
const body = {supplier_node_id: id, update_master_data: updateMasterData};
|
|
const url = `${config.backendUrl}/calculation/supplier/`;
|
|
await this.setData(url, body, ids);
|
|
|
|
if (selectedId != null && this.destinations && !this.destinations.fromMassEditView) {
|
|
this.prepareDestinations(selectedId, [selectedId]);
|
|
}
|
|
|
|
this.processDestinationMassEdit = false;
|
|
|
|
},
|
|
async setMaterial(id, updateMasterData, ids = null) {
|
|
logger.info("setMaterial");
|
|
const body = {material_id: id, update_master_data: updateMasterData};
|
|
const url = `${config.backendUrl}/calculation/material/`;
|
|
await this.setData(url, body, ids);
|
|
},
|
|
async setData(url, body, ids = null) {
|
|
|
|
const toBeUpdated = this.premisses ? (ids ? (ids) : (this.premisses.filter(p => p.selected).map(p => p.id))) : null;
|
|
|
|
if (null !== toBeUpdated) {
|
|
this.selectedLoading = true;
|
|
this.loading = true;
|
|
|
|
body.premise_id = toBeUpdated;
|
|
logger.info(url, body)
|
|
|
|
const {data: data} = await performRequest(this, 'PUT', url, body).catch(e => {
|
|
this.loading = false;
|
|
});
|
|
|
|
if (data) {
|
|
data.forEach(p => p.selected = true);
|
|
this.premisses = this.replacePremissesById(this.premisses, data);
|
|
}
|
|
|
|
this.loading = false;
|
|
this.selectedLoading = false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Replace the premisses with the loaded ones by id.
|
|
* This is used to update the premisses after a "Set" change.
|
|
* @param premisses
|
|
* @param loadedData
|
|
* @returns {*}
|
|
*/
|
|
replacePremissesById(premisses, loadedData) {
|
|
const replacementMap = new Map(loadedData.map(obj => [obj.id, obj]));
|
|
const replaced = premisses.map(obj => replacementMap.get(obj.id) || obj);
|
|
logger.info("Replaced", replaced);
|
|
return replaced;
|
|
},
|
|
|
|
/**
|
|
* Save
|
|
*/
|
|
|
|
async savePrice(ids = null, priceData = null) {
|
|
let success = true;
|
|
const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.premisses.filter(p => p.selected))) : null;
|
|
|
|
if (!toBeUpdated?.length) return;
|
|
|
|
const body = {
|
|
premise_ids: toBeUpdated.map(p => p.id),
|
|
material_cost: priceData === null ? toBeUpdated[0].material_cost : priceData.price,
|
|
oversea_share: priceData === null ? toBeUpdated[0].oversea_share : priceData.overSeaShare,
|
|
is_fca_enabled: priceData === null ? toBeUpdated[0].is_fca_enabled : priceData.includeFcaFee
|
|
};
|
|
|
|
await performRequest(this, 'POST', `${config.backendUrl}/calculation/price/`, body, false).catch(e => {
|
|
success = false;
|
|
})
|
|
|
|
return success;
|
|
},
|
|
async savePackaging(ids = null, packagingData = null) {
|
|
let success = true;
|
|
|
|
const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.premisses.filter(p => p.selected))) : null;
|
|
|
|
if (!toBeUpdated?.length) return;
|
|
|
|
const body = {
|
|
premise_ids: toBeUpdated.map(p => p.id),
|
|
|
|
handling_unit: {
|
|
weight: packagingData === null ? toBeUpdated[0].handling_unit.weight : packagingData.weight,
|
|
weight_unit: packagingData === null ? toBeUpdated[0].handling_unit.weight_unit : packagingData.weightUnit,
|
|
|
|
length: packagingData === null ? toBeUpdated[0].handling_unit.length : packagingData.length,
|
|
width: packagingData === null ? toBeUpdated[0].handling_unit.width : packagingData.width,
|
|
height: packagingData === null ? toBeUpdated[0].handling_unit.height : packagingData.height,
|
|
dimension_unit: packagingData === null ? toBeUpdated[0].handling_unit.dimension_unit : packagingData.dimensionUnit,
|
|
|
|
content_unit_count: packagingData === null ? toBeUpdated[0].handling_unit.content_unit_count : packagingData.unitCount,
|
|
},
|
|
|
|
is_mixable: packagingData === null ? toBeUpdated[0].is_mixable : packagingData.mixable,
|
|
is_stackable: packagingData === null ? toBeUpdated[0].is_stackable : packagingData.stackable,
|
|
|
|
};
|
|
|
|
await performRequest(this, 'POST', `${config.backendUrl}/calculation/packaging/`, body, false).catch(() => {
|
|
success = false;
|
|
})
|
|
|
|
return success;
|
|
|
|
},
|
|
async saveMaterial(ids = null, materialData = null, tariffRates = null) {
|
|
let success = true;
|
|
const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.premisses.filter(p => p.selected))) : null;
|
|
|
|
|
|
if (!toBeUpdated?.length) return;
|
|
|
|
const body = {
|
|
premise_ids: toBeUpdated.map(p => p.id),
|
|
|
|
hs_code: materialData === null ? toBeUpdated[0].hs_code : materialData.hsCode,
|
|
tariff_rate: materialData === null ? toBeUpdated[0].tariff_rate : materialData.tariffRate,
|
|
tariff_rates: (null === tariffRates) ? null : Object.fromEntries(tariffRates),
|
|
};
|
|
|
|
await performRequest(this, 'POST', `${config.backendUrl}/calculation/material/`, body, false).catch(() => {
|
|
success = false;
|
|
})
|
|
|
|
|
|
return success;
|
|
|
|
},
|
|
|
|
/**
|
|
* PREMISE stuff
|
|
* =================
|
|
*/
|
|
deselectPremise() {
|
|
this.selectedLoading = true;
|
|
|
|
this.premisses.forEach(p => p.selected = false);
|
|
|
|
this.destinations = null;
|
|
this.selectedDestination = null;
|
|
this.selectedLoading = false;
|
|
},
|
|
setAll(value) {
|
|
|
|
this.selectedLoading = true;
|
|
|
|
const updatedPremises = this.premisses.map(p => ({
|
|
...p,
|
|
selected: value
|
|
}));
|
|
this.premisses = updatedPremises;
|
|
|
|
this.selectedLoading = false;
|
|
|
|
},
|
|
setSelectTo(ids, value) {
|
|
this.selectedLoading = true;
|
|
|
|
const idsSet = new Set(ids);
|
|
const updatedPremises = this.premisses.map(p => ({
|
|
...p,
|
|
selected: idsSet.has(p.id) ? value : p.selected
|
|
}));
|
|
this.premisses = updatedPremises;
|
|
|
|
this.selectedLoading = false;
|
|
},
|
|
async selectSinglePremise(id, ids) {
|
|
this.selectedLoading = true;
|
|
|
|
await this.loadPremissesIfNeeded(ids);
|
|
|
|
this.premisses.forEach(p => p.selected = String(id) === String(p.id));
|
|
|
|
this.prepareDestinations(id, [id]);
|
|
|
|
this.selectedLoading = false;
|
|
},
|
|
async loadAndSelectSinglePremise(id) {
|
|
|
|
this.loading = true;
|
|
this.selectedLoading = true;
|
|
this.premises = [];
|
|
|
|
const params = new URLSearchParams();
|
|
params.append('premissIds', `${[id]}`);
|
|
const url = `${config.backendUrl}/calculation/edit/${params.size === 0 ? '' : '?'}${params.toString()}`;
|
|
|
|
const {data: data, headers: headers} = await performRequest(this, 'GET', url, null).catch(e => {
|
|
this.selectedLoading = false;
|
|
this.loading = false;
|
|
});
|
|
this.premisses = data;
|
|
|
|
this.premisses.forEach(p => p.selected = true);
|
|
this.prepareDestinations(id, [id]);
|
|
this.selectedLoading = false;
|
|
this.loading = false;
|
|
},
|
|
async loadPremissesIfNeeded(ids, exact = false) {
|
|
const reload = this.premisses ? !ids.every((id) => this.premisses.find(d => d.id === id) && (!exact || ids.length === this.premisses.length)) : true;
|
|
|
|
if (reload) {
|
|
await this.loadPremissesForced(ids);
|
|
}
|
|
},
|
|
async loadPremissesForced(ids) {
|
|
|
|
this.loading = true;
|
|
this.premises = [];
|
|
|
|
const params = new URLSearchParams();
|
|
params.append('premissIds', ids.join(', '));
|
|
const url = `${config.backendUrl}/calculation/edit/${params.size === 0 ? '' : '?'}${params.toString()}`;
|
|
|
|
const {data: data, headers: headers} = await performRequest(this, 'GET', url, null).catch(e => {
|
|
this.loading = false;
|
|
});
|
|
this.premisses = data;
|
|
|
|
this.premisses.forEach(p => p.selected = false);
|
|
this.loading = false;
|
|
|
|
|
|
},
|
|
removePremise(id) {
|
|
const idx = this.premisses.findIndex(p => p.id === id);
|
|
this.premisses.splice(idx, 1);
|
|
}
|
|
}
|
|
})
|
|
; |