diff --git a/frontend/src/Components/LineEditor.svelte b/frontend/src/Components/LineEditor.svelte index 8d91272..f44ca9e 100644 --- a/frontend/src/Components/LineEditor.svelte +++ b/frontend/src/Components/LineEditor.svelte @@ -21,8 +21,12 @@ async function applyEdit(){ let success = await onSet(editValue); - if (success) value = editValue; - editing=false; + if (success) { + value = editValue; + editing=false; + } else { + editValue = value; + } } function ignore(evt){ diff --git a/frontend/src/routes/stock/Index.svelte b/frontend/src/routes/stock/Index.svelte index d5bcfab..6eb70ad 100644 --- a/frontend/src/routes/stock/Index.svelte +++ b/frontend/src/routes/stock/Index.svelte @@ -22,6 +22,32 @@ let properties = $state(null); let top_level = $state(null); + async function deleteLocation(loc){ + if (!confirm(t('confirm_delete',{element:loc.name}))) return; + const url = api(`stock/location/${loc.id}`); + const res = await fetch(url,{ + credentials: 'include', + method: 'DELETE', + }); + if (res.ok){ + yikes(); + for (var owner of top_level){ + if (owner.locations && dropNestedLocation(owner.locations,loc)) break; + } + } else error(res); + } + + function dropNestedLocation(locations,loc){ + for (let [idx,entry] of locations.entries()){ + if (entry.id == loc.id){ + locations.splice(idx,1); + return true; + } + if (entry.locations && dropNestedLocation(entry.locations,loc)) return true; + } + return false; + } + async function move_dragged_to(new_loc){ const data = { item : draggedItem.id, target: new_loc.id }; const url = api('stock/move_item'); diff --git a/frontend/src/routes/stock/Locations.svelte b/frontend/src/routes/stock/Locations.svelte index f19e009..8a32edf 100644 --- a/frontend/src/routes/stock/Locations.svelte +++ b/frontend/src/routes/stock/Locations.svelte @@ -39,6 +39,10 @@ return false; } + function reset(){ + new_location_name = ''; + } + async function onSet(new_location_name){ const data = { name: new_location_name, @@ -54,6 +58,8 @@ yikes; const saved = await res.json(); locations.push(saved); + show_location_form = false; + setTimeout(reset,500); return true; } else { error(res); @@ -86,18 +92,6 @@ - \ No newline at end of file + diff --git a/stock/src/main/java/de/srsoftware/umbrella/stock/SqliteDb.java b/stock/src/main/java/de/srsoftware/umbrella/stock/SqliteDb.java index c340ed6..c904b84 100644 --- a/stock/src/main/java/de/srsoftware/umbrella/stock/SqliteDb.java +++ b/stock/src/main/java/de/srsoftware/umbrella/stock/SqliteDb.java @@ -14,6 +14,7 @@ import static java.lang.System.Logger.Level.ERROR; import static java.lang.System.Logger.Level.WARNING; import static java.text.MessageFormat.format; +import de.srsoftware.tools.jdbc.Query; import de.srsoftware.umbrella.core.BaseDb; import de.srsoftware.umbrella.core.model.*; import de.srsoftware.umbrella.core.model.Location; @@ -56,6 +57,16 @@ public class SqliteDb extends BaseDb implements StockDb { } } + @Override + public Location delete(DbLocation location) { + try { + Query.delete().from(TABLE_LOCATIONS).where(ID,equal(location.id())).execute(db); + return location; + } catch (SQLException e){ + throw databaseException("Failed to delete \"{0}\"",location.name()); + } + } + /** * id, owner, owner_id, code, name, location_id * @throws SQLException @@ -333,7 +344,7 @@ public class SqliteDb extends BaseDb implements StockDb { rs.close(); if (prop == null) throw databaseException("Failed to add new property to item {0}",itemId); if ("".equals(value)){ - delete().from(TABLE_ITEM_PROPERTIES).where(ITEM_ID,equal(itemId)).where(PROPERTY_ID,equal(existingPropId)).execute(db); + Query.delete().from(TABLE_ITEM_PROPERTIES).where(ITEM_ID,equal(itemId)).where(PROPERTY_ID,equal(existingPropId)).execute(db); } else { replaceInto(TABLE_ITEM_PROPERTIES,ITEM_ID,PROPERTY_ID,VALUE).values(itemId,existingPropId,value).execute(db); } diff --git a/stock/src/main/java/de/srsoftware/umbrella/stock/StockDb.java b/stock/src/main/java/de/srsoftware/umbrella/stock/StockDb.java index d93350d..80a954b 100644 --- a/stock/src/main/java/de/srsoftware/umbrella/stock/StockDb.java +++ b/stock/src/main/java/de/srsoftware/umbrella/stock/StockDb.java @@ -7,6 +7,7 @@ import java.util.Collection; public interface StockDb { Property addNewProperty(long itemId, String name, Object value, String unit); + Location delete(DbLocation location); Collection listChildLocations(long parentId); Collection listCompanyLocations(Company company); Collection listItemsAt(Location location); diff --git a/stock/src/main/java/de/srsoftware/umbrella/stock/StockModule.java b/stock/src/main/java/de/srsoftware/umbrella/stock/StockModule.java index 19a796c..a1205c8 100644 --- a/stock/src/main/java/de/srsoftware/umbrella/stock/StockModule.java +++ b/stock/src/main/java/de/srsoftware/umbrella/stock/StockModule.java @@ -44,7 +44,39 @@ public class StockModule extends BaseHandler implements StockService { if (owner instanceof UmbrellaUser u && user.id() == u.id()) return true; if (owner instanceof Company comp) return companyService().membership(comp.id(),user.id()); return false; + } + + private boolean deleteLocation(UmbrellaUser user, Location locationRef, HttpExchange ex) throws IOException { + var location = locationRef.resolve(); + var owner = location.owner().resolve(); + if (!assigned(owner,user)) throw forbidden("You are not allowed to modify \"{0}\"",location); + if (!stockDb.listItemsAt(location).isEmpty()) throw forbidden("\"{0}\" cannot be deleted, as it contains items!",location); + if (!stockDb.listChildLocations(location.id()).isEmpty()) throw forbidden("\"{0}\" cannot be deleted, as it contains other locations!",location); + return sendContent(ex,stockDb.delete(location)); + } + @Override + public boolean doDelete(Path path, HttpExchange ex) throws IOException { + addCors(ex); + try { + Optional token = SessionToken.from(ex).map(Token::of); + var user = userService().loadUser(token); + if (user.isEmpty()) return unauthorized(ex); + var head = path.pop(); + return switch (head) { + case LOCATION -> { + try { + var location = Location.of(Long.parseLong(path.pop())); + yield deleteLocation(user.get(), location, ex); + } catch (NumberFormatException e){ + yield super.doGet(path,ex); + } + } + case null, default -> super.doDelete(path,ex); + }; + } catch (UmbrellaException e){ + return send(ex,e); + } } @Override