overhauling constants, working on translations

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
2026-01-15 13:58:50 +01:00
parent 669853352e
commit 0d1cdd35d1
103 changed files with 2161 additions and 1207 deletions
@@ -9,10 +9,8 @@ public class Constants {
public static final String BELOW = "below";
public static final String CONFIG_DATABASE = "umbrella.modules.stock.database";
public static final String CONFIG_ITEM_DB = "umbrella.modules.items.database";
public static final String ITEM = "item";
public static final String ITEM_ID = "item_id";
public static final String ITEMS = "items";
public static final String LOCATIONS = "locations";
public static final String MOVE_ITEM = "move_item";
public static final String MOVE_LOCATION = "move_location";
public static final String OF_USER = "of_user";
@@ -4,16 +4,18 @@ package de.srsoftware.umbrella.stock;
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
import static de.srsoftware.tools.jdbc.Query.select;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
import static de.srsoftware.umbrella.core.Constants.*;
import static de.srsoftware.umbrella.core.Field.COMPANY_ID;
import static de.srsoftware.umbrella.core.Field.UNIT_PRICE;
import static de.srsoftware.umbrella.core.ModuleRegistry.companyService;
import static de.srsoftware.umbrella.core.ModuleRegistry.translator;
import static de.srsoftware.umbrella.core.constants.Field.*;
import static de.srsoftware.umbrella.core.constants.Text.T_UNIT_PRICE;
import static de.srsoftware.umbrella.core.constants.Text.UNIT;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException;
import static de.srsoftware.umbrella.stock.Constants.TABLE_ITEMS;
import static java.lang.System.Logger.Level.DEBUG;
import de.srsoftware.tools.Tuple;
import de.srsoftware.umbrella.core.constants.Field;
import de.srsoftware.umbrella.core.constants.Text;
import de.srsoftware.umbrella.core.model.*;
import java.sql.Connection;
import java.sql.SQLException;
@@ -36,7 +38,7 @@ public class ItemDb {
while (rs.next()){
var id = rs.getLong(ID);
var companyId = rs.getLong(COMPANY_ID);
var code = rs.getString(CODE);
var code = rs.getString(Field.CODE);
var name = rs.getString(NAME);
var description = rs.getString(DESCRIPTION);
var unit = rs.getString(UNIT);
@@ -73,8 +75,8 @@ public class ItemDb {
LOG.log(DEBUG, " using location: {0}",location.resolve().name());
var stockItem = new Item(0,company,0,location,code,name,description);
var props = stockItem.properties();
var keyUnitPrice = translator().translate(lang,UNIT_PRICE);
var keyUnit = translator().translate(lang,UNIT);
var keyUnitPrice = translator().translate(lang,T_UNIT_PRICE);
var keyUnit = translator().translate(lang, Text.UNIT);
var keyTax = translator().translate(lang,TAX_RATE);
var keyLegacyId = translator().translate(lang,"legacy_id");
props.add(new Property(0,keyUnitPrice,unitPrice/100d,company.currency()));
@@ -86,7 +88,7 @@ public class ItemDb {
}
rs.close();
} catch (SQLException e) {
throw databaseException("Failed to migrate items from itemDB to stockDB!");
throw databaseException("Failed to migrate items from ItemDB to stockDB!");
}
}
}
@@ -7,11 +7,18 @@ import static de.srsoftware.tools.jdbc.Condition.*;
import static de.srsoftware.tools.jdbc.Condition.like;
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.Errors.*;
import static de.srsoftware.umbrella.core.ModuleRegistry.noteService;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.notFound;
import static de.srsoftware.umbrella.core.constants.Field.*;
import static de.srsoftware.umbrella.core.constants.Field.LOCATIONS;
import static de.srsoftware.umbrella.core.constants.Field.TYPE;
import static de.srsoftware.umbrella.core.constants.Field.UNIT;
import static de.srsoftware.umbrella.core.constants.Text.*;
import static de.srsoftware.umbrella.core.constants.Text.ITEMS;
import static de.srsoftware.umbrella.core.constants.Text.LOCATION;
import static de.srsoftware.umbrella.core.constants.Text.PROPERTY;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.core.model.Translatable.t;
import static de.srsoftware.umbrella.stock.Constants.*;
import static java.lang.System.Logger.Level.*;
import static java.text.MessageFormat.format;
@@ -19,6 +26,8 @@ import static java.text.MessageFormat.format;
import de.srsoftware.tools.jdbc.Query;
import de.srsoftware.umbrella.core.BaseDb;
import de.srsoftware.umbrella.core.api.Owner;
import de.srsoftware.umbrella.core.constants.Field;
import de.srsoftware.umbrella.core.constants.Text;
import de.srsoftware.umbrella.core.model.*;
import de.srsoftware.umbrella.core.model.Location;
import java.sql.Connection;
@@ -35,7 +44,7 @@ public class SqliteDb extends BaseDb implements StockDb {
public String owner() {
var parts = id.split(":");
if (parts.length != 3) throw databaseException("Expected legacy location id to be of the form ss:dd:ss, encountered {0}!",id);
if (parts.length != 3) throw databaseException("Expected legacy location id to be of the form ss:dd:ss, encountered {ID}!", ID,id);
return String.join(":", parts[0], parts[1]);
}
};
@@ -48,26 +57,26 @@ public class SqliteDb extends BaseDb implements StockDb {
public Property addNewProperty(long itemId, String name, Object value, String unit) {
try {
db.setAutoCommit(false);
var rs = insertInto(TABLE_PROPERTIES,NAME,TYPE,UNIT).values(name,0,unit).execute(db).getGeneratedKeys();
var rs = insertInto(TABLE_PROPERTIES, NAME, TYPE,UNIT).values(name,0,unit).execute(db).getGeneratedKeys();
Long propertyId = null;
if (rs.next()) propertyId = rs.getLong(1);
rs.close();
if (propertyId == null || propertyId == 0) throw databaseException(FAILED_TO_STORE_ENTITY,"property");
if (propertyId == null || propertyId == 0) throw failedToStoreObject(t(PROPERTY));
insertInto(TABLE_ITEM_PROPERTIES,ITEM_ID,PROPERTY_ID,VALUE).values(itemId,propertyId,value).execute(db).close();
db.setAutoCommit(true);
return new Property(propertyId,name,value,unit);
} catch (SQLException e) {
throw databaseException(FAILED_TO_STORE_ENTITY,"property").causedBy(e);
throw failedToStoreObject(t(PROPERTY)).causedBy(e);
}
}
private void createDescriptionColumn(){
try {
var sql = "ALTER TABLE {0} ADD COLUMN {1} TEXT";
sql = format(sql,TABLE_ITEMS,DESCRIPTION);
sql = format(sql,TABLE_ITEMS, DESCRIPTION);
db.prepareStatement(sql).execute();
} catch (SQLException e) {
throw databaseException(FAILED_TO_ADD_COLUMN,DESCRIPTION,TABLE_ITEMS).causedBy(e);
throw databaseException(FAILED_TO_ADD_COLUMN, NAME, DESCRIPTION, TABLE,TABLE_ITEMS).causedBy(e);
}
}
@@ -84,7 +93,7 @@ public class SqliteDb extends BaseDb implements StockDb {
{3} VARCHAR(255),
{4} VARCHAR(255) NOT NULL,
{5} LONG NOT NULL)""";
sql = format(sql, ID, OWNER, OWNER_NUMBER, CODE, NAME, LOCATION_ID);
sql = format(sql, ID, OWNER, OWNER_NUMBER, Field.CODE, NAME, LOCATION_ID);
db.prepareStatement(sql).execute();
}
@@ -117,10 +126,10 @@ public class SqliteDb extends BaseDb implements StockDb {
private void createItemsTable() {
try {
var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} VARCHAR(255) PRIMARY KEY, {2} VARCHAR(255) NOT NULL, {3} TEXT, {4} VARCHAR(255))";
sql = format(sql, TABLE_ITEMS, ID, CODE, NAME, LOCATION_ID);
sql = format(sql, TABLE_ITEMS, ID, Field.CODE, NAME, LOCATION_ID);
db.prepareStatement(sql).execute();
} catch (SQLException e) {
throw databaseException(ERROR_FAILED_CREATE_TABLE,TABLE_ITEMS).causedBy(e);
throw failedToCreateTable(TABLE_ITEMS).causedBy(e);
}
}
@@ -130,7 +139,7 @@ public class SqliteDb extends BaseDb implements StockDb {
sql = format(sql, TABLE_ITEM_PROPERTIES, ITEM_ID, PROPERTY_ID,VALUE);
db.prepareStatement(sql).execute();
} catch (SQLException e) {
throw databaseException(ERROR_FAILED_CREATE_TABLE,TABLE_ITEM_PROPERTIES).causedBy(e);
throw failedToCreateTable(TABLE_ITEM_PROPERTIES).causedBy(e);
}
}
@@ -140,7 +149,7 @@ public class SqliteDb extends BaseDb implements StockDb {
sql = format(sql, TABLE_LOCATIONS, ID, LOCATION_ID, NAME, DESCRIPTION);
db.prepareStatement(sql).execute();
} catch (SQLException e) {
throw databaseException(ERROR_FAILED_CREATE_TABLE,TABLE_LOCATIONS).causedBy(e);
throw failedToCreateTable(TABLE_LOCATIONS).causedBy(e);
}
}
@@ -150,7 +159,7 @@ public class SqliteDb extends BaseDb implements StockDb {
sql = format(sql, TABLE_PROPERTIES, ID, NAME, TYPE, UNIT);
db.prepareStatement(sql).execute();
} catch (SQLException e) {
throw databaseException(ERROR_FAILED_CREATE_TABLE,TABLE_PROPERTIES).causedBy(e);
throw failedToCreateTable(TABLE_PROPERTIES).causedBy(e);
}
}
@@ -179,7 +188,7 @@ public class SqliteDb extends BaseDb implements StockDb {
Query.delete().from(TABLE_LOCATIONS).where(ID,equal(location.id())).execute(db);
return location;
} catch (SQLException e){
throw databaseException(FAILED_TO_DROP_ENTITY,location.name()).causedBy(e);
throw failedToDropObject(location.name()).causedBy(e);
}
}
@@ -187,7 +196,7 @@ public class SqliteDb extends BaseDb implements StockDb {
try {
db.prepareStatement("DROP TABLE IF EXISTS tokens").execute();
} catch (SQLException e) {
throw databaseException(FAILED_TO_DROP_ENTITY,"table tokens").causedBy(e);
throw failedToDropObject("table tokens").causedBy(e);
}
}
@@ -199,7 +208,7 @@ public class SqliteDb extends BaseDb implements StockDb {
var query = select(ALL).from(TABLE_ITEMS).where(OWNER, in(ownerCodes));
if (fulltext) {
query.leftJoin(ID,TABLE_ITEM_PROPERTIES,ITEM_ID);
for (var key : keys) query.where(format("CONCAT({0},\" \",{1},\" \",{2})",NAME,DESCRIPTION,VALUE),like("%"+key+"%"));
for (var key : keys) query.where(format("CONCAT({0},\" \",{1},\" \",{2})", NAME, DESCRIPTION,VALUE),like("%"+key+"%"));
} else {
for (var key : keys) query.where(NAME,like("%"+key+"%"));
}
@@ -211,7 +220,7 @@ public class SqliteDb extends BaseDb implements StockDb {
rs.close();
return items;
} catch (SQLException e) {
throw databaseException(FAILED_TO_LIST_ENTITIES,ITEMS).causedBy(e);
throw databaseException(FAILED_TO_LIST_ENTITIES, TYPE,t(ITEMS)).causedBy(e);
}
}
@@ -224,7 +233,7 @@ public class SqliteDb extends BaseDb implements StockDb {
rs.close();
return list;
} catch (SQLException e){
throw databaseException(FAILED_TO_LOAD_CHILD_LOCATIONS,parentId).causedBy(e);
throw databaseException(FAILED_TO_LOAD_CHILD_LOCATIONS,PARENT,parentId).causedBy(e);
}
}
@@ -237,7 +246,7 @@ public class SqliteDb extends BaseDb implements StockDb {
rs.close();
return list;
} catch (SQLException e){
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER,"locations",company.name()).causedBy(e);
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER, TYPE,t(Text.LOCATIONS), OWNER,company.name()).causedBy(e);
}
}
@@ -252,7 +261,7 @@ public class SqliteDb extends BaseDb implements StockDb {
for (var item : list) loadProperties(item);
return list;
} catch (SQLException e){
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER,ITEMS,location).causedBy(e);
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER, TYPE,ITEMS, OWNER,location).causedBy(e);
}
}
@@ -268,7 +277,7 @@ public class SqliteDb extends BaseDb implements StockDb {
for (var item : list) loadProperties(item);
return list;
} catch (SQLException e){
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER,ITEMS,company).causedBy(e);
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER, TYPE,ITEMS, OWNER,company).causedBy(e);
}
}
@@ -280,7 +289,7 @@ public class SqliteDb extends BaseDb implements StockDb {
rs.close();
return item;
} catch (SQLException e){
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER,PROPERTIES,item.name()).causedBy(e);
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER, TYPE,t(Text.PROPERTIES), OWNER,item.name()).causedBy(e);
}
}
@@ -303,9 +312,9 @@ public class SqliteDb extends BaseDb implements StockDb {
if (rs.next()) result = Item.of(rs);
rs.close();
if (result != null) return result;
throw notFound(FAILED_TO_LOAD_ENTITY,ITEM);
throw notFound(FAILED_TO_LOAD_OBJECT, OBJECT,t(Text.ITEM));
} catch (SQLException e) {
throw databaseException(FAILED_TO_LOAD_ENTITY,ITEM).causedBy(e);
throw failedToLoadObject(t(Text.ITEM)).causedBy(e);
}
}
@@ -316,9 +325,9 @@ public class SqliteDb extends BaseDb implements StockDb {
if (rs.next()) loc = DbLocation.of(rs);
rs.close();
if (loc != null) return loc;
throw notFound(FAILED_TO_LOAD_ENTITY_BY_ID,LOCATION,locationId);
throw failedToLoadObject(t(LOCATION),locationId);
} catch (SQLException e){
throw databaseException(FAILED_TO_LOAD_ENTITY_BY_ID,LOCATION,locationId).causedBy(e);
throw failedToLoadObject(t(LOCATION),locationId).causedBy(e);
}
}
@@ -331,7 +340,7 @@ public class SqliteDb extends BaseDb implements StockDb {
rs.close();
return list;
} catch (SQLException e){
throw databaseException(FAILED_TO_LOAD_ENTITY,PROPERTIES).causedBy(e);
throw failedToLoadObject(t(Text.PROPERTIES)).causedBy(e);
}
}
@@ -344,7 +353,7 @@ public class SqliteDb extends BaseDb implements StockDb {
rs.close();
return list;
} catch (SQLException e){
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER,LOCATIONS,user.name()).causedBy(e);
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER, TYPE,t(Text.LOCATIONS), OWNER,user.name()).causedBy(e);
}
}
@@ -356,7 +365,7 @@ public class SqliteDb extends BaseDb implements StockDb {
rs.close();
return number +1L;
} catch (SQLException e) {
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER,"last item number",owner).causedBy(e);
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER, TYPE,"last item number", OWNER,owner).causedBy(e);
}
}
@@ -379,7 +388,7 @@ public class SqliteDb extends BaseDb implements StockDb {
location = parent;
}
} catch (SQLException e){
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER,"path",target).causedBy(e);
throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER, TYPE,t(Text.PATH), OWNER,target).causedBy(e);
}
return root;
}
@@ -404,16 +413,16 @@ public class SqliteDb extends BaseDb implements StockDb {
var parentId = is0(location.parent()) ? null : location.parent();
if (location.id() == 0) { // new location
try {
var rs = insertInto(TABLE_LOCATIONS,OWNER,PARENT_LOCATION_ID,NAME,DESCRIPTION)
var rs = insertInto(TABLE_LOCATIONS, OWNER, PARENT_LOCATION_ID, NAME, DESCRIPTION)
.values(location.owner().dbCode(),parentId,location.name(),location.description())
.execute(db).getGeneratedKeys();
long id = 0;
if (rs.next()) id = rs.getLong(1);
rs.close();
if (id == 0) throw databaseException(FAILED_TO_STORE_ENTITY,location.name());
if (id == 0) throw failedToStoreObject(location.name());
return location.id(id);
} catch (SQLException e){
throw databaseException(FAILED_TO_STORE_ENTITY,location.name()).causedBy(e);
throw failedToStoreObject(location.name()).causedBy(e);
}
} else {
try {
@@ -425,7 +434,7 @@ public class SqliteDb extends BaseDb implements StockDb {
.close();
return location.clear();
} catch (SQLException e){
throw databaseException(FAILED_TO_UPDATE_ENTITY,location.name()).causedBy(e);
throw databaseException(FAILED_TO_UPDATE_OBJECT, OBJECT,location.name()).causedBy(e);
}
}
}
@@ -435,22 +444,22 @@ public class SqliteDb extends BaseDb implements StockDb {
if (item.id() == 0){
var number = nextItemNumberFor(item.location().resolve().owner());
try {
var rs = insertInto(TABLE_ITEMS, OWNER, OWNER_NUMBER, CODE, NAME, DESCRIPTION, LOCATION_ID)
var rs = insertInto(TABLE_ITEMS, OWNER, OWNER_NUMBER, Field.CODE, NAME, DESCRIPTION, LOCATION_ID)
.values(item.owner().dbCode(), number, item.code(), item.name(), item.description(), item.location().id())
.execute(db).getGeneratedKeys();
if (rs.next()) item.id(rs.getLong(1)).ownerNumber(number);
rs.close();
} catch (SQLException e) {
throw databaseException(FAILED_TO_STORE_ENTITY,item.name()).causedBy(e);
throw failedToStoreObject(item.name()).causedBy(e);
}
} else if (item.isDirty()) {
try {
var location = item.location();
var query = update(TABLE_ITEMS).where(ID, equal(item.id()));
if (location == null) {
query.set(CODE,NAME,DESCRIPTION);
query.set(Field.CODE, NAME, DESCRIPTION);
} else {
query.set(CODE,NAME,DESCRIPTION,LOCATION_ID);
query.set(Field.CODE, NAME, DESCRIPTION, LOCATION_ID);
}
var pq = query.prepare(db);
if (location == null) {
@@ -460,7 +469,7 @@ public class SqliteDb extends BaseDb implements StockDb {
}
item.clear();
} catch (SQLException e){
throw databaseException(FAILED_TO_UPDATE_ENTITY,item.name()).causedBy(e);
throw databaseException(FAILED_TO_UPDATE_OBJECT, OBJECT,item.name()).causedBy(e);
}
}
saveProperties(item);
@@ -483,7 +492,7 @@ public class SqliteDb extends BaseDb implements StockDb {
if (rs.next()) propId = rs.getLong(1);
rs.close();
} catch (SQLException e) {
throw databaseException(FAILED_TO_LOAD_ENTITY,property.name()).causedBy(e);
throw failedToLoadObject(property.name()).causedBy(e);
}
}
if (is0(propId)) return addNewProperty(item.id(), property.name(), property.value(), property.unit());
@@ -497,7 +506,7 @@ public class SqliteDb extends BaseDb implements StockDb {
var rs = select(ALL).from(TABLE_PROPERTIES).where(ID,equal(existingPropId)).exec(db);
if (rs.next()) prop = Property.of(rs);
rs.close();
if (prop == null) throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER,"property "+existingPropId,"item "+itemId);
if (prop == null) throw databaseException(FAILED_TO_LOAD_ENTITIES_OF_OWNER, TYPE,"property "+existingPropId, OWNER,"item "+itemId);
if ("".equals(value)){
Query.delete().from(TABLE_ITEM_PROPERTIES).where(ITEM_ID,equal(itemId)).where(PROPERTY_ID,equal(existingPropId)).execute(db);
} else {
@@ -505,30 +514,30 @@ public class SqliteDb extends BaseDb implements StockDb {
}
return prop.value(value);
} catch (SQLException e) {
throw databaseException(FAILED_TO_ADD_PROPERTY_TO_ITEM,itemId).causedBy(e);
throw databaseException(FAILED_TO_ADD_PROPERTY_TO_ITEM, OBJECT,itemId).causedBy(e);
}
}
private HashMap<String, Long> transformItems(Map<String, Long> oldLocationIdsToNew) throws SQLException {
var rs = select(ALL).from(TABLE_ITEMS).exec(db);
var insert = insertInto("items_temp",OWNER, OWNER_NUMBER, LOCATION_ID, CODE, NAME);
var insert = insertInto("items_temp", OWNER, OWNER_NUMBER, LOCATION_ID, Field.CODE, NAME);
var oldToNew = new HashMap<String,Long>(); // maps from old item ids to new ones
while (rs.next()){
var oldId = rs.getString(ID);
var parts = oldId.split(":");
if (parts.length != 3) throw databaseException(UNEXPECTED_ITEM_ID_FORMAT,oldId);
if (parts.length != 3) throw databaseException(UNEXPECTED_ITEM_ID_FORMAT,OBJECT,oldId);
var owner = String.join(":",parts[0], parts[1]);
long ownerNumber;
try {
ownerNumber = Long.parseLong(parts[2]);
} catch (NumberFormatException e) {
throw databaseException(UNEXPECTED_ITEM_ID_FORMAT,oldId).causedBy(e);
throw databaseException(UNEXPECTED_ITEM_ID_FORMAT,OBJECT,oldId).causedBy(e);
}
var oldLocationId = rs.getString(LOCATION_ID);
var locationId = oldLocationIdsToNew.get(oldLocationId);
if (locationId == null) throw databaseException(UNKNOWN_ITEM_LOCATION,oldId,parts[0],parts[1],oldLocationId);
var rs2 = insert.values(owner, ownerNumber, locationId, rs.getString(CODE), rs.getString(NAME)).execute(db).getGeneratedKeys();
if (locationId == null) throw databaseException(UNKNOWN_ITEM_LOCATION, OBJECT,oldId, OWNER,parts[0]+" "+parts[1], Field.LOCATION,oldLocationId);
var rs2 = insert.values(owner, ownerNumber, locationId, rs.getString(Field.CODE), rs.getString(NAME)).execute(db).getGeneratedKeys();
oldToNew.put(oldId,rs2.getLong(1));
rs2.close();
}
@@ -572,7 +581,7 @@ public class SqliteDb extends BaseDb implements StockDb {
while (rs.next()){
var oldItemId = rs.getString(ITEM_ID);
var itemId = oldItemIdsToNew.get(oldItemId);
if (itemId == null) throw databaseException(MISSING_NEW_ITEM_ID,oldItemId);
if (itemId == null) throw databaseException(MISSING_NEW_ITEM_ID, ID,oldItemId);
insert.values(itemId, rs.getLong(PROPERTY_ID), rs.getString(VALUE));
}
rs.close();
@@ -598,7 +607,7 @@ public class SqliteDb extends BaseDb implements StockDb {
db.rollback();
} catch (SQLException ignored) {
}
throw databaseException(FAILED_TO_UPDATE_TABLE,TABLE_LOCATIONS).causedBy(e);
throw databaseException(FAILED_TO_UPDATE_OBJECT, OBJECT, t(TABLE_WITH_NAME,NAME,TABLE_LOCATIONS)).causedBy(e);
}
}
@@ -4,13 +4,15 @@ package de.srsoftware.umbrella.stock;
import static de.srsoftware.tools.Optionals.is0;
import static de.srsoftware.tools.Optionals.nullIfEmpty;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
import static de.srsoftware.umbrella.core.Constants.*;
import static de.srsoftware.umbrella.core.Field.ITEM;
import static de.srsoftware.umbrella.core.ModuleRegistry.companyService;
import static de.srsoftware.umbrella.core.ModuleRegistry.userService;
import static de.srsoftware.umbrella.core.Paths.LIST;
import static de.srsoftware.umbrella.core.Paths.SEARCH;
import static de.srsoftware.umbrella.core.Util.mapValues;
import static de.srsoftware.umbrella.core.constants.Field.*;
import static de.srsoftware.umbrella.core.constants.Field.ITEM;
import static de.srsoftware.umbrella.core.constants.Module.COMPANY;
import static de.srsoftware.umbrella.core.constants.Module.USER;
import static de.srsoftware.umbrella.core.constants.Path.*;
import static de.srsoftware.umbrella.core.constants.Path.PROPERTY;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.stock.Constants.*;
import static java.lang.System.Logger.Level.WARNING;
@@ -19,12 +21,12 @@ import static java.util.Comparator.comparing;
import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.configuration.Configuration;
import de.srsoftware.configuration.JsonConfig;
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.*;
import de.srsoftware.umbrella.core.api.Owner;
import de.srsoftware.umbrella.core.api.StockService;
import de.srsoftware.umbrella.core.constants.Field;
import de.srsoftware.umbrella.core.constants.Path;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.*;
import de.srsoftware.umbrella.core.model.Location;
@@ -39,7 +41,7 @@ public class StockModule extends BaseHandler implements StockService {
public StockModule(Configuration config) throws UmbrellaException {
super();
var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE));
var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingField(CONFIG_DATABASE));
stockDb = new SqliteDb(connect(dbFile));
Optional<String> itemDbConfig = config.get(CONFIG_ITEM_DB);
itemDbConfig.map(ItemDb::new).ifPresent(itemDb -> itemDb.migrateTo(stockDb));
@@ -64,14 +66,14 @@ public class StockModule extends BaseHandler implements StockService {
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);
if (!assigned(owner,user)) throw forbidden("You are not allowed to modify \"{location}\"", Field.LOCATION,location);
if (!stockDb.listItemsAt(location).isEmpty()) throw forbidden("\"{location}\" cannot be deleted, as it contains items!", Field.LOCATION,location);
if (!stockDb.listChildLocations(location.id()).isEmpty()) throw forbidden("\"{location}\" cannot be deleted, as it contains other locations!", Field.LOCATION,location);
return sendContent(ex,stockDb.delete(location));
}
@Override
public boolean doDelete(Path path, HttpExchange ex) throws IOException {
public boolean doDelete(de.srsoftware.tools.Path path, HttpExchange ex) throws IOException {
addCors(ex);
try {
Optional<Token> token = SessionToken.from(ex).map(Token::of);
@@ -79,7 +81,7 @@ public class StockModule extends BaseHandler implements StockService {
if (user.isEmpty()) return unauthorized(ex);
var head = path.pop();
return switch (head) {
case LOCATION -> {
case Path.LOCATION -> {
try {
var location = Location.of(Long.parseLong(path.pop()));
yield deleteLocation(user.get(), location, ex);
@@ -95,7 +97,7 @@ public class StockModule extends BaseHandler implements StockService {
}
@Override
public boolean doGet(Path path, HttpExchange ex) throws IOException {
public boolean doGet(de.srsoftware.tools.Path path, HttpExchange ex) throws IOException {
addCors(ex);
try {
Optional<Token> token = SessionToken.from(ex).map(Token::of);
@@ -103,7 +105,7 @@ public class StockModule extends BaseHandler implements StockService {
if (user.isEmpty()) return unauthorized(ex);
var head = path.pop();
return switch (head) {
case COMPANY -> {
case Path.COMPANY -> {
try {
var company = companyService().get(Long.parseLong(path.pop()));
yield getItem(user.get(),company,path,ex);
@@ -111,7 +113,7 @@ public class StockModule extends BaseHandler implements StockService {
yield super.doGet(path,ex);
}
}
case LOCATION -> {
case Path.LOCATION -> {
try {
var location = Location.of(Long.parseLong(path.pop()));
yield getLocationEntities(location, ex);
@@ -119,9 +121,9 @@ public class StockModule extends BaseHandler implements StockService {
yield super.doGet(path,ex);
}
}
case LOCATIONS -> getLocations(path,user.get(),ex);
case PROPERTIES -> getProperties(ex);
case USER -> {
case Path.LOCATIONS -> getLocations(path,user.get(),ex);
case Path.PROPERTIES -> getProperties(ex);
case Path.USER -> {
try {
var userId = Long.parseLong(path.pop());
if (userId != user.get().id()) throw forbidden("You are not allowed to access items of another user!");
@@ -138,7 +140,7 @@ public class StockModule extends BaseHandler implements StockService {
}
@Override
public boolean doPatch(Path path, HttpExchange ex) throws IOException {
public boolean doPatch(de.srsoftware.tools.Path path, HttpExchange ex) throws IOException {
addCors(ex);
try {
Optional<Token> token = SessionToken.from(ex).map(Token::of);
@@ -146,7 +148,7 @@ public class StockModule extends BaseHandler implements StockService {
if (user.isEmpty()) return unauthorized(ex);
return switch (path.pop()){
case MOVE_ITEM -> patchMoveItem(user.get(), path,ex);
case LOCATION -> {
case Path.LOCATION -> {
try {
var id = Long.parseLong(path.pop());
yield patchLocation(id, user.get(), ex);
@@ -164,7 +166,7 @@ public class StockModule extends BaseHandler implements StockService {
}
@Override
public boolean doPost(Path path, HttpExchange ex) throws IOException {
public boolean doPost(de.srsoftware.tools.Path path, HttpExchange ex) throws IOException {
addCors(ex);
try {
Optional<Token> token = SessionToken.from(ex).map(Token::of);
@@ -172,9 +174,9 @@ public class StockModule extends BaseHandler implements StockService {
if (user.isEmpty()) return unauthorized(ex);
var head = path.pop();
return switch (head) {
case ITEM -> postItem(user.get(), ex);
case Path.ITEM -> postItem(user.get(), ex);
case LIST -> postItemList(user.get(), path, ex);
case LOCATION -> postLocation(user.get(),ex);
case Path.LOCATION -> postLocation(user.get(),ex);
case PROPERTY -> postProperty(user.get(),ex);
case SEARCH -> postSearch(user.get(),ex);
case null, default -> super.doPost(path,ex);
@@ -189,10 +191,10 @@ public class StockModule extends BaseHandler implements StockService {
return sendContent(ex, stockDb.listChildLocations(parentId).stream().sorted(comparing(l -> l.name().toLowerCase())).map(DbLocation::toMap));
}
private boolean getItem(UmbrellaUser user, Owner owner, Path path, HttpExchange ex) throws IOException {
if (!assigned(owner,user)) throw forbidden("You are not allowed to access items of {0}",owner);
private boolean getItem(UmbrellaUser user, Owner owner, de.srsoftware.tools.Path path, HttpExchange ex) throws IOException {
if (!assigned(owner,user)) throw forbidden("You are not allowed to access items of {owner}", OWNER,owner);
return switch (path.pop()){
case ITEM -> {
case Path.ITEM -> {
try {
var itemId = Long.parseLong(path.pop());
var item = stockDb.loadItem(owner.dbCode(),itemId);
@@ -211,13 +213,13 @@ public class StockModule extends BaseHandler implements StockService {
List<Long> userIds = switch (owner.type()){
case COMPANY -> companyService().getMembers(owner.id()).stream().map(UmbrellaUser::id).toList();
case USER -> List.of(owner.id());
case null, default -> throw unprocessable("Unprocessable owner type: {0}",owner.type());
case null, default -> throw unprocessable("Unprocessable owner type: {type}", TYPE,owner.type());
};
var pathToLocation = stockDb.pathToLocation(location);
return sendContent(ex,Map.of(ITEMS,items,USERS,userIds,PATH,pathToLocation,LOCATION,location.resolve().toMap()));
return sendContent(ex,Map.of(ITEMS,items,USERS,userIds, PATH,pathToLocation, Field.LOCATION,location.resolve().toMap()));
}
private boolean getLocations(Path path, UmbrellaUser user, HttpExchange ex) throws IOException {
private boolean getLocations(de.srsoftware.tools.Path path, UmbrellaUser user, HttpExchange ex) throws IOException {
var head = path.pop();
return switch (head){
case BELOW -> {
@@ -242,17 +244,17 @@ public class StockModule extends BaseHandler implements StockService {
var result = new ArrayList<Object>();
var userLocations = stockDb.listUserLocations(user);
result.add(Map.of(
PARENT, Map.of(USER, user.id()),
PARENT, Map.of(Field.USER, user.id()),
NAME,user.name(),
LOCATIONS,userLocations.stream().map(DbLocation::toMap).toList()));
Field.LOCATIONS,userLocations.stream().map(DbLocation::toMap).toList()));
var companies = companyService().listCompaniesOf(user);
companies.values().stream().sorted(comparing(a -> a.name().toLowerCase())).forEach(company -> {
var locations = stockDb.listCompanyLocations(company);
result.add(Map.of(
PARENT, Map.of(COMPANY, company.id()),
PARENT, Map.of(Field.COMPANY, company.id()),
NAME,company.name(),
LOCATIONS,locations.stream().sorted(comparing(a -> a.name().toLowerCase())).map(DbLocation::toMap).toList()));
Field.LOCATIONS,locations.stream().sorted(comparing(a -> a.name().toLowerCase())).map(DbLocation::toMap).toList()));
});
return sendContent(ex, result);
@@ -265,7 +267,7 @@ public class StockModule extends BaseHandler implements StockService {
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(ID) instanceof Number id)) throw missingField(ID);
json.remove(ID);
var item = stockDb.loadItem(id.longValue());
@@ -273,20 +275,20 @@ public class StockModule extends BaseHandler implements StockService {
return sendContent(ex,stockDb.save(item));
}
private boolean patchMoveItem(UmbrellaUser user, Path path, HttpExchange ex) throws IOException {
private boolean patchMoveItem(UmbrellaUser user, de.srsoftware.tools.Path path, HttpExchange ex) throws IOException {
var json = json(ex);
if (!(json.get(ITEM) instanceof Number itemId)) throw missingFieldException(ITEM);
if (!(json.get(TARGET) instanceof Number locationId)) throw missingFieldException(TARGET);
if (!(json.get(ITEM) instanceof Number itemId)) throw missingField(ITEM);
if (!(json.get(TARGET) instanceof Number locationId)) throw missingField(TARGET);
var item = stockDb.loadItem(itemId.longValue());
var itemOwner = item.owner().resolve();
if (!assigned(itemOwner,user)) throw forbidden("You are not allowed to alter the location of \"{0}\"!",item.name());
if (!assigned(itemOwner,user)) throw forbidden("You are not allowed to alter the location of \"{name}\"!", NAME,item.name());
var target = stockDb.loadLocation(locationId.longValue());
var locOwner = target.resolve().owner().resolve();
if (!assigned(locOwner,user)) throw forbidden("You are not allowed to modify \"{0}\"!",target.name());
if (!assigned(locOwner,user)) throw forbidden("You are not allowed to modify \"{name}\"!", NAME,target.name());
if (!locOwner.equals(itemOwner)) throw unprocessable("You may not move items from one owner ({0}) to another ({1})",itemOwner,locOwner);
if (!locOwner.equals(itemOwner)) throw unprocessable("You may not move items from one owner ({owner}) to another ({object})", OWNER,itemOwner, OBJECT,locOwner);
stockDb.save(item.location(target));
return sendContent(ex,item);
@@ -296,7 +298,7 @@ public class StockModule extends BaseHandler implements StockService {
var json = json(ex);
var location = stockDb.loadLocation(locationId);
var owner = location.owner().resolve();
if (!assigned(owner,user)) throw forbidden("You are not allowed to edit \"{0}\"!",location.name());
if (!assigned(owner,user)) throw forbidden("You are not allowed to edit \"{location}\"!", Field.LOCATION,location.name());
if (json.has(PARENT_LOCATION_ID) && json.get(PARENT_LOCATION_ID) instanceof Number parentId){
if (parentId.longValue() != 0L) {
@@ -308,8 +310,8 @@ public class StockModule extends BaseHandler implements StockService {
current = is0(current.parent()) ? null : stockDb.loadLocation(current.parent());
}
var targetOwner = target.owner().resolve();
if (!assigned(targetOwner, user)) throw forbidden("You are not allowed to edit \"{0}\"!", target.name());
if (!targetOwner.equals(owner)) throw unprocessable("You may not move locations from one owner ({0}) to another ({1})", owner, targetOwner);
if (!assigned(targetOwner, user)) throw forbidden("You are not allowed to edit \"{name}\"!", NAME, target.name());
if (!targetOwner.equals(owner)) throw unprocessable("You may not move locations from one owner ({a}) to another ({b})","a", owner,"b", targetOwner);
LOG.log(WARNING,"Not checking, if location is moved to on of its own children!");
}
}
@@ -320,22 +322,22 @@ public class StockModule extends BaseHandler implements StockService {
private boolean postItem(UmbrellaUser user, HttpExchange ex) throws IOException {
var json = json(ex);
if (!json.has(NAME) || !(json.get(NAME) instanceof String name)) throw missingFieldException(NAME);
if (!json.has(NAME) || !(json.get(NAME) instanceof String name)) throw missingField(NAME);
var description = json.has(DESCRIPTION) && json.get(DESCRIPTION) instanceof String d ? d : null;
if (!json.has(CODE) || !(json.get(CODE) instanceof String code)) throw missingFieldException(CODE);
if (!json.has(LOCATION) || !(json.get(LOCATION) instanceof JSONObject locationData)) throw missingFieldException(LOCATION);
if (!json.has(Field.CODE) || !(json.get(Field.CODE) instanceof String code)) throw missingField(Field.CODE);
if (!json.has(Field.LOCATION) || !(json.get(Field.LOCATION) instanceof JSONObject locationData)) throw missingField(Field.LOCATION);
var location = stockDb.loadLocation(locationData.getLong(ID));
var owner = location.owner().resolve();
if (!assigned(owner,user)) throw forbidden("You are not allowed to add items to {0}!",location);
if (!assigned(owner,user)) throw forbidden("You are not allowed to add items to {location}!", Field.LOCATION,location);
var newItem = new Item(0,owner,0,location,code,name,description);
return sendContent(ex,stockDb.save(newItem));
}
private boolean postItemList(UmbrellaUser user, Path path, HttpExchange ex) throws IOException {
private boolean postItemList(UmbrellaUser user, de.srsoftware.tools.Path path, HttpExchange ex) throws IOException {
var json = json(ex);
if (!json.has(COMPANY_ID) || !(json.get(COMPANY_ID) instanceof Number company_id)) throw missingFieldException(COMPANY_ID);
if (!json.has(COMPANY_ID) || !(json.get(COMPANY_ID) instanceof Number company_id)) throw missingField(COMPANY_ID);
var company = companyService().get(company_id.longValue());
if (!companyService().membership(company_id.longValue(),user.id())) throw forbidden("You are not a member of {0}!", company.name());
if (!companyService().membership(company_id.longValue(),user.id())) throw notAmember(company.name());
var map = new HashMap<Long,Location>();
var items = stockDb.listItemsOf(company)
.stream()
@@ -346,10 +348,10 @@ public class StockModule extends BaseHandler implements StockService {
private boolean postLocation(UmbrellaUser user, HttpExchange ex) throws IOException {
var json = json(ex);
if (!(json.get(NAME) instanceof String name)) throw missingFieldException(NAME);
if (!(json.get(PARENT) instanceof JSONObject parentData)) throw missingFieldException(PARENT);
var key = parentData.keySet().stream().findFirst().orElseThrow(() -> missingFieldException(PARENT));
if (!(parentData.get(key) instanceof Number id)) throw missingFieldException(key);
if (!(json.get(NAME) instanceof String name)) throw missingField(NAME);
if (!(json.get(PARENT) instanceof JSONObject parentData)) throw missingField(PARENT);
var key = parentData.keySet().stream().findFirst().orElseThrow(() -> missingField(PARENT));
if (!(parentData.get(key) instanceof Number id)) throw missingField(key);
Location parent;
Owner owner;
switch (key){
@@ -361,11 +363,11 @@ public class StockModule extends BaseHandler implements StockService {
owner = userService().loadUser(id.longValue());
parent = null;
break;
case LOCATION:
case Path.LOCATION:
parent = stockDb.loadLocation(id.longValue());
owner = parent.resolve().owner().resolve();
break;
default: throw unprocessable("Unknown parent object: {0} → {1}",key,id);
default: throw unprocessable("Unknown parent object: {key} → {id}", KEY,key, ID,id);
};
var loc = new DbLocation(0,owner,parent == null?null:parent.id(),name,null);
return sendContent(ex,stockDb.save(loc));
@@ -373,12 +375,12 @@ public class StockModule extends BaseHandler implements StockService {
private boolean postProperty(UmbrellaUser user, HttpExchange ex) throws IOException {
var json = json(ex);
if (!(json.get(ITEM) instanceof JSONObject itemData)) throw missingFieldException(ITEM);
if (!(itemData.get(ID) instanceof Number itemId)) throw missingFieldException(ID);
if (!(json.get("add_prop") instanceof JSONObject propData)) throw missingFieldException("add_prop");
if (!propData.has(VALUE)) throw missingFieldException(VALUE);
if (!(json.get(ITEM) instanceof JSONObject itemData)) throw missingField(ITEM);
if (!(itemData.get(ID) instanceof Number itemId)) throw missingField(ID);
if (!(json.get("add_prop") instanceof JSONObject propData)) throw missingField("add_prop");
if (!propData.has(VALUE)) throw missingField(VALUE);
var value = propData.get(VALUE);
if (value == null) throw missingFieldException(VALUE);
if (value == null) throw missingField(VALUE);
Property property = null;
if (propData.get("existing_prop_id") instanceof Number existingPropId && existingPropId.longValue() != 0L){
@@ -394,7 +396,7 @@ public class StockModule extends BaseHandler implements StockService {
private boolean postSearch(UmbrellaUser user, HttpExchange ex) throws IOException {
var json = json(ex);
if (!(json.has(KEY) && json.get(KEY) instanceof String key)) throw missingFieldException(KEY);
if (!(json.has(KEY) && json.get(KEY) instanceof String key)) throw missingField(KEY);
var keys = Arrays.asList(key.split(" "));
var fulltext = json.has(FULLTEXT) && json.get(FULLTEXT) instanceof Boolean val && val;
Set<Owner> owners = new HashSet<>(companyService().listCompaniesOf(user).values());