diff --git a/core/src/main/java/de/srsoftware/umbrella/core/Constants.java b/core/src/main/java/de/srsoftware/umbrella/core/Constants.java index 1410100..f62c58b 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/Constants.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/Constants.java @@ -149,8 +149,6 @@ public class Constants { public static final String PROJECT = "project"; public static final String PROJECT_ID = "project_id"; public static final String PROPERTIES = "properties"; - - public static final String QUANTITY = "quantity"; public static final String RECEIVERS = "receivers"; public static final String REDIRECT = "redirect"; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/Item.java b/core/src/main/java/de/srsoftware/umbrella/core/model/Item.java index 242e01c..a8ef6be 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/Item.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/Item.java @@ -1,16 +1,15 @@ /* © SRSoftware 2025 */ package de.srsoftware.umbrella.core.model; -import de.srsoftware.tools.Mappable; +import static de.srsoftware.umbrella.core.Constants.*; +import de.srsoftware.tools.Mappable; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; import java.util.HashSet; import java.util.Map; -import static de.srsoftware.umbrella.core.Constants.*; - public class Item implements Mappable { private long id; private Mappable owner; @@ -27,6 +26,10 @@ public class Item implements Mappable { this.properties = new HashSet<>(); } + public long id(){ + return id; + } + public static Item of(ResultSet rs, Mappable owner, Location location) throws SQLException { var id = rs.getLong(ID); var code = rs.getString(CODE); @@ -34,6 +37,14 @@ public class Item implements Mappable { return new Item(owner, id, location, code, name); } + public Mappable owner(){ + return owner; + } + + public Collection properties() { + return properties; + } + @Override public Map toMap() { return Map.of( diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/Property.java b/core/src/main/java/de/srsoftware/umbrella/core/model/Property.java index d592f48..5924a93 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/Property.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/Property.java @@ -1,18 +1,33 @@ /* © SRSoftware 2025 */ package de.srsoftware.umbrella.core.model; -import de.srsoftware.tools.Mappable; +import static de.srsoftware.umbrella.core.Constants.*; +import de.srsoftware.tools.Mappable; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.Map; -import static de.srsoftware.umbrella.core.Constants.*; - public class Property implements Mappable { long id; String name; Object value; String unit; - String quantity; + + public Property(long id, String name, Object value, String unit) { + this.id = id; + this.name = name; + this.value = value; + this.unit = unit; + } + + public static Property of(ResultSet rs) throws SQLException { + var id = rs.getLong(ID); + var name = rs.getString(NAME); + var value = rs.getObject(VALUE); + var unit = rs.getString(UNIT); + return new Property(id, name, value, unit); + } @Override public Map toMap() { @@ -20,8 +35,7 @@ public class Property implements Mappable { ID, id, NAME, name, VALUE, value, - UNIT, unit, - QUANTITY, quantity + UNIT, unit ); } } diff --git a/frontend/src/routes/stock/Index.svelte b/frontend/src/routes/stock/Index.svelte index 8db3959..e561c6c 100644 --- a/frontend/src/routes/stock/Index.svelte +++ b/frontend/src/routes/stock/Index.svelte @@ -9,17 +9,19 @@ import ItemProps from './ItemProps.svelte'; + let items = $derived.by(loadItems); + let item = $state(null); + let location = $state(null); + let properties = $state(null); let top_level = $state(null); - let selected = $state(null); - let items = $derived(loadItems(selected)); - async function loadItems(loc){ - if (!loc) return null; - const url = api(`stock/items_at/${loc.id}`) + async function loadItems(){ + if (!location) return null; + const url = api(`stock/items_at/${location.id}`) const res = await fetch(url,{credentials:'include'}); if (res.ok){ yikes(); - return loc.name; + return res.json(); } else { error(res); return null; @@ -47,22 +49,23 @@ {#each top_level as realm,idx}

{realm.name}

{#if realm.locations} - + {/if} {/each} {/if} - {#if selected} -

{selected.name}

+ {#await items} + loading… + {:then data} + {#if location} +

{location.name}

{/if} - {#if items} -
{JSON.stringify(items)}
- {/if} - + a.code.localeCompare(b.code))} bind:selected={item} /> + {/await} - + diff --git a/frontend/src/routes/stock/ItemList.svelte b/frontend/src/routes/stock/ItemList.svelte index 0e1ab5d..be9010f 100644 --- a/frontend/src/routes/stock/ItemList.svelte +++ b/frontend/src/routes/stock/ItemList.svelte @@ -1,13 +1,23 @@ - \ No newline at end of file + + + + + + + + + + {#each items as item} + selected = item}> + + + + + {/each} + +
{t('id')}{t('code')}{t('name')}
{item.id}{item.code}{item.name}
diff --git a/frontend/src/routes/stock/ItemProps.svelte b/frontend/src/routes/stock/ItemProps.svelte index 0aad2c2..9e20424 100644 --- a/frontend/src/routes/stock/ItemProps.svelte +++ b/frontend/src/routes/stock/ItemProps.svelte @@ -1,5 +1,22 @@ -
    -
  • Prop 1
  • -
  • Prop 2
  • -
  • Prop 3
  • -
\ No newline at end of file + + +{#if item} +

{item.name}

+ + + {#each item.properties as prop} + + + + + {/each} + +
+ {prop.name} + + {prop.value} + {prop.unit} +
+{/if} diff --git a/frontend/src/routes/stock/Locations.svelte b/frontend/src/routes/stock/Locations.svelte index 2dee340..b3e802e 100644 --- a/frontend/src/routes/stock/Locations.svelte +++ b/frontend/src/routes/stock/Locations.svelte @@ -26,7 +26,7 @@
  • - {t('add_object',{object:'location'})} + {t('add_object',{object:t('location')})}
  • {#each locations as location} 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 29419a1..7beb459 100644 --- a/stock/src/main/java/de/srsoftware/umbrella/stock/SqliteDb.java +++ b/stock/src/main/java/de/srsoftware/umbrella/stock/SqliteDb.java @@ -7,6 +7,8 @@ import static de.srsoftware.tools.jdbc.Condition.isNull; import static de.srsoftware.tools.jdbc.Query.*; import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; import static de.srsoftware.umbrella.core.Constants.*; +import static de.srsoftware.umbrella.core.ModuleRegistry.companyService; +import static de.srsoftware.umbrella.core.ModuleRegistry.userService; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException; import static de.srsoftware.umbrella.stock.Constants.*; import static java.lang.System.Logger.Level.ERROR; @@ -15,12 +17,7 @@ import static java.text.MessageFormat.format; import de.srsoftware.tools.Mappable; import de.srsoftware.umbrella.core.BaseDb; -import de.srsoftware.umbrella.core.ModuleRegistry; -import de.srsoftware.umbrella.core.exceptions.UmbrellaException; -import de.srsoftware.umbrella.core.model.Company; -import de.srsoftware.umbrella.core.model.Item; -import de.srsoftware.umbrella.core.model.Location; -import de.srsoftware.umbrella.core.model.UmbrellaUser; +import de.srsoftware.umbrella.core.model.*; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; @@ -53,23 +50,6 @@ public class SqliteDb extends BaseDb implements StockDb { super(connection); } - @Override - protected int createTables() { - int currentVersion = createSettingsTable(); - switch (currentVersion){ - case 0: - createLocationsTable(); - createItemsTable(); - createPropertiesTable(); - createItemPropsTable(); - case 1: - dropTokenTable(); - case 2: - transformTables(); - } - return setCurrentVersion(3); - } - private void createIntermediateItemsTable() throws SQLException { // create intermediate table var sql = "CREATE TABLE IF NOT EXISTS items_temp ({0} LONG NOT NULL, {1} LONG NOT NULL, {2} VARCHAR(255), {3} VARCHAR(255) NOT NULL, {4} LONG NOT NULL, PRIMARY KEY({0}, {1}))"; sql = format(sql, OWNER, ID, CODE, NAME, LOCATION_ID); @@ -128,6 +108,23 @@ public class SqliteDb extends BaseDb implements StockDb { } } + @Override + protected int createTables() { + int currentVersion = createSettingsTable(); + switch (currentVersion){ + case 0: + createLocationsTable(); + createItemsTable(); + createPropertiesTable(); + createItemPropsTable(); + case 1: + dropTokenTable(); + case 2: + transformTables(); + } + return setCurrentVersion(3); + } + private void dropTokenTable() { try { db.prepareStatement("DROP TABLE IF EXISTS tokens").execute(); @@ -137,23 +134,57 @@ public class SqliteDb extends BaseDb implements StockDb { } @Override - public Collection listItems(long companyId) throws UmbrellaException { - return List.of(); + public Collection listChildLocations(long parentId) { + try { + var rs = select(ALL).from(TABLE_LOCATIONS).where(PARENT_LOCATION_ID,equal(parentId)).exec(db); + var list = new ArrayList(); + while (rs.next()) list.add(Location.of(rs)); + rs.close(); + return list; + } catch (SQLException e){ + throw databaseException("Failed to load child locations for {0}",parentId); + } + } + + @Override + public Collection listCompanyLocations(Company company) { + try { + var rs = select(ALL).from(TABLE_LOCATIONS).where(OWNER,equal(-company.id())).where(PARENT_LOCATION_ID,isNull()).exec(db); + var list = new ArrayList(); + while (rs.next()) list.add(Location.of(rs)); + rs.close(); + return list; + } catch (SQLException e){ + throw databaseException("Failed to load locations for user {0}",company.name()); + } } @Override public Collection listItemsAt(long locationId) { try { + var location = loadLocation(locationId); var rs = select(ALL).from(TABLE_ITEMS).where(LOCATION_ID,equal(locationId)).exec(db); var list = new ArrayList(); - + var ownerMap = new HashMap(); while (rs.next()) { var ownerId = rs.getLong(OWNER); - Mappable owner = ownerId < 0 ? ModuleRegistry.companyService().get(-ownerId) : ModuleRegistry.userService().loadUser(ownerId); - var location = loadLocation(rs.getLong(LOCATION_ID)); + var owner = ownerMap.get(ownerId); + if (owner == null) { + owner = ownerId < 0 ? companyService().get(-ownerId) : userService().loadUser(ownerId); + ownerMap.put(ownerId,owner); + } list.add(Item.of(rs, owner, location)); } rs.close(); + + for (var item : list){ + var owner = item.owner(); + var ownerId = owner instanceof Company comp ? -comp.id() : (owner instanceof UmbrellaUser u ? u.id() : 0); + if (ownerId == 0) throw databaseException("Encountered unknown item owner of type {0}",owner.getClass().getSimpleName()); + rs = select(ALL).from(TABLE_ITEM_PROPERTIES).leftJoin(PROPERTY_ID,TABLE_PROPERTIES,ID).where(OWNER,equal(ownerId)).where(ITEM_ID,equal(item.id())).exec(db); + while (rs.next()) item.properties().add(Property.of(rs)); + rs.close(); + } return list; } catch (SQLException e){ throw databaseException("Failed to load items at {0}",locationId); @@ -173,37 +204,6 @@ public class SqliteDb extends BaseDb implements StockDb { } } - @Override - public Collection listLocations(long companyId) { - return List.of(); - } - - @Override - public Collection listChildLocations(long parentId) { - try { - var rs = select(ALL).from(TABLE_LOCATIONS).where(PARENT_LOCATION_ID,equal(parentId)).exec(db); - var list = new ArrayList(); - while (rs.next()) list.add(Location.of(rs)); - rs.close(); - return list; - } catch (SQLException e){ - throw databaseException("Failed to load child locations for {0}",parentId); - } - } - - @Override - public Collection listCompanyLocations(Company company) { - try { - var rs = select(ALL).from(TABLE_LOCATIONS).where(OWNER,equal(-company.id())).where(PARENT_LOCATION_ID,isNull()).exec(db); - var list = new ArrayList(); - while (rs.next()) list.add(Location.of(rs)); - rs.close(); - return list; - } catch (SQLException e){ - throw databaseException("Failed to load locations for user {0}",company.name()); - } - } - @Override public Collection listUserLocations(UmbrellaUser user) { try { @@ -217,29 +217,6 @@ public class SqliteDb extends BaseDb implements StockDb { } } - private void transformTables(){ - try { - db.setAutoCommit(false); - createIntermediateLocationTable(); - createIntermediateItemsTable(); - createIntermediatePropsTable(); - var oldLocationIdsToNew = transformLocations(); - transformItems(oldLocationIdsToNew); - transformProperties(); - replaceLocationsTable(); - replaceItemsTable(); - replaceItemPropsTable(); - db.setAutoCommit(true); - } catch (Exception e) { - try { - db.rollback(); - } catch (SQLException ignored) { - } - LOG.log(ERROR,"Failed to transform {0} table!",TABLE_LOCATIONS,e); - throw databaseException("Failed to transform {0} table!",TABLE_LOCATIONS); - } - } - private void replaceItemsTable() throws SQLException { db.prepareStatement(format("DROP TABLE {0}",TABLE_ITEMS)).execute(); db.prepareStatement(format("ALTER TABLE {0} RENAME TO {1}","items_temp",TABLE_ITEMS)).execute(); @@ -335,4 +312,27 @@ public class SqliteDb extends BaseDb implements StockDb { rs.close(); insert.execute(db).close(); } + + private void transformTables(){ + try { + db.setAutoCommit(false); + createIntermediateLocationTable(); + createIntermediateItemsTable(); + createIntermediatePropsTable(); + var oldLocationIdsToNew = transformLocations(); + transformItems(oldLocationIdsToNew); + transformProperties(); + replaceLocationsTable(); + replaceItemsTable(); + replaceItemPropsTable(); + db.setAutoCommit(true); + } catch (Exception e) { + try { + db.rollback(); + } catch (SQLException ignored) { + } + LOG.log(ERROR,"Failed to transform {0} table!",TABLE_LOCATIONS,e); + throw databaseException("Failed to transform {0} table!",TABLE_LOCATIONS); + } + } } 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 2ff5a72..9c258ba 100644 --- a/stock/src/main/java/de/srsoftware/umbrella/stock/StockDb.java +++ b/stock/src/main/java/de/srsoftware/umbrella/stock/StockDb.java @@ -1,7 +1,6 @@ /* © SRSoftware 2025 */ package de.srsoftware.umbrella.stock; -import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Company; import de.srsoftware.umbrella.core.model.Item; import de.srsoftware.umbrella.core.model.Location; @@ -11,12 +10,6 @@ import java.util.Collection; public interface StockDb { Collection listChildLocations(long parentId); Collection listCompanyLocations(Company company); - - Collection listItems(long companyId) throws UmbrellaException; - - Collection listItemsAt(long locationId); - - Collection listLocations(long companyId); - + Collection listItemsAt(long locationId); Collection listUserLocations(UmbrellaUser userId); } diff --git a/translations/src/main/resources/de.json b/translations/src/main/resources/de.json index d1260c2..b7103c0 100644 --- a/translations/src/main/resources/de.json +++ b/translations/src/main/resources/de.json @@ -130,6 +130,7 @@ "loading_object": "lade {object}…", "local_court": "Amtsgericht", "locality": "Ort", + "location": "Ort", "login" : "Anmeldung", "login_services": "Login-Services", "logout": "Abmelden",