21 changed files with 209 additions and 189 deletions
			
			
		@ -1,10 +0,0 @@
				@@ -1,10 +0,0 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.core.api; | 
				
			||||
 | 
				
			||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; | 
				
			||||
import de.srsoftware.umbrella.core.model.Item; | 
				
			||||
import java.util.Collection; | 
				
			||||
 | 
				
			||||
public interface ItemService { | 
				
			||||
	Collection<Item> list(long companyId) throws UmbrellaException; | 
				
			||||
} | 
				
			||||
@ -0,0 +1,15 @@
				@@ -0,0 +1,15 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.core.api; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.util.Collection; | 
				
			||||
 | 
				
			||||
public interface StockService { | 
				
			||||
	/** | 
				
			||||
	 * Das war mal die methode um zu checken, ob einer Firma noch Items zugewiesen sind. | 
				
			||||
	 * Diese Methode muss neu definiert werden, sobald der Stock-Service neu implementiert ist. | 
				
			||||
	 * @param company_id | 
				
			||||
	 * @return | 
				
			||||
	 */ | 
				
			||||
	Collection<Object> redefineMe(long company_id); | 
				
			||||
} | 
				
			||||
@ -1,38 +1,12 @@
				@@ -1,38 +1,12 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.core.model; | 
				
			||||
 | 
				
			||||
import static de.srsoftware.umbrella.core.Constants.*; | 
				
			||||
import static de.srsoftware.umbrella.core.Constants.CODE; | 
				
			||||
import static de.srsoftware.umbrella.core.Util.mapMarkdown; | 
				
			||||
 | 
				
			||||
import de.srsoftware.tools.Mappable; | 
				
			||||
import java.sql.ResultSet; | 
				
			||||
import java.sql.SQLException; | 
				
			||||
import java.util.Map; | 
				
			||||
 | 
				
			||||
public record Item(long id, long companyId, String code, String name, String description, String unit, long unitPrice, long tax) implements Mappable { | 
				
			||||
	public static Item of(ResultSet rs) throws SQLException { | 
				
			||||
		var id = rs.getLong(ID); | 
				
			||||
		var companyId = rs.getLong(COMPANY_ID); | 
				
			||||
		var code      = rs.getString(CODE); | 
				
			||||
		var name      =  rs.getString(NAME); | 
				
			||||
		var desc      = rs.getString(DESCRIPTION); | 
				
			||||
		var unit      = rs.getString(UNIT); | 
				
			||||
		var unitPrice = rs.getLong(UNIT_PRICE); | 
				
			||||
		var tax       = rs.getInt(TAX); | 
				
			||||
 | 
				
			||||
		return new Item(id,companyId,code,name,desc,unit,unitPrice,tax); | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	@Override | 
				
			||||
	public Map<String, Object> toMap() { | 
				
			||||
		return Map.of( | 
				
			||||
				ID,id, | 
				
			||||
				COMPANY_ID,companyId, | 
				
			||||
				CODE,code,NAME,name, | 
				
			||||
				DESCRIPTION,mapMarkdown(description), | 
				
			||||
				UNIT,unit, | 
				
			||||
				UNIT_PRICE,unitPrice, | 
				
			||||
				TAX,tax); | 
				
			||||
	} | 
				
			||||
import java.util.Collection; | 
				
			||||
 | 
				
			||||
public class Item { | 
				
			||||
	private long id; | 
				
			||||
	private String code; | 
				
			||||
	private boolean physical; | 
				
			||||
	private Location location; | 
				
			||||
	private Collection<Property> properties; | 
				
			||||
} | 
				
			||||
				 
					 | 
				
			||||
@ -0,0 +1,10 @@
				@@ -0,0 +1,10 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.core.model; | 
				
			||||
 | 
				
			||||
public class Location { | 
				
			||||
	private long id; | 
				
			||||
	private long parentLocationId; | 
				
			||||
	private String name; | 
				
			||||
	private String description; | 
				
			||||
	private String relation; // when added to an item, this field describes the type of the relation
 | 
				
			||||
} | 
				
			||||
@ -0,0 +1,10 @@
				@@ -0,0 +1,10 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.core.model; | 
				
			||||
 | 
				
			||||
public class Property { | 
				
			||||
	long id; | 
				
			||||
	String name; | 
				
			||||
	Object value; | 
				
			||||
	String unit; | 
				
			||||
	String quantity; | 
				
			||||
} | 
				
			||||
@ -1,8 +0,0 @@
				@@ -1,8 +0,0 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.items; | 
				
			||||
 | 
				
			||||
public class Constants { | 
				
			||||
	private Constants(){} | 
				
			||||
	public static final String CONFIG_DATABASE = "umbrella.modules.items.database"; | 
				
			||||
	public static final String TABLE_ITEMS = "items"; | 
				
			||||
} | 
				
			||||
@ -1,75 +0,0 @@
				@@ -1,75 +0,0 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.items; | 
				
			||||
 | 
				
			||||
import static de.srsoftware.umbrella.core.ConnectionProvider.connect; | 
				
			||||
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.Paths.LIST; | 
				
			||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.forbidden; | 
				
			||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; | 
				
			||||
import static de.srsoftware.umbrella.items.Constants.CONFIG_DATABASE; | 
				
			||||
 | 
				
			||||
import com.sun.net.httpserver.HttpExchange; | 
				
			||||
import de.srsoftware.configuration.Configuration; | 
				
			||||
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.api.ItemService; | 
				
			||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; | 
				
			||||
import de.srsoftware.umbrella.core.model.Item; | 
				
			||||
import de.srsoftware.umbrella.core.model.Token; | 
				
			||||
import de.srsoftware.umbrella.core.model.UmbrellaUser; | 
				
			||||
import java.io.IOException; | 
				
			||||
import java.util.Collection; | 
				
			||||
import java.util.HashMap; | 
				
			||||
import java.util.Optional; | 
				
			||||
 | 
				
			||||
public class ItemApi extends BaseHandler implements ItemService { | 
				
			||||
 | 
				
			||||
	private final ItemDb itemDb; | 
				
			||||
 | 
				
			||||
	public ItemApi(Configuration config) throws UmbrellaException { | 
				
			||||
		super(); | 
				
			||||
		var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); | 
				
			||||
		itemDb = new SqliteDb(connect(dbFile)); | 
				
			||||
		ModuleRegistry.add(this); | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	@Override | 
				
			||||
	public boolean doPost(Path path, HttpExchange ex) throws IOException { | 
				
			||||
		addCors(ex); | 
				
			||||
		try { | 
				
			||||
			Optional<Token> 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 LIST -> listItems(ex,user.get()); | 
				
			||||
				default -> super.doGet(path,ex); | 
				
			||||
			}; | 
				
			||||
		} catch (UmbrellaException e){ | 
				
			||||
			return send(ex,e); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	@Override | 
				
			||||
	public Collection<Item> list(long companyId) throws UmbrellaException { | 
				
			||||
		return itemDb.list(companyId); | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	private boolean listItems(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException { | 
				
			||||
		var json = json(ex); | 
				
			||||
		if (!(json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number cid)) throw missingFieldException(COMPANY_ID); | 
				
			||||
		var companyId = cid.longValue(); | 
				
			||||
		var company = companyService().get(companyId); | 
				
			||||
		if (!companyService().membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); | 
				
			||||
		var items = list(companyId) | 
				
			||||
				.stream() | 
				
			||||
				.map(Item::toMap) | 
				
			||||
				.map(HashMap::new) | 
				
			||||
				.peek(map -> map.put(FIELD_CURRENCY,company.currency())); | 
				
			||||
		return sendContent(ex,items); | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -1,10 +0,0 @@
				@@ -1,10 +0,0 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.items; | 
				
			||||
 | 
				
			||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; | 
				
			||||
import de.srsoftware.umbrella.core.model.Item; | 
				
			||||
import java.util.Collection; | 
				
			||||
 | 
				
			||||
public interface ItemDb { | 
				
			||||
	Collection<Item> list(long companyId) throws UmbrellaException; | 
				
			||||
} | 
				
			||||
@ -1,37 +0,0 @@
				@@ -1,37 +0,0 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.items; | 
				
			||||
 | 
				
			||||
import static de.srsoftware.tools.jdbc.Condition.equal; | 
				
			||||
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; | 
				
			||||
import static de.srsoftware.tools.jdbc.Query.select; | 
				
			||||
import static de.srsoftware.umbrella.core.Constants.COMPANY_ID; | 
				
			||||
import static de.srsoftware.umbrella.items.Constants.TABLE_ITEMS; | 
				
			||||
 | 
				
			||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; | 
				
			||||
import de.srsoftware.umbrella.core.model.Item; | 
				
			||||
import java.sql.Connection; | 
				
			||||
import java.sql.SQLException; | 
				
			||||
import java.util.Collection; | 
				
			||||
import java.util.HashSet; | 
				
			||||
 | 
				
			||||
public class SqliteDb implements ItemDb{ | 
				
			||||
 | 
				
			||||
	private final Connection db; | 
				
			||||
 | 
				
			||||
	public SqliteDb(Connection connection) { | 
				
			||||
		db = connection; | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	@Override | 
				
			||||
	public Collection<Item> list(long companyId) throws UmbrellaException { | 
				
			||||
		try { | 
				
			||||
			var items = new HashSet<Item>(); | 
				
			||||
			var rs = select(ALL).from(TABLE_ITEMS).where(COMPANY_ID, equal(companyId)).exec(db); | 
				
			||||
			while (rs.next()) items.add(Item.of(rs)); | 
				
			||||
			rs.close(); | 
				
			||||
			return items; | 
				
			||||
		} catch (SQLException e) { | 
				
			||||
			throw new UmbrellaException("Failed to load items from database"); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -1,4 +1,4 @@
				@@ -1,4 +1,4 @@
					 | 
				
			||||
description = "Umbrella : Items" | 
				
			||||
description = "Umbrella : Stock" | 
				
			||||
 | 
				
			||||
dependencies{ | 
				
			||||
    implementation(project(":core")) | 
				
			||||
@ -0,0 +1,15 @@
				@@ -0,0 +1,15 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.stock; | 
				
			||||
 | 
				
			||||
public class Constants { | 
				
			||||
	private Constants(){} | 
				
			||||
 | 
				
			||||
 | 
				
			||||
	public static final String CONFIG_DATABASE = "umbrella.modules.stock.database"; | 
				
			||||
	public static final String ITEM_ID = "item_id";	public static final String LOCATION_ID = "location_id"; | 
				
			||||
	public static final String PROPERTY_ID = "prop_id"; | 
				
			||||
	public static final String TABLE_ITEMS = "items"; | 
				
			||||
	public static final String TABLE_ITEM_PROPERTIES = "item_props"; | 
				
			||||
	public static final String TABLE_LOCATIONS = "locations"; | 
				
			||||
	public static final String TABLE_PROPERTIES = "properties"; | 
				
			||||
} | 
				
			||||
@ -0,0 +1,87 @@
				@@ -0,0 +1,87 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.stock; | 
				
			||||
 | 
				
			||||
import static de.srsoftware.umbrella.core.Constants.*; | 
				
			||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException; | 
				
			||||
import static de.srsoftware.umbrella.stock.Constants.*; | 
				
			||||
import static java.text.MessageFormat.format; | 
				
			||||
 | 
				
			||||
import de.srsoftware.umbrella.core.BaseDb; | 
				
			||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; | 
				
			||||
import de.srsoftware.umbrella.core.model.Item; | 
				
			||||
import de.srsoftware.umbrella.core.model.Location; | 
				
			||||
import java.sql.Connection; | 
				
			||||
import java.sql.SQLException; | 
				
			||||
import java.util.Collection; | 
				
			||||
import java.util.List; | 
				
			||||
 | 
				
			||||
public class SqliteDb extends BaseDb implements StockDb { | 
				
			||||
	public SqliteDb(Connection connection) { | 
				
			||||
		super(connection); | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	@Override | 
				
			||||
	protected int createTables() { | 
				
			||||
		int currentVersion = createSettingsTable(); | 
				
			||||
		switch (currentVersion){ | 
				
			||||
			case 0: | 
				
			||||
				createLocationsTable(); | 
				
			||||
				createItemsTable(); | 
				
			||||
				createPropertiesTable(); | 
				
			||||
				createItemPropsTable(); | 
				
			||||
		} | 
				
			||||
		return setCurrentVersion(1); | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	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); | 
				
			||||
			db.prepareStatement(sql).execute(); | 
				
			||||
		} catch (SQLException e) { | 
				
			||||
			throw databaseException(ERROR_FAILED_CREATE_TABLE,TABLE_ITEMS); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	private void createItemPropsTable() { | 
				
			||||
		try { | 
				
			||||
			var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} INT NOT NULL, {2} INT NOT NULL, {3} VARCHAR(255) NOT NULL, PRIMARY KEY({1}, {2}))"; | 
				
			||||
			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); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	private void createLocationsTable() { | 
				
			||||
		try { | 
				
			||||
			var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} VARCHAR(255) PRIMARY KEY, {2} VARCHAR(255) DEFAULT NULL, {3} VARCHAR(255) NOT NULL, {4} TEXT)"; | 
				
			||||
			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); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	private void createPropertiesTable() { | 
				
			||||
		try { | 
				
			||||
			var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} LONG PRIMARY KEY, {2} VARCHAR(255) NOT NULL, {3} INT NOT NULL, {4} VARCHAR(255))"; | 
				
			||||
			sql = format(sql, TABLE_PROPERTIES, ID, NAME, TYPE, UNIT); | 
				
			||||
			db.prepareStatement(sql).execute(); | 
				
			||||
		} catch (SQLException e) { | 
				
			||||
			throw databaseException(ERROR_FAILED_CREATE_TABLE,TABLE_PROPERTIES); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	@Override | 
				
			||||
	public Collection<Item> listItems(long companyId) throws UmbrellaException { | 
				
			||||
		return List.of(); | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	@Override | 
				
			||||
	public Collection<Location> listLocations(long companyId) { | 
				
			||||
		return List.of(); | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
 | 
				
			||||
} | 
				
			||||
@ -0,0 +1,31 @@
				@@ -0,0 +1,31 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.stock; | 
				
			||||
 | 
				
			||||
import static de.srsoftware.umbrella.core.ConnectionProvider.connect; | 
				
			||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; | 
				
			||||
import static de.srsoftware.umbrella.stock.Constants.CONFIG_DATABASE; | 
				
			||||
 | 
				
			||||
import de.srsoftware.configuration.Configuration; | 
				
			||||
import de.srsoftware.umbrella.core.BaseHandler; | 
				
			||||
import de.srsoftware.umbrella.core.ModuleRegistry; | 
				
			||||
import de.srsoftware.umbrella.core.api.StockService; | 
				
			||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; | 
				
			||||
import java.util.Collection; | 
				
			||||
import java.util.List; | 
				
			||||
 | 
				
			||||
public class StockApi extends BaseHandler implements StockService { | 
				
			||||
 | 
				
			||||
	private final StockDb stockDb; | 
				
			||||
 | 
				
			||||
	public StockApi(Configuration config) throws UmbrellaException { | 
				
			||||
		super(); | 
				
			||||
		var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); | 
				
			||||
		stockDb = new SqliteDb(connect(dbFile)); | 
				
			||||
		ModuleRegistry.add(this); | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	@Override | 
				
			||||
	public Collection<Object> redefineMe(long company_id) { | 
				
			||||
		return List.of(); | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,12 @@
				@@ -0,0 +1,12 @@
					 | 
				
			||||
/* © SRSoftware 2025 */ | 
				
			||||
package de.srsoftware.umbrella.stock; | 
				
			||||
 | 
				
			||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; | 
				
			||||
import de.srsoftware.umbrella.core.model.Item; | 
				
			||||
import de.srsoftware.umbrella.core.model.Location; | 
				
			||||
import java.util.Collection; | 
				
			||||
 | 
				
			||||
public interface StockDb { | 
				
			||||
	Collection<Item> listItems(long companyId) throws UmbrellaException; | 
				
			||||
	Collection<Location> listLocations(long companyId); | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue