Add bulk update functionality for destination data
- Introduced `DestinationMassUpdateDTO` to handle bulk update payload. - Added new API endpoint `/destination/all` for mass updates in `PremiseController`. - Updated frontend logic to support mass updating of destinations and enhanced matrix calculations with debounce. - Improved handling of destination and route processing across components.
This commit is contained in:
parent
a40a8c6bb4
commit
cbd467d3b0
8 changed files with 168 additions and 35 deletions
|
|
@ -107,7 +107,7 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(useDestinationEditStore, usePremiseEditStore),
|
...mapStores(useDestinationEditStore, usePremiseEditStore),
|
||||||
rows() {
|
rows() {
|
||||||
return this.destinationEditStore.getHandlingCostMatrix
|
return this.destinationEditStore.getHandlingCostMatrix ?? [];
|
||||||
},
|
},
|
||||||
allChecked() {
|
allChecked() {
|
||||||
return this.rows.every(r => r.selected);
|
return this.rows.every(r => r.selected);
|
||||||
|
|
@ -128,7 +128,10 @@ export default {
|
||||||
async created() {
|
async created() {
|
||||||
this.onLoadingChange(true);
|
this.onLoadingChange(true);
|
||||||
try {
|
try {
|
||||||
await this.buildMatrix();
|
await new Promise(resolve => setTimeout(() => {
|
||||||
|
this.buildMatrix();
|
||||||
|
resolve();
|
||||||
|
}, 10));
|
||||||
} finally {
|
} finally {
|
||||||
this.onLoadingChange(false);
|
this.onLoadingChange(false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(useDestinationEditStore, usePremiseEditStore),
|
...mapStores(useDestinationEditStore, usePremiseEditStore),
|
||||||
rows() {
|
rows() {
|
||||||
return this.destinationEditStore.getQuantityMatrix;
|
return this.destinationEditStore.getQuantityMatrix ?? [];
|
||||||
},
|
},
|
||||||
allChecked() {
|
allChecked() {
|
||||||
return this.rows.every(r => r.selected);
|
return this.rows.every(r => r.selected);
|
||||||
|
|
@ -98,7 +98,10 @@ export default {
|
||||||
async created() {
|
async created() {
|
||||||
this.onLoadingChange(true);
|
this.onLoadingChange(true);
|
||||||
try {
|
try {
|
||||||
await this.buildMatrix();
|
await new Promise(resolve => setTimeout(() => {
|
||||||
|
this.buildMatrix();
|
||||||
|
resolve();
|
||||||
|
}, 10));
|
||||||
} finally {
|
} finally {
|
||||||
this.onLoadingChange(false);
|
this.onLoadingChange(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(useDestinationEditStore, usePremiseEditStore),
|
...mapStores(useDestinationEditStore, usePremiseEditStore),
|
||||||
rows() {
|
rows() {
|
||||||
return this.destinationEditStore.getRouteMatrix;
|
return this.destinationEditStore.getRouteMatrix ?? [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -75,7 +75,10 @@ export default {
|
||||||
async created() {
|
async created() {
|
||||||
this.onLoadingChange(true);
|
this.onLoadingChange(true);
|
||||||
try {
|
try {
|
||||||
await this.buildMatrix();
|
await new Promise(resolve => setTimeout(() => {
|
||||||
|
this.buildMatrix();
|
||||||
|
resolve();
|
||||||
|
}, 10));
|
||||||
} finally {
|
} finally {
|
||||||
this.onLoadingChange(false);
|
this.onLoadingChange(false);
|
||||||
}
|
}
|
||||||
|
|
@ -116,13 +119,16 @@ export default {
|
||||||
const destOfCurPremise = this.destinationEditStore.getByPremiseId(pId);
|
const destOfCurPremise = this.destinationEditStore.getByPremiseId(pId);
|
||||||
if (!destOfCurPremise) continue;
|
if (!destOfCurPremise) continue;
|
||||||
|
|
||||||
/* supplier map collects all destinations for one supplier
|
/* supplier map collects all destinations for one supplier.
|
||||||
* and replaces the destination if the same destination is found
|
* if there is more than one instance of a destination for one supplier
|
||||||
* that already has a selected route.
|
* (more than one part number), a destination instance is chosen by the following priority list:
|
||||||
|
* 1. instances with d2d rate
|
||||||
|
* 2. instances with selected routes
|
||||||
|
* 3. all other instances.
|
||||||
*/
|
*/
|
||||||
if (!supplierToDestinationsMap.has(curPremise.supplier.id)) {
|
if (!supplierToDestinationsMap.has(curPremise.supplier.id)) {
|
||||||
supplierToDestinationsMap.set(curPremise.supplier.id, {
|
supplierToDestinationsMap.set(curPremise.supplier.id, {
|
||||||
destinations: destOfCurPremise ?? []
|
destinations: [...destOfCurPremise]
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const mapEntry = supplierToDestinationsMap.get(curPremise.supplier.id);
|
const mapEntry = supplierToDestinationsMap.get(curPremise.supplier.id);
|
||||||
|
|
@ -144,8 +150,8 @@ export default {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/* destination map collects all destinations over all
|
/* Collects all destinations over all
|
||||||
* suppliers for table headers
|
* suppliers and part numbers for the table headers
|
||||||
*/
|
*/
|
||||||
for (const d of destOfCurPremise) {
|
for (const d of destOfCurPremise) {
|
||||||
const destId = d.destination_node.id;
|
const destId = d.destination_node.id;
|
||||||
|
|
@ -166,23 +172,23 @@ export default {
|
||||||
const premiseMap = new Map();
|
const premiseMap = new Map();
|
||||||
|
|
||||||
this.premiseIds.forEach(pId => {
|
this.premiseIds.forEach(pId => {
|
||||||
const premise = this.premiseEditStore.getById(pId);
|
const curPremise = this.premiseEditStore.getById(pId);
|
||||||
const destinations = this.destinationEditStore.getByPremiseId(pId);
|
const destOfCurPremise = this.destinationEditStore.getByPremiseId(pId);
|
||||||
|
|
||||||
if (!premiseMap.has(premise.supplier.id)) {
|
if (!premiseMap.has(curPremise.supplier.id)) {
|
||||||
premiseMap.set(premise.supplier.id, {
|
premiseMap.set(curPremise.supplier.id, {
|
||||||
ids: [],
|
ids: [],
|
||||||
supplierNodeId: premise.supplier.id,
|
supplierNodeId: curPremise.supplier.id,
|
||||||
supplier: premise.supplier,
|
supplier: curPremise.supplier,
|
||||||
destinations: this.buildDestinations(columnHeadersMap, supplierToDestinationsMap.get(premise.supplier.id)?.destinations ?? [])
|
destinations: this.buildDestinations(columnHeadersMap, supplierToDestinationsMap.get(curPremise.supplier.id)?.destinations ?? [])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const row = premiseMap.get(premise.supplier.id);
|
const row = premiseMap.get(curPremise.supplier.id);
|
||||||
|
|
||||||
if (row) {
|
if (row) {
|
||||||
row.ids.push(premise.id);
|
row.ids.push(curPremise.id);
|
||||||
this.addDestinationsToRow(row.destinations, destinations)
|
this.addDestinationsToRow(row.destinations, destOfCurPremise)
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
@ -215,17 +221,25 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addDestinationsToRow(rowDestinations, premiseDestinations) {
|
addDestinationsToRow(rowDestinations, destOfPremises) {
|
||||||
premiseDestinations.forEach(premD => {
|
destOfPremises.forEach(curDestOfPremise => {
|
||||||
|
|
||||||
let existingDest = rowDestinations.find(rowD => rowD.destinationNodeId === premD.destination_node.id) ?? null;
|
/* rowDestinations contains all here known destinations that are shown
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
let existingDest = rowDestinations.find(rowD => rowD.destinationNodeId === curDestOfPremise.destination_node.id) ?? null;
|
||||||
|
|
||||||
if (existingDest) {
|
if (existingDest) {
|
||||||
existingDest.disabled = false;
|
existingDest.disabled = false;
|
||||||
existingDest.ids.push(premD.id);
|
|
||||||
|
if(existingDest.ids.includes(curDestOfPremise.id))
|
||||||
|
console.log("Duplicate id: ", curDestOfPremise.id);
|
||||||
|
|
||||||
|
existingDest.ids.push(curDestOfPremise.id);
|
||||||
|
|
||||||
/* add route ids to routes */
|
/* add route ids to routes */
|
||||||
this.verifyRoutes(existingDest, premD)
|
this.verifyRoutes(existingDest, curDestOfPremise)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -242,9 +256,13 @@ export default {
|
||||||
premiseRoutes.forEach(route => {
|
premiseRoutes.forEach(route => {
|
||||||
const routeString = JSON.stringify(route.transit_nodes.map(n => n.external_mapping_id)); //.join(" > ").replace("_", " ");
|
const routeString = JSON.stringify(route.transit_nodes.map(n => n.external_mapping_id)); //.join(" > ").replace("_", " ");
|
||||||
|
|
||||||
if (!(rowDest.routes.some(r => r.routeCompareString === routeString && r.type === route.type))) {
|
const rowRoute = rowDest.routes.find(r => r.routeCompareString === routeString && r.type === route.type);
|
||||||
|
|
||||||
|
if (!rowRoute) {
|
||||||
console.log("no matching route ", routeString, rowDest);
|
console.log("no matching route ", routeString, rowDest);
|
||||||
rowDest.valid = false;
|
rowDest.valid = false;
|
||||||
|
} else {
|
||||||
|
rowRoute.ids.push(route.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -252,6 +270,7 @@ export default {
|
||||||
|
|
||||||
return routes?.map(r => {
|
return routes?.map(r => {
|
||||||
return {
|
return {
|
||||||
|
ids: [],
|
||||||
type: r.type,
|
type: r.type,
|
||||||
selected: r.is_selected,
|
selected: r.is_selected,
|
||||||
transitNodes: r.transit_nodes.map(n => n.external_mapping_id),
|
transitNodes: r.transit_nodes.map(n => n.external_mapping_id),
|
||||||
|
|
|
||||||
|
|
@ -439,6 +439,8 @@ export default {
|
||||||
},
|
},
|
||||||
async closeEditModalAction(action) {
|
async closeEditModalAction(action) {
|
||||||
|
|
||||||
|
let massUpdate = false;
|
||||||
|
|
||||||
if (this.modalType === 'amount' || this.modalType === 'routes' || this.modalType === "destinations") {
|
if (this.modalType === 'amount' || this.modalType === 'routes' || this.modalType === "destinations") {
|
||||||
|
|
||||||
if (action === 'accept') {
|
if (action === 'accept') {
|
||||||
|
|
@ -450,7 +452,7 @@ export default {
|
||||||
await this.destinationEditStore.massSetDestinations(setMatrix);
|
await this.destinationEditStore.massSetDestinations(setMatrix);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.destinationEditStore.massUpdateDestinations();
|
massUpdate = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -458,6 +460,10 @@ export default {
|
||||||
this.fillData(this.modalType);
|
this.fillData(this.modalType);
|
||||||
this.modalType = null;
|
this.modalType = null;
|
||||||
|
|
||||||
|
if(massUpdate) {
|
||||||
|
await this.destinationEditStore.massUpdateDestinations(this.editIds);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.modalStash && action === 'accept') {
|
if (this.modalStash && action === 'accept') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.openModal(this.modalStash.type, this.modalStash.ids, this.modalStash.dataSource, this.modalStash.massEdit);
|
this.openModal(this.modalStash.type, this.modalStash.ids, this.modalStash.dataSource, this.modalStash.massEdit);
|
||||||
|
|
|
||||||
|
|
@ -108,15 +108,87 @@ export const useDestinationEditStore = defineStore('destinationEdit', {
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
async massUpdateDestinations() {
|
async massUpdateDestinations(premiseIds) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
this.updateQuantity();
|
|
||||||
this.updateHandlingCosts();
|
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(() => {
|
||||||
|
this.updateQuantity();
|
||||||
|
resolve();
|
||||||
|
}, 10));
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(() => {
|
||||||
|
this.updateHandlingCosts();
|
||||||
|
resolve();
|
||||||
|
}, 10));
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(() => {
|
||||||
|
this.updateRoutes();
|
||||||
|
resolve();
|
||||||
|
}, 10));
|
||||||
|
|
||||||
|
const destinationMap = new Map();
|
||||||
|
|
||||||
|
console.log("premiseids", premiseIds )
|
||||||
|
|
||||||
|
premiseIds.forEach(premiseId => {
|
||||||
|
this.destinations.get(premiseId)?.forEach(toUpdate => {
|
||||||
|
|
||||||
|
console.log("toUpdate", toUpdate)
|
||||||
|
|
||||||
|
destinationMap.set(toUpdate.id,{
|
||||||
|
annual_amount: toUpdate.annual_amount,
|
||||||
|
repackaging_costs: toUpdate.repackaging_costs,
|
||||||
|
handling_costs: toUpdate.handling_costs,
|
||||||
|
disposal_costs: toUpdate.disposal_costs,
|
||||||
|
is_d2d: toUpdate.is_d2d,
|
||||||
|
rate_d2d: toUpdate.rate_d2d,
|
||||||
|
lead_time_d2d: toUpdate.lead_time_d2d,
|
||||||
|
route_selected_id: toUpdate.routes.find(r => r.is_selected)?.id ?? null,
|
||||||
|
})
|
||||||
|
} )
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("destmap",destinationMap)
|
||||||
|
|
||||||
|
await performRequest(this, 'PUT', `${config.backendUrl}/calculation/destination/all`, {destinations: Object.fromEntries(destinationMap)}, false);
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
|
updateRoutes() {
|
||||||
|
this.routeMatrix.forEach(row => {
|
||||||
|
|
||||||
|
row.ids.forEach(premiseId => {
|
||||||
|
row.destinations.forEach(destinationUpdateInfo => {
|
||||||
|
const destOfCurPremisses = this.destinations.get(premiseId);
|
||||||
|
|
||||||
|
if ((destOfCurPremisses ?? null) !== null) {
|
||||||
|
const destOfCurPremise = destOfCurPremisses.find(d => destinationUpdateInfo.destinationNodeId === d.destination_node.id);
|
||||||
|
|
||||||
|
if(destinationUpdateInfo.ids.includes(destOfCurPremise.id)) {
|
||||||
|
/* set d2d stuff */
|
||||||
|
destOfCurPremise.is_d2d = destinationUpdateInfo.isD2d;
|
||||||
|
destOfCurPremise.rate_d2d = destinationUpdateInfo.rateD2d;
|
||||||
|
destOfCurPremise.lead_time_d2d = destinationUpdateInfo.leadTimeD2d;
|
||||||
|
|
||||||
|
/* set selected route */
|
||||||
|
const selectedRoute = destinationUpdateInfo.routes?.find(r => r.routeCompareString === destinationUpdateInfo.selectedRoute);
|
||||||
|
|
||||||
|
destOfCurPremise.routes.forEach(r => r.is_selected = false);
|
||||||
|
|
||||||
|
if(selectedRoute) {
|
||||||
|
const routeOfCurPremise = destOfCurPremise.routes.find(r => selectedRoute.ids.includes(r.id));
|
||||||
|
|
||||||
|
if(routeOfCurPremise) {
|
||||||
|
routeOfCurPremise.is_selected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
updateHandlingCosts() {
|
updateHandlingCosts() {
|
||||||
this.handlingCostMatrix.forEach(row => {
|
this.handlingCostMatrix.forEach(row => {
|
||||||
const destinations = this.destinations.get(row.id);
|
const destinations = this.destinations.get(row.id);
|
||||||
|
|
@ -125,7 +197,7 @@ export const useDestinationEditStore = defineStore('destinationEdit', {
|
||||||
|
|
||||||
const destination = destinations.find(dest => dest.id === row.destinationId);
|
const destination = destinations.find(dest => dest.id === row.destinationId);
|
||||||
|
|
||||||
if((destination ?? null) !== null) {
|
if ((destination ?? null) !== null) {
|
||||||
destination.disposal_costs = row.disposal_costs;
|
destination.disposal_costs = row.disposal_costs;
|
||||||
destination.repackaging_costs = row.repackaging_costs;
|
destination.repackaging_costs = row.repackaging_costs;
|
||||||
destination.handling_costs = row.handling_costs;
|
destination.handling_costs = row.handling_costs;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import de.avatic.lcc.dto.calculation.create.CreatePremiseDTO;
|
||||||
import de.avatic.lcc.dto.calculation.create.PremiseSearchResultDTO;
|
import de.avatic.lcc.dto.calculation.create.PremiseSearchResultDTO;
|
||||||
import de.avatic.lcc.dto.calculation.edit.PremiseDetailDTO;
|
import de.avatic.lcc.dto.calculation.edit.PremiseDetailDTO;
|
||||||
import de.avatic.lcc.dto.calculation.edit.destination.DestinationCreateDTO;
|
import de.avatic.lcc.dto.calculation.edit.destination.DestinationCreateDTO;
|
||||||
|
import de.avatic.lcc.dto.calculation.edit.destination.DestinationMassUpdateDTO;
|
||||||
import de.avatic.lcc.dto.calculation.edit.destination.DestinationSetDTO;
|
import de.avatic.lcc.dto.calculation.edit.destination.DestinationSetDTO;
|
||||||
import de.avatic.lcc.dto.calculation.edit.destination.DestinationUpdateDTO;
|
import de.avatic.lcc.dto.calculation.edit.destination.DestinationUpdateDTO;
|
||||||
import de.avatic.lcc.dto.calculation.edit.masterData.MaterialUpdateDTO;
|
import de.avatic.lcc.dto.calculation.edit.masterData.MaterialUpdateDTO;
|
||||||
|
|
@ -128,7 +129,8 @@ public class PremiseController {
|
||||||
@GetMapping({"/edit", "/edit/"})
|
@GetMapping({"/edit", "/edit/"})
|
||||||
@PreAuthorize("hasAnyRole('SUPER', 'CALCULATION')")
|
@PreAuthorize("hasAnyRole('SUPER', 'CALCULATION')")
|
||||||
public ResponseEntity<List<PremiseDetailDTO>> getPremises(@RequestParam List<Integer> premissIds) {
|
public ResponseEntity<List<PremiseDetailDTO>> getPremises(@RequestParam List<Integer> premissIds) {
|
||||||
return ResponseEntity.ok(premisesServices.getPremises(premissIds));
|
var premisses = premisesServices.getPremises(premissIds);
|
||||||
|
return ResponseEntity.ok(premisses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping({"/start", "/start/"})
|
@PutMapping({"/start", "/start/"})
|
||||||
|
|
@ -198,6 +200,13 @@ public class PremiseController {
|
||||||
return ResponseEntity.ok().build();
|
return ResponseEntity.ok().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping({"/destination/all", "/destination/all/"})
|
||||||
|
@PreAuthorize("hasAnyRole('SUPER', 'CALCULATION')")
|
||||||
|
public ResponseEntity<Void> updateAllDestination(@RequestBody @Valid DestinationMassUpdateDTO destinationUpdateDTO) {
|
||||||
|
destinationUpdateDTO.getDestinations().forEach(destinationService::updateDestination);
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
@DeleteMapping({"/destination/{id}", "/destination/{id}/"})
|
@DeleteMapping({"/destination/{id}", "/destination/{id}/"})
|
||||||
@PreAuthorize("hasAnyRole('SUPER', 'CALCULATION')")
|
@PreAuthorize("hasAnyRole('SUPER', 'CALCULATION')")
|
||||||
public ResponseEntity<Void> deleteDestination(@PathVariable Integer id) {
|
public ResponseEntity<Void> deleteDestination(@PathVariable Integer id) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package de.avatic.lcc.dto.calculation.edit.destination;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class DestinationMassUpdateDTO {
|
||||||
|
|
||||||
|
Map<Integer, @Valid DestinationUpdateDTO> destinations;
|
||||||
|
|
||||||
|
|
||||||
|
public Map<Integer, DestinationUpdateDTO> getDestinations() {
|
||||||
|
return destinations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestinations(Map<Integer, DestinationUpdateDTO> destinations) {
|
||||||
|
this.destinations = destinations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -96,7 +96,9 @@ public class PremisesService {
|
||||||
if (!admin)
|
if (!admin)
|
||||||
premiseRepository.checkOwner(premiseIds, userId);
|
premiseRepository.checkOwner(premiseIds, userId);
|
||||||
|
|
||||||
return premiseRepository.getPremisesById(premiseIds).stream().filter(p -> p.getState().equals(PremiseState.DRAFT)).map(premiseTransformer::toPremiseDetailDTO).toList();
|
var premisses = premiseRepository.getPremisesById(premiseIds).stream().filter(p -> p.getState().equals(PremiseState.DRAFT)).toList();
|
||||||
|
|
||||||
|
return premisses.stream().map(premiseTransformer::toPremiseDetailDTO).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue