diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index d786457..e864333 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -11,6 +11,7 @@ application{ } dependencies{ + implementation(project(":company")) implementation(project(":core")) implementation(project(":documents")) implementation(project(":legacy")) @@ -23,7 +24,6 @@ dependencies{ implementation("de.srsoftware:tools.optionals:1.0.0") implementation("de.srsoftware:tools.slf4j2syslog:1.0.1") // this provides a slf4j implementation that forwards to System.Logger implementation("de.srsoftware:tools.util:2.0.3") - implementation("org.json:json:20240303") } tasks.jar { diff --git a/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java b/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java index 252cb71..c58a1b2 100644 --- a/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java +++ b/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java @@ -8,7 +8,8 @@ import static java.lang.System.Logger.Level.INFO; import com.sun.net.httpserver.HttpServer; import de.srsoftware.configuration.JsonConfig; import de.srsoftware.tools.ColorLogger; -import de.srsoftware.umbrella.core.UmbrellaException; +import de.srsoftware.umbrella.company.CompanyModule; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.documents.DocumentApi; import de.srsoftware.umbrella.legacy.LegacyApi; import de.srsoftware.umbrella.message.MessageApi; @@ -49,7 +50,8 @@ public class Application { var server = HttpServer.create(new InetSocketAddress(port), 0); var userModule = new UserModule(config,messageSystem); - var documentApi = new DocumentApi(userModule, config); + var companyModule = new CompanyModule(config, userModule); + var documentApi = new DocumentApi(companyModule, config); var legacyApi = new LegacyApi(userModule.userDb(),config); var messageApi = new MessageApi(messageSystem); var webHandler = new WebHandler(); diff --git a/build.gradle.kts b/build.gradle.kts index 7ea4cc1..3978f42 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,6 +42,7 @@ subprojects { testImplementation("org.junit.jupiter:junit-jupiter") implementation("de.srsoftware:tools.http:6.0.4") implementation("de.srsoftware:tools.logging:1.3.2") + implementation("org.json:json:20240303") } tasks.withType() { diff --git a/company/build.gradle.kts b/company/build.gradle.kts index 3c67e9c..6b72887 100644 --- a/company/build.gradle.kts +++ b/company/build.gradle.kts @@ -2,5 +2,7 @@ description = "Umbrella : Companies" dependencies{ implementation(project(":core")) + implementation("de.srsoftware:configuration.api:1.0.2") + implementation("de.srsoftware:tools.jdbc:1.3.2") } diff --git a/company/src/main/java/de/srsoftware/umbrella/company/CompanyModule.java b/company/src/main/java/de/srsoftware/umbrella/company/CompanyModule.java index 5d0f936..f679778 100644 --- a/company/src/main/java/de/srsoftware/umbrella/company/CompanyModule.java +++ b/company/src/main/java/de/srsoftware/umbrella/company/CompanyModule.java @@ -1,21 +1,45 @@ /* © SRSoftware 2025 */ package de.srsoftware.umbrella.company; +import static de.srsoftware.umbrella.company.Constants.CONFIG_DATABASE; +import static de.srsoftware.umbrella.core.ConnectionProvider.connect; +import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; + +import de.srsoftware.configuration.Configuration; +import de.srsoftware.umbrella.company.api.CompanyDb; import de.srsoftware.umbrella.core.api.CompanyService; import de.srsoftware.umbrella.core.api.UserService; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; +import de.srsoftware.umbrella.core.model.Company; import de.srsoftware.umbrella.core.model.UmbrellaUser; -import java.util.List; +import java.util.Collection; +import java.util.HashSet; public class CompanyModule implements CompanyService { private final UserService users; + private final CompanyDb companyDb; - public CompanyModule(UserService userService){ + public CompanyModule(Configuration config, UserService userService) throws UmbrellaException { + var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); + companyDb = new SqliteDb(connect(dbFile)); users = userService; } @Override - public List getMembers(long companyId) { - return null; + public Company get(long companyId) throws UmbrellaException { + return companyDb.load(companyId); + } + + @Override + public Collection getMembers(long companyId) throws UmbrellaException { + var members = new HashSet(); + for (var userId : companyDb.getMembers(companyId)) members.add(users.loadUser(userId)); + return members; + } + + @Override + public UserService userService() { + return users; } } diff --git a/company/src/main/java/de/srsoftware/umbrella/company/Constants.java b/company/src/main/java/de/srsoftware/umbrella/company/Constants.java new file mode 100644 index 0000000..489a04e --- /dev/null +++ b/company/src/main/java/de/srsoftware/umbrella/company/Constants.java @@ -0,0 +1,10 @@ +/* © SRSoftware 2025 */ +package de.srsoftware.umbrella.company; + +public class Constants { + private Constants(){} + + public static final String CONFIG_DATABASE = "umbrella.modules.company.database"; + public static final String TABLE_COMPANIES_USERS = "companies_users"; + public static final String TABLE_COMPANIES = "companies"; +} diff --git a/company/src/main/java/de/srsoftware/umbrella/company/SqliteDb.java b/company/src/main/java/de/srsoftware/umbrella/company/SqliteDb.java new file mode 100644 index 0000000..ce0d940 --- /dev/null +++ b/company/src/main/java/de/srsoftware/umbrella/company/SqliteDb.java @@ -0,0 +1,52 @@ +/* © SRSoftware 2025 */ +package de.srsoftware.umbrella.company; + +import static de.srsoftware.tools.jdbc.Condition.equal; +import static de.srsoftware.tools.jdbc.Query.select; +import static de.srsoftware.umbrella.company.Constants.TABLE_COMPANIES; +import static de.srsoftware.umbrella.company.Constants.TABLE_COMPANIES_USERS; +import static de.srsoftware.umbrella.core.Constants.*; + +import de.srsoftware.umbrella.company.api.CompanyDb; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; +import de.srsoftware.umbrella.core.model.Company; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashSet; + +public class SqliteDb implements CompanyDb { + + private final Connection db; + + public SqliteDb(Connection connection) { + db = connection; + } + + @Override + public Collection getMembers(long companyId) throws UmbrellaException { + try { + var rs = select("*").from(TABLE_COMPANIES_USERS).where(COMPANY_ID, equal(companyId)).exec(db); + var ids = new HashSet(); + while (rs.next()) ids.add(rs.getLong(USER_ID)); + rs.close(); + return ids; + } catch (SQLException e) { + throw new UmbrellaException("Failed to load members of company {0}",companyId); + } + } + + @Override + public Company load(long companyId) throws UmbrellaException { + try { + var rs = select("*").from(TABLE_COMPANIES).where(ID, equal(companyId)).exec(db); + Company company = null; + if (rs.next()) company = Company.of(rs); + rs.close(); + if (company == null) throw new UmbrellaException("Could not load company {0}",companyId); + return company; + } catch (SQLException e){ + throw new UmbrellaException("Could not load company {0}",companyId); + } + } +} diff --git a/company/src/main/java/de/srsoftware/umbrella/company/api/CompanyDb.java b/company/src/main/java/de/srsoftware/umbrella/company/api/CompanyDb.java new file mode 100644 index 0000000..883a504 --- /dev/null +++ b/company/src/main/java/de/srsoftware/umbrella/company/api/CompanyDb.java @@ -0,0 +1,12 @@ +/* © SRSoftware 2025 */ +package de.srsoftware.umbrella.company.api; + +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; +import de.srsoftware.umbrella.core.model.Company; +import java.util.Collection; + +public interface CompanyDb { + Collection getMembers(long companyId) throws UmbrellaException; + + Company load(long companyId) throws UmbrellaException; +} diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 17fa00a..dd3160e 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -13,7 +13,6 @@ dependencies { implementation("de.srsoftware:tools.mime:1.1.2") implementation("de.srsoftware:tools.optionals:1.0.0") implementation("de.srsoftware:tools.util:2.0.3") - implementation("org.json:json:20240303") implementation("org.xerial:sqlite-jdbc:3.49.0.0") testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") diff --git a/core/src/main/java/de/srsoftware/umbrella/core/BaseHandler.java b/core/src/main/java/de/srsoftware/umbrella/core/BaseHandler.java index 52fbca8..3a6dd2b 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/BaseHandler.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/BaseHandler.java @@ -10,6 +10,7 @@ import static java.text.MessageFormat.format; import com.sun.net.httpserver.HttpExchange; import de.srsoftware.tools.Path; import de.srsoftware.tools.PathHandler; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; @@ -35,7 +36,7 @@ public abstract class BaseHandler extends PathHandler { return sendEmptyResponse(HTTP_FORBIDDEN,ex); } - public record Document(String mime, byte[] bytes){} + public record Page(String mime, byte[] bytes){} public boolean load(Path path, HttpExchange ex) throws IOException { try { @@ -47,7 +48,7 @@ public abstract class BaseHandler extends PathHandler { } } - public Document load(String resourcePath) throws IOException, UmbrellaException { + public Page load(String resourcePath) throws IOException, UmbrellaException { var url = getClass().getClassLoader().getResource(resourcePath); if (url == null) throw new UmbrellaException(HTTP_NOT_FOUND,"{0} not found!",resourcePath); LOG.log(DEBUG,"Trying to load {0}",url); @@ -56,7 +57,7 @@ public abstract class BaseHandler extends PathHandler { var mime = conn.getContentType(); try (var stream = conn.getInputStream()){ stream.transferTo(bos); - return new Document(mime,bos.toByteArray()); + return new Page(mime,bos.toByteArray()); } catch (Exception e) { LOG.log(WARNING,"Failed to load {0}",url); throw new UmbrellaException(HTTP_NOT_FOUND,"Failed to load {0}",url); 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 71927cc..650232d 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/Constants.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/Constants.java @@ -64,4 +64,16 @@ public class Constants { public static final String USER_LIST = "user_list"; public static final String UTF8 = UTF_8.displayName(); public static final String VALUE = "value"; + + public static final String COMPANY_ID = "company_id"; + public static final String FIELD_COURT = "court"; + public static final String FIELD_TAX_NUMBER = "tax_number"; + public static final String FIELD_PHONE = "phone"; + public static final String DECIMAL_SEPARATOR = "decimal_separator"; + public static final String THOUSANDS_SEPARATOR = "thousands_separator"; + public static final String LAST_CUSTOMER_NUMBER = "last_customer_number"; + public static final String DECIMALS = "decimals"; + public static final String CUSTOMER_NUMBER_PREFIX = "customer_number_prefix"; + public static final String FIELD_CURRENCY = "currency"; + public static final String FIELD_BANK_ACCOUNT = "bank_account"; } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/UmbrellaException.java b/core/src/main/java/de/srsoftware/umbrella/core/UmbrellaException.java deleted file mode 100644 index 3f8b404..0000000 --- a/core/src/main/java/de/srsoftware/umbrella/core/UmbrellaException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* © SRSoftware 2025 */ -package de.srsoftware.umbrella.core; - -import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR; -import static java.text.MessageFormat.format; - - -public class UmbrellaException extends Exception{ - private final int statusCode; - - public UmbrellaException(String message, Object ... fills){ - this(HTTP_SERVER_ERROR,message,fills); - } - - public UmbrellaException(int statusCode, String message, Object ... fills){ - super(format(message,fills)); - this.statusCode = statusCode; - } - - public UmbrellaException causedBy(Exception e) { - initCause(e); - return this; - } - - public int statusCode(){ - return statusCode; - } -} diff --git a/core/src/main/java/de/srsoftware/umbrella/core/Util.java b/core/src/main/java/de/srsoftware/umbrella/core/Util.java index 6d6f130..1b93738 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/Util.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/Util.java @@ -9,6 +9,7 @@ import static java.lang.System.Logger.Level.WARNING; import static java.nio.charset.StandardCharsets.UTF_8; import de.srsoftware.tools.Query; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.HttpURLConnection; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/CompanyService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/CompanyService.java index 35a1fcf..88d0d9b 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/api/CompanyService.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/CompanyService.java @@ -1,9 +1,14 @@ /* © SRSoftware 2025 */ package de.srsoftware.umbrella.core.api; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; +import de.srsoftware.umbrella.core.model.Company; import de.srsoftware.umbrella.core.model.UmbrellaUser; -import java.util.List; +import java.util.Collection; public interface CompanyService { - public List getMembers(long companyId); + public Collection getMembers(long companyId) throws UmbrellaException; + public UserService userService(); + + Company get(long companyId) throws UmbrellaException; } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/UserService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/UserService.java index a2739e2..1157abb 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/api/UserService.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/UserService.java @@ -2,7 +2,7 @@ package de.srsoftware.umbrella.core.api; import com.sun.net.httpserver.HttpExchange; -import de.srsoftware.umbrella.core.UmbrellaException; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Token; import de.srsoftware.umbrella.core.model.UmbrellaUser; import java.util.Optional; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/exceptions/UmbrellaException.java b/core/src/main/java/de/srsoftware/umbrella/core/exceptions/UmbrellaException.java new file mode 100644 index 0000000..c14e707 --- /dev/null +++ b/core/src/main/java/de/srsoftware/umbrella/core/exceptions/UmbrellaException.java @@ -0,0 +1,46 @@ +/* © SRSoftware 2025 */ +package de.srsoftware.umbrella.core.exceptions; + +import static de.srsoftware.umbrella.core.Constants.*; +import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR; +import static de.srsoftware.umbrella.core.ResponseCode.HTTP_UNPROCESSABLE; +import static java.lang.System.Logger.Level.ERROR; +import static java.text.MessageFormat.format; + + +public class UmbrellaException extends Exception{ + private final int statusCode; + + public UmbrellaException(String message, Object ... fills){ + this(HTTP_SERVER_ERROR,message,fills); + } + + public UmbrellaException(int statusCode, String message, Object ... fills){ + super(format(message,fills)); + this.statusCode = statusCode; + } + + public UmbrellaException causedBy(Exception e) { + initCause(e); + return this; + } + + public static UmbrellaException invalidFieldException(String field,String expected){ + return new UmbrellaException(HTTP_UNPROCESSABLE, ERROR_INVALID_FIELD, field, expected); + } + + + public static UmbrellaException missingConfigException(String field){ + System.getLogger("Configuration").log(ERROR,ERROR_MISSING_CONFIG, field); + return new UmbrellaException(HTTP_SERVER_ERROR, ERROR_MISSING_CONFIG, field); + } + + + public static UmbrellaException missingFieldException(String field){ + return new UmbrellaException(HTTP_UNPROCESSABLE, ERROR_MISSING_FIELD, field); + } + + public int statusCode(){ + return statusCode; + } +} diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/model/Company.java b/core/src/main/java/de/srsoftware/umbrella/core/model/Company.java similarity index 54% rename from documents/src/main/java/de/srsoftware/umbrella/documents/model/Company.java rename to core/src/main/java/de/srsoftware/umbrella/core/model/Company.java index 69c9e1e..9cf7e34 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/model/Company.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/Company.java @@ -1,10 +1,11 @@ /* © SRSoftware 2025 */ -package de.srsoftware.umbrella.documents.model; +package de.srsoftware.umbrella.core.model; import static de.srsoftware.umbrella.core.Constants.*; -import static de.srsoftware.umbrella.documents.Constants.*; -import de.srsoftware.umbrella.core.UmbrellaException; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; +import java.sql.ResultSet; +import java.sql.SQLException; import org.json.JSONException; import org.json.JSONObject; @@ -31,4 +32,26 @@ public record Company(long id, String name, String address, String court, String throw new UmbrellaException(500,"Failed to convert JSON to Company!").causedBy(e); } } + + public static Company of(ResultSet json) throws UmbrellaException { + try { + var id = json.getLong(ID); + var name = json.getString(NAME); + var address = json.getString(ADDRESS); + var email = json.getString(EMAIL); + var phone = json.getString(FIELD_PHONE); + var bankAccount = json.getString(FIELD_BANK_ACCOUNT); + var court = json.getString(FIELD_COURT); + var currency = json.getString(FIELD_CURRENCY); + var taxId = json.getString(FIELD_TAX_NUMBER); + var decimals = json.getInt(DECIMALS); + var decimalSep = json.getString(DECIMAL_SEPARATOR); + var thousandsSep = json.getString(THOUSANDS_SEPARATOR); + var lastCustomerNumber = json.getLong(LAST_CUSTOMER_NUMBER); + var customerNumberPrefix = json.getString(CUSTOMER_NUMBER_PREFIX); + return new Company(id,name,address,court,taxId,phone,decimalSep,thousandsSep,lastCustomerNumber,decimals,customerNumberPrefix,currency,email,bankAccount); + } catch (SQLException e){ + throw new UmbrellaException(500,"Failed to convert ResultSet to Company!").causedBy(e); + } + } } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/EmailAddress.java b/core/src/main/java/de/srsoftware/umbrella/core/model/EmailAddress.java index 665e67f..9d66260 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/EmailAddress.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/EmailAddress.java @@ -4,7 +4,8 @@ package de.srsoftware.umbrella.core.model; import static de.srsoftware.tools.Optionals.allSet; import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; -import de.srsoftware.umbrella.core.UmbrellaException; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; +import java.util.Objects; public class EmailAddress { private final String email; @@ -15,6 +16,11 @@ public class EmailAddress { email = addr; } + @Override + public boolean equals(Object obj) { + return obj instanceof EmailAddress other && Objects.equals(email,other.email); + } + @Override public String toString() { return email; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java b/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java index 22fb504..82600fe 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java @@ -3,10 +3,10 @@ package de.srsoftware.umbrella.core.model; import static de.srsoftware.umbrella.core.Constants.*; -import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; +import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; import de.srsoftware.tools.Mappable; -import de.srsoftware.umbrella.core.UmbrellaException; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -60,7 +60,7 @@ public class UmbrellaUser implements Mappable { public static UmbrellaUser of(JSONObject json) throws UmbrellaException { if (json.has(USER)) json = json.getJSONObject(USER); - if (!json.has(ID)) throw new UmbrellaException(HTTP_BAD_REQUEST,ERROR_MISSING_FIELD,ID); + if (!json.has(ID)) throw missingFieldException(ID); var id = json.getLong(ID); var name = json.has(NAME) ? json.getString(NAME) : null; var email = json.has(EMAIL) ? new EmailAddress(json.getString(EMAIL)) : null; diff --git a/documents/build.gradle.kts b/documents/build.gradle.kts index 3df4f2d..a66e013 100644 --- a/documents/build.gradle.kts +++ b/documents/build.gradle.kts @@ -1,11 +1,11 @@ description = "Umbrella : Documents" dependencies{ + implementation(project(":company")) implementation(project(":core")) implementation("de.srsoftware:configuration.api:1.0.2") implementation("de.srsoftware:tools.jdbc:1.3.2") implementation("de.srsoftware:tools.mime:1.1.2") implementation("de.srsoftware:tools.optionals:1.0.0") implementation("de.srsoftware:tools.util:2.0.3") - implementation("org.json:json:20240303") } \ No newline at end of file diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/Constants.java b/documents/src/main/java/de/srsoftware/umbrella/documents/Constants.java index c3105cc..757df37 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/Constants.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/Constants.java @@ -14,26 +14,18 @@ public class Constants { public static final String CONFIG_DATABASE = "umbrella.modules.document.database"; public static final String CONTACTS = "contacts"; - public static final String CUSTOMER_NUMBER_PREFIX = "customer_number_prefix"; - public static final String DECIMAL_SEPARATOR = "decimal_separator"; - public static final String DECIMALS = "decimals"; public static final String ERROR_ADDRESS_MISSING = "{0} address does not contain street address / post code / city"; - public static final String LAST_CUSTOMER_NUMBER = "last_customer_number"; - public static final String THOUSANDS_SEPARATOR = "thousands_separator"; public static final String PROJECT_ID = "project_id"; public static final String FIELD_AMOUNT = "amount"; - public static final String FIELD_BANK_ACCOUNT = "bank_account"; public static final String FIELD_CODE = "code"; public static final String FIELD_COMPANY = "company"; public static final String FIELD_COMPANY_ID = "company_id"; - public static final String FIELD_COURT = "court"; public static final String FIELD_CUSTOMER = "customer"; public static final String FIELD_CUSTOMER_EMAIL = "customer_email"; public static final String FIELD_CUSTOMER_NUMBER = "customer_number"; public static final String FIELD_CUSTOMER_TAX_NUMBER = "customer_tax_number"; - public static final String FIELD_CURRENCY = "currency"; public static final String FIELD_DEFAULT_HEADER = "default_header"; public static final String FIELD_DEFAULT_FOOTER = "default_footer"; public static final String FIELD_DEFAULT_MAIL = "type_mail_text"; @@ -53,7 +45,6 @@ public class Constants { public static final String FIELD_NET_PRICE = "net_price"; public static final String FIELD_NET_SUM = "net_sum"; public static final String FIELD_NEXT_TYPE = "next_type_id"; - public static final String FIELD_PHONE = "phone"; public static final String FIELD_POS = "pos"; public static final String FIELD_POSITIONS = "positions"; public static final String FIELD_PRICE = "single_price"; @@ -61,7 +52,6 @@ public class Constants { public static final String FIELD_PROJECTS = "projects"; public static final String FIELD_RECEIVER = "receiver"; public static final String FIELD_START_TIME = "start_time"; - public static final String FIELD_TAX_NUMBER = "tax_number"; public static final String FIELD_TEMPLATE_ID = "template_id"; public static final String FIELD_TASKS = "tasks"; public static final String FIELD_TAX = "tax"; diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java b/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java index 41f92bc..728cb18 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java @@ -3,25 +3,29 @@ package de.srsoftware.umbrella.documents; import static de.srsoftware.tools.MimeType.MIME_FORM_URL; import static de.srsoftware.umbrella.core.ConnectionProvider.connect; -import static de.srsoftware.umbrella.core.Constants.ERROR_MISSING_FIELD; +import static de.srsoftware.umbrella.core.Constants.SENDER; import static de.srsoftware.umbrella.core.Paths.LIST; import static de.srsoftware.umbrella.core.Util.request; +import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; import static de.srsoftware.umbrella.documents.Constants.*; +import static de.srsoftware.umbrella.documents.model.Document.State.NEW; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.WARNING; -import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; +import static java.net.HttpURLConnection.*; 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.UmbrellaException; +import de.srsoftware.umbrella.core.api.CompanyService; import de.srsoftware.umbrella.core.api.UserService; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Token; import de.srsoftware.umbrella.core.model.UmbrellaUser; -import de.srsoftware.umbrella.documents.model.Type; +import de.srsoftware.umbrella.documents.model.*; import java.io.IOException; +import java.time.LocalDate; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -31,15 +35,17 @@ import org.json.JSONObject; public class DocumentApi extends BaseHandler { - private final UserService users; + private final CompanyService companies; private final Configuration config; private final DocumentDb db; + private final UserService users; - public DocumentApi(UserService userHelper, Configuration config) throws UmbrellaException { + public DocumentApi(CompanyService companyService, Configuration config) throws UmbrellaException { this.config = config; - var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> new UmbrellaException(ERROR_MISSING_FIELD,CONFIG_DATABASE)); + var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); db = new SqliteDb(connect(dbFile)); - users = userHelper; + companies = companyService; + users = companyService.userService(); } @Override @@ -111,7 +117,7 @@ public class DocumentApi extends BaseHandler { private boolean listDocuments(HttpExchange ex, UmbrellaUser user, Token token) throws UmbrellaException { try { var json = json(ex); - if (!json.has(COMPANY)) throw new UmbrellaException(HTTP_BAD_REQUEST,ERROR_MISSING_FIELD, COMPANY); + if (!json.has(COMPANY)) throw missingFieldException(COMPANY); long companyId = json.getLong(COMPANY); var companies = getLegacyCompanies(ex,user, token); var company = companies.get(companyId); @@ -126,17 +132,34 @@ public class DocumentApi extends BaseHandler { } } - private boolean postDocument(HttpExchange ex, UmbrellaUser umbrellaUser) throws IOException, UmbrellaException { + private boolean postDocument(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException { var json = json(ex); - if (!(json.has(COMPANY) && json.get(COMPANY) instanceof String cid) || cid.isBlank()) throw new UmbrellaException(HTTP_BAD_REQUEST,ERROR_MISSING_FIELD,COMPANY); - long companyId; - try { - companyId = Long.parseLong(cid); - } catch (NumberFormatException nfe){ - throw new UmbrellaException(HTTP_BAD_REQUEST,nfe.getMessage()); - } + if (!(json.has(SENDER) && json.get(SENDER) instanceof JSONObject senderData)) throw missingFieldException(SENDER); + if (!senderData.has(FIELD_COMPANY) || !(senderData.get(FIELD_COMPANY) instanceof Number companyId)) throw missingFieldException(FIELD_COMPANY); + + var company = companies.get(companyId.longValue()); + var members = companies.getMembers(companyId.longValue()); + var match = false; + for (var member : members) match |= user.equals(member); + if (!match) return sendContent(ex,HTTP_FORBIDDEN,"You are mot a member of company "+companyId); + if (!json.has(FIELD_CUSTOMER) || !(json.get(FIELD_CUSTOMER) instanceof JSONObject customerData)) throw missingFieldException(FIELD_CUSTOMER); + if (!json.has(FIELD_TYPE) || !(json.get(FIELD_TYPE) instanceof Number docTypeId)) throw missingFieldException(FIELD_TYPE); + var type = db.getType(docTypeId.intValue()); + var customer = Customer.of(customerData); + Template template = new Template(6,companyId.longValue(),"unknwon",null); + String currency = company.currency(); + String sep = company.decimalSeparator(); + var settings = db.getCustomerSettings(companyId.longValue(),type,customer.id()); + var companySettings = db.getCompanySettings(companyId.longValue(),type); + var nextNumber = companySettings.nextDocId(); + String lastHead = settings.header(); + String lastFooter = settings.footer(); + var sender = Sender.of(senderData); LOG.log(DEBUG,json.toString(2)); - return notImplemented(ex,"{0}.postDocument(…)",this); + var doc = new Document(0,companyId.longValue(),nextNumber,type, LocalDate.now(), NEW,template,null,lastHead,lastFooter,currency,sep,sender,customer,new PositionList()); + var saved = db.save(doc); + db.step(companySettings); + return sendContent(ex,saved.toMap()); } } diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentDb.java b/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentDb.java index 9c69989..d87fa08 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentDb.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentDb.java @@ -2,7 +2,7 @@ package de.srsoftware.umbrella.documents; import de.srsoftware.tools.Pair; -import de.srsoftware.umbrella.core.UmbrellaException; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.documents.model.CompanySettings; import de.srsoftware.umbrella.documents.model.CustomerSettings; import de.srsoftware.umbrella.documents.model.Document; diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/SqliteDb.java b/documents/src/main/java/de/srsoftware/umbrella/documents/SqliteDb.java index f6a615a..22c0941 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/SqliteDb.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/SqliteDb.java @@ -6,6 +6,7 @@ import static de.srsoftware.tools.jdbc.Condition.equal; import static de.srsoftware.tools.jdbc.Condition.in; import static de.srsoftware.tools.jdbc.Query.*; import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; +import static de.srsoftware.umbrella.company.Constants.*; import static de.srsoftware.umbrella.core.Constants.*; import static de.srsoftware.umbrella.documents.Constants.*; import static de.srsoftware.umbrella.documents.model.Document.DEFAULT_THOUSANDS_SEPARATOR; @@ -16,7 +17,7 @@ import static java.time.ZoneOffset.UTC; import de.srsoftware.tools.Pair; import de.srsoftware.tools.jdbc.Query; -import de.srsoftware.umbrella.core.UmbrellaException; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.documents.model.*; import de.srsoftware.umbrella.documents.model.Type; import java.sql.Connection; diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/model/Customer.java b/documents/src/main/java/de/srsoftware/umbrella/documents/model/Customer.java index b1bf5eb..3fdfcc9 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/model/Customer.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/model/Customer.java @@ -2,10 +2,11 @@ package de.srsoftware.umbrella.documents.model; import static de.srsoftware.umbrella.core.Constants.*; +import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; import static de.srsoftware.umbrella.documents.Constants.FIELD_TAX_ID; import de.srsoftware.tools.Mappable; -import de.srsoftware.umbrella.core.UmbrellaException; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import java.util.HashSet; import java.util.Map; import java.util.Objects; @@ -63,10 +64,10 @@ public final class Customer implements Mappable { } public static Customer of(JSONObject json) throws UmbrellaException { - if (!json.has(ID) || !(json.get(ID) instanceof String id)) throw new UmbrellaException(400,ERROR_MISSING_FIELD,ID); - if (!json.has(NAME) || !(json.get(NAME) instanceof String name)) throw new UmbrellaException(400,ERROR_MISSING_FIELD,NAME); - if (!json.has(EMAIL) || !(json.get(EMAIL) instanceof String email)) throw new UmbrellaException(400,ERROR_MISSING_FIELD,EMAIL); - if (!json.has(FIELD_TAX_ID) || !(json.get(FIELD_TAX_ID) instanceof String taxId)) throw new UmbrellaException(400,ERROR_MISSING_FIELD,FIELD_TAX_ID); + if (!json.has(ID) || !(json.get(ID) instanceof String id)) throw missingFieldException(ID); + if (!json.has(NAME) || !(json.get(NAME) instanceof String name)) throw missingFieldException(NAME); + if (!json.has(EMAIL) || !(json.get(EMAIL) instanceof String email)) throw missingFieldException(EMAIL); + if (!json.has(FIELD_TAX_ID) || !(json.get(FIELD_TAX_ID) instanceof String taxId)) throw missingFieldException(FIELD_TAX_ID); return new Customer(id,name,email,taxId); } diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/model/PositionList.java b/documents/src/main/java/de/srsoftware/umbrella/documents/model/PositionList.java index df98255..6ea7fcb 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/model/PositionList.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/model/PositionList.java @@ -5,7 +5,7 @@ import static de.srsoftware.umbrella.core.Constants.*; import static de.srsoftware.umbrella.documents.Constants.*; import de.srsoftware.tools.Pair; -import de.srsoftware.umbrella.core.UmbrellaException; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import java.util.HashMap; import java.util.Map; import org.json.JSONObject; diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/model/Sender.java b/documents/src/main/java/de/srsoftware/umbrella/documents/model/Sender.java index 15a5805..02e2528 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/model/Sender.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/model/Sender.java @@ -1,12 +1,12 @@ /* © SRSoftware 2025 */ package de.srsoftware.umbrella.documents.model; -import static de.srsoftware.umbrella.core.Constants.ERROR_MISSING_FIELD; -import static de.srsoftware.umbrella.core.Constants.NAME; +import static de.srsoftware.umbrella.core.Constants.*; +import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; import static de.srsoftware.umbrella.documents.Constants.*; import de.srsoftware.tools.Mappable; -import de.srsoftware.umbrella.core.UmbrellaException; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import java.util.HashSet; import java.util.Map; import java.util.Objects; @@ -65,10 +65,10 @@ public final class Sender implements Mappable { } public static Sender of(JSONObject json) throws UmbrellaException { - if (!json.has(NAME) || !(json.get(NAME) instanceof String name)) throw new UmbrellaException(400,ERROR_MISSING_FIELD,NAME); - if (!json.has(FIELD_BANK_ACCOUNT) || !(json.get(FIELD_BANK_ACCOUNT) instanceof String bankAccount)) throw new UmbrellaException(400,ERROR_MISSING_FIELD,FIELD_BANK_ACCOUNT); - if (!json.has(FIELD_TAX_ID) || !(json.get(FIELD_TAX_ID) instanceof String taxId)) throw new UmbrellaException(400,ERROR_MISSING_FIELD,FIELD_TAX_ID); - if (!json.has(FIELD_COURT) || !(json.get(FIELD_COURT) instanceof String court)) throw new UmbrellaException(400,ERROR_MISSING_FIELD,FIELD_COURT); + if (!json.has(NAME) || !(json.get(NAME) instanceof String name)) throw missingFieldException(NAME); + if (!json.has(FIELD_BANK_ACCOUNT) || !(json.get(FIELD_BANK_ACCOUNT) instanceof String bankAccount)) throw missingFieldException(FIELD_BANK_ACCOUNT); + if (!json.has(FIELD_TAX_ID) || !(json.get(FIELD_TAX_ID) instanceof String taxId)) throw missingFieldException(FIELD_TAX_ID); + if (!json.has(FIELD_COURT) || !(json.get(FIELD_COURT) instanceof String court)) throw missingFieldException(FIELD_COURT); return new Sender(name,bankAccount,taxId,court); } diff --git a/frontend/src/routes/document/AddDocument.svelte b/frontend/src/routes/document/AddDocument.svelte index 1ec5231..878422c 100644 --- a/frontend/src/routes/document/AddDocument.svelte +++ b/frontend/src/routes/document/AddDocument.svelte @@ -9,13 +9,13 @@ let error = null; let docType = $state(null); let document = $state({ - type : router.query.document_type, - company : router.query.company_id, + type : +router.query.document_type, customer : { - addr : '' + name : '' }, sender : { - name : 'sender' + name : 'sender', + company : +router.query.company_id } }); @@ -24,13 +24,13 @@ var resp = await fetch(url,{ credentials: 'include'}); if (resp.ok){ const companies = await resp.json(); - company = companies[document.company]; + company = companies[document.sender.company]; document.sender.name = ''; - if (company.name) document.sender.name += company.name+"\n"; - if (company.address) document.sender.name += company.address+"\n"; - if (company.tax_number) document.sender.tax_id = company.tax_number; + if (company.name) document.sender.name += company.name+"\n"; + if (company.address) document.sender.name += company.address+"\n"; + if (company.tax_number) document.sender.tax_id = company.tax_number; if (company.bank_account) document.sender.bank_account = company.bank_account; - if (company.court) document.sender.court = company.court; + if (company.court) document.sender.court = company.court; } else { error = await resp.text(); } @@ -59,13 +59,13 @@ var name = (contact.N.given+" "+contact.N.family).trim()+"\n"; if (name != addr) addr += name; } - if (contact.ADR.street) addr += contact.ADR.street+"\n"; + if (contact.ADR.street) addr += contact.ADR.street+"\n"; if (contact.ADR.locality) addr += contact.ADR.post_code + " "+ contact.ADR.locality + "\n"; - if (contact.ADR.county) addr += contact.ADR.country+"\n"; - document.customer.addr = addr; - document.customer.tax_number = contact['X-TAX-NUMBER']; - document.customer.id = contact['X-CUSTOMER-NUMBER']; - document.customer.email = contact.EMAIL.val; + if (contact.ADR.county) addr += contact.ADR.country+"\n"; + document.customer.name = addr; + document.customer.tax_id = contact['X-TAX-NUMBER']; + document.customer.id = contact['X-CUSTOMER-NUMBER']; + document.customer.email = contact.EMAIL.val; } async function submit(){ @@ -76,6 +76,8 @@ body: JSON.stringify(document) }); if (resp.ok){ + const json = await resp.json(); + router.navigate(`/document/${json.id}/view`); } else { error = await resp.text(); } @@ -101,12 +103,12 @@ {t('document.customer')}