refactoring object model

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
2025-10-16 01:07:50 +02:00
parent c6823b116c
commit 64d7e0cb37
14 changed files with 270 additions and 147 deletions

View File

@@ -7,8 +7,6 @@ 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;
@@ -17,6 +15,7 @@ import static java.text.MessageFormat.format;
import de.srsoftware.tools.Mappable;
import de.srsoftware.umbrella.core.BaseDb;
import de.srsoftware.umbrella.core.model.Location;
import de.srsoftware.umbrella.core.model.*;
import java.sql.Connection;
import java.sql.ResultSet;
@@ -195,11 +194,11 @@ public class SqliteDb extends BaseDb implements StockDb {
}
@Override
public Collection<Location> listChildLocations(long parentId) {
public Collection<DbLocation> listChildLocations(long parentId) {
try {
var rs = select(ALL).from(TABLE_LOCATIONS).where(PARENT_LOCATION_ID,equal(parentId)).exec(db);
var list = new ArrayList<Location>();
while (rs.next()) list.add(Location.of(rs));
var list = new ArrayList<DbLocation>();
while (rs.next()) list.add(DbLocation.of(rs));
rs.close();
return list;
} catch (SQLException e){
@@ -208,11 +207,11 @@ public class SqliteDb extends BaseDb implements StockDb {
}
@Override
public Collection<Location> listCompanyLocations(Company company) {
public Collection<DbLocation> 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<Location>();
while (rs.next()) list.add(Location.of(rs));
var list = new ArrayList<DbLocation>();
while (rs.next()) list.add(DbLocation.of(rs));
rs.close();
return list;
} catch (SQLException e){
@@ -221,41 +220,32 @@ public class SqliteDb extends BaseDb implements StockDb {
}
@Override
public Collection<Item> listItemsAt(long locationId) {
public Collection<Item> listItemsAt(Location location) {
try {
var location = loadLocation(locationId);
var rs = select(ALL).from(TABLE_ITEMS).where(LOCATION_ID,equal(locationId)).exec(db);
var rs = select(ALL).from(TABLE_ITEMS).where(LOCATION_ID,equal(location.id())).exec(db);
var list = new ArrayList<Item>();
var ownerMap = new HashMap<Long,Mappable>();
while (rs.next()) {
var ownerId = rs.getLong(OWNER);
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));
}
while (rs.next()) list.add(Item.of(rs));
rs.close();
for (var item : list){
var ownerId = item.ownerId();
var ownerId = item.owner();
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);
throw databaseException("Failed to load items at {0}",location);
}
}
@Override
public Item loadItem(Mappable owner, long itemId) {
public Item loadItem(long id) {
try {
var rs = select(ALL).from(TABLE_ITEMS).where(OWNER,equal(Item.ownerId(owner))).where(ID,equal(itemId)).exec(db);
var rs = select(ALL).from(TABLE_ITEMS).where(ID,equal(id)).exec(db);
Item result = null;
if (rs.next()) result = Item.of(rs,owner,null);
if (rs.next()) result = Item.of(rs);
rs.close();
if (result != null) return result;
} catch (SQLException ignored) {
@@ -263,11 +253,11 @@ public class SqliteDb extends BaseDb implements StockDb {
throw databaseException("Failed to load item");
}
private Location loadLocation(long locationId) {
public DbLocation loadLocation(long locationId) {
try {
var rs = select(ALL).from(TABLE_LOCATIONS).where(ID,equal(locationId)).exec(db);
Location loc = null;
if (rs.next()) loc = Location.of(rs);
DbLocation loc = null;
if (rs.next()) loc = DbLocation.of(rs);
rs.close();
if (loc != null) return loc;
throw databaseException("Failed to load location with id = {0}",locationId);
@@ -290,11 +280,11 @@ public class SqliteDb extends BaseDb implements StockDb {
}
@Override
public Collection<Location> listUserLocations(UmbrellaUser user) {
public Collection<DbLocation> listUserLocations(UmbrellaUser user) {
try {
var rs = select(ALL).from(TABLE_LOCATIONS).where(OWNER,equal(user.id())).where(PARENT_LOCATION_ID,isNull()).exec(db);
var list = new ArrayList<Location>();
while (rs.next()) list.add(Location.of(rs));
var list = new ArrayList<DbLocation>();
while (rs.next()) list.add(DbLocation.of(rs));
rs.close();
return list;
} catch (SQLException e){
@@ -324,7 +314,7 @@ public class SqliteDb extends BaseDb implements StockDb {
} else if (item.isDirty()) {
try {
var location = item.location();
var query = update(TABLE_ITEMS).where(OWNER, equal(item.ownerId())).where(ID, equal(item.id()));
var query = update(TABLE_ITEMS).where(ID, equal(item.id()));
if (location == null) {
query.set(CODE,NAME);
} else {

View File

@@ -1,18 +1,19 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.stock;
import de.srsoftware.tools.Mappable;
import de.srsoftware.umbrella.core.model.Location;
import de.srsoftware.umbrella.core.model.*;
import java.util.Collection;
public interface StockDb {
Property addNewProperty(long ownerId, long itemId, String name, Object value, String unit);
Collection<Location> listChildLocations(long parentId);
Collection<Location> listCompanyLocations(Company company);
Collection<Item> listItemsAt(long locationId);
Collection<DbLocation> listChildLocations(long parentId);
Collection<DbLocation> listCompanyLocations(Company company);
Collection<Item> listItemsAt(Location location);
Collection<Property> listProperties();
Collection<Location> listUserLocations(UmbrellaUser userId);
Item loadItem(Mappable owner, long itemId);
Collection<DbLocation> listUserLocations(UmbrellaUser userId);
Item loadItem(long id);
DbLocation loadLocation(long locationId);
Item save(Item item);
Property setProperty(long ownerId, long itemId, long existingPropId, Object value);
}

View File

@@ -19,6 +19,7 @@ import de.srsoftware.tools.Path;
import de.srsoftware.tools.SessionToken;
import de.srsoftware.umbrella.core.BaseHandler;
import de.srsoftware.umbrella.core.ModuleRegistry;
import de.srsoftware.umbrella.core.model.Location;
import de.srsoftware.umbrella.core.api.StockService;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.*;
@@ -49,7 +50,7 @@ public class StockModule extends BaseHandler implements StockService {
case LOCATION -> {
try {
var id = Long.parseLong(path.pop());
yield getLocation(user.get(),id,ex);
yield getLocation(user.get(), Location.of(id),ex);
} catch (Exception e){
yield super.doGet(path,ex);
}
@@ -95,12 +96,12 @@ public class StockModule extends BaseHandler implements StockService {
private boolean getChildLocations(UmbrellaUser user, long parentId, HttpExchange ex) throws IOException {
LOG.log(WARNING,"No security check implemented for {0}.getChildLocations(user, parentId, ex)!",getClass().getSimpleName()); // TODO check, that user is allowed to request that location
return sendContent(ex, stockDb.listChildLocations(parentId).stream().sorted(comparing(l -> l.name().toLowerCase())).map(Location::toMap));
return sendContent(ex, stockDb.listChildLocations(parentId).stream().sorted(comparing(l -> l.name().toLowerCase())).map(DbLocation::toMap));
}
private boolean getLocation(UmbrellaUser user, long locationId, HttpExchange ex) throws IOException {
return sendContent(ex, stockDb.listItemsAt(locationId).stream().map(Item::toMap).toList());
private boolean getLocation(UmbrellaUser user, Location location, HttpExchange ex) throws IOException {
return sendContent(ex, stockDb.listItemsAt(location).stream().map(Item::toMap).toList());
}
private boolean getLocations(Path path, UmbrellaUser user, HttpExchange ex) throws IOException {
@@ -130,7 +131,7 @@ public class StockModule extends BaseHandler implements StockService {
result.add(Map.of(
ID, user.id(),
NAME,user.name(),
LOCATIONS,userLocations.stream().map(Location::toMap).toList()));
LOCATIONS,userLocations.stream().map(DbLocation::toMap).toList()));
var companies = companyService().listCompaniesOf(user);
companies.values().stream().sorted(comparing(a -> a.name().toLowerCase())).forEach(company -> {
@@ -138,37 +139,27 @@ public class StockModule extends BaseHandler implements StockService {
result.add(Map.of(
ID, company.id(),
NAME,company.name(),
LOCATIONS,locations.stream().sorted(comparing(a -> a.name().toLowerCase())).map(Location::toMap).toList()));
LOCATIONS,locations.stream().sorted(comparing(a -> a.name().toLowerCase())).map(DbLocation::toMap).toList()));
});
return sendContent(ex, result);
}
@Override
public DbLocation loadLocation(long locationId) {
return stockDb.loadLocation(locationId);
}
private boolean patchItem(UmbrellaUser user, HttpExchange ex) throws IOException {
var json = json(ex);
if (!(json.get(ID) instanceof Number id)) throw missingFieldException(ID);
if (!(json.get(OWNER) instanceof JSONObject ownerRef)) throw missingFieldException(OWNER);
json.remove(ID);
json.remove(OWNER);
var owner = toOwner(ownerRef);
var item = stockDb.loadItem(owner,id.longValue());
var item = stockDb.loadItem(id.longValue());
item.patch(json);
return sendContent(ex,stockDb.save(item));
}
private Mappable toOwner(JSONObject owner) {
var keys = owner.keySet();
if (keys.size() != 1) throw unprocessable("{0} expected to have only one child!",OWNER);
String key = new ArrayList<>(keys).getFirst();
return switch (key) {
case COMPANY -> companyService().get(owner.getLong(key));
case USER -> userService().loadUser(owner.getLong(key));
default -> throw invalidFieldException(format("Single child of {0}", OWNER), format("either {0} or {1}", COMPANY, USER));
};
}
private boolean postProperty(UmbrellaUser user, HttpExchange ex) throws IOException {
var json = json(ex);
if (!(json.get(FIELD_ITEM) instanceof JSONObject itemData)) throw missingFieldException(FIELD_ITEM);
@@ -192,6 +183,18 @@ public class StockModule extends BaseHandler implements StockService {
return sendContent(ex,property);
}
private Mappable toOwner(JSONObject owner) {
var keys = owner.keySet();
if (keys.size() != 1) throw unprocessable("{0} expected to have only one child!",OWNER);
String key = new ArrayList<>(keys).getFirst();
return switch (key) {
case COMPANY -> companyService().get(owner.getLong(key));
case USER -> userService().loadUser(owner.getLong(key));
default -> throw invalidFieldException(format("Single child of {0}", OWNER), format("either {0} or {1}", COMPANY, USER));
};
}
private long toOwnerId(JSONObject owner) {
var keys = owner.keySet();
if (keys.size() != 1) throw unprocessable("{0} expected to have only one child!",OWNER);