From bce6efcdcb0d3a8be2a3eee9fa9d5817199a271d Mon Sep 17 00:00:00 2001 From: Mario Serrano Date: Thu, 31 Jul 2025 11:20:49 -0500 Subject: [PATCH 01/13] chore: update version to 3.4.1 in pom.xml files --- sources/api/pom.xml | 4 ++-- sources/core/pom.xml | 8 ++++---- sources/jpa/pom.xml | 4 ++-- sources/pom.xml | 2 +- sources/remote/pom.xml | 4 ++-- sources/ui/pom.xml | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/sources/api/pom.xml b/sources/api/pom.xml index 973cb2b..eab8d6e 100644 --- a/sources/api/pom.xml +++ b/sources/api/pom.xml @@ -26,13 +26,13 @@ tools.dynamia.modules tools.dynamia.modules.saas.parent - 3.4.0 + 3.4.1 tools.dynamia.modules.saas.api DynamiaModules - SaaS API https://www.dynamia.tools/modules/saas - 3.4.0 + 3.4.1 diff --git a/sources/core/pom.xml b/sources/core/pom.xml index ea3f73d..8de5fda 100644 --- a/sources/core/pom.xml +++ b/sources/core/pom.xml @@ -22,10 +22,10 @@ tools.dynamia.modules tools.dynamia.modules.saas.parent - 3.4.0 + 3.4.1 tools.dynamia.modules.saas - 3.4.0 + 3.4.1 DynamiaModules - SaaS Core https://www.dynamia.tools/modules/saas @@ -49,12 +49,12 @@ tools.dynamia.modules tools.dynamia.modules.saas.api - 3.4.0 + 3.4.1 tools.dynamia.modules tools.dynamia.modules.saas.jpa - 3.4.0 + 3.4.1 junit diff --git a/sources/jpa/pom.xml b/sources/jpa/pom.xml index 65ae18a..c3fa1a8 100644 --- a/sources/jpa/pom.xml +++ b/sources/jpa/pom.xml @@ -22,7 +22,7 @@ tools.dynamia.modules.saas.parent tools.dynamia.modules - 3.4.0 + 3.4.1 4.0.0 DynamiaModules - SaaS JPA @@ -33,7 +33,7 @@ tools.dynamia.modules tools.dynamia.modules.saas.api - 3.4.0 + 3.4.1 tools.dynamia diff --git a/sources/pom.xml b/sources/pom.xml index 9998364..567b689 100644 --- a/sources/pom.xml +++ b/sources/pom.xml @@ -21,7 +21,7 @@ 4.0.0 tools.dynamia.modules tools.dynamia.modules.saas.parent - 3.4.0 + 3.4.1 pom DynamiaModules - SaaS DynamiaTools extension to create SaaS applications with accounts control and multi tenants in same diff --git a/sources/remote/pom.xml b/sources/remote/pom.xml index 99bdecf..6c9dc39 100644 --- a/sources/remote/pom.xml +++ b/sources/remote/pom.xml @@ -22,7 +22,7 @@ tools.dynamia.modules.saas.parent tools.dynamia.modules - 3.4.0 + 3.4.1 4.0.0 @@ -35,7 +35,7 @@ tools.dynamia.modules tools.dynamia.modules.saas.jpa - 3.4.0 + 3.4.1 diff --git a/sources/ui/pom.xml b/sources/ui/pom.xml index ce5eea1..85760b6 100644 --- a/sources/ui/pom.xml +++ b/sources/ui/pom.xml @@ -22,10 +22,10 @@ tools.dynamia.modules tools.dynamia.modules.saas.parent - 3.4.0 + 3.4.1 tools.dynamia.modules.saas.ui - 3.4.0 + 3.4.1 DynamiaModules - SaaS UI https://www.dynamia.tools/modules/saas From 28f3e76dc5ca80ecdb10b66f98d265bbf43b3ec2 Mon Sep 17 00:00:00 2001 From: Mario Serrano Date: Thu, 31 Jul 2025 11:21:06 -0500 Subject: [PATCH 02/13] refactor: change account relationships from OneToOne to ManyToOne --- .../java/tools/dynamia/modules/saas/domain/Account.java | 4 ++-- .../tools/dynamia/modules/saas/domain/AccountCharge.java | 3 ++- .../java/tools/dynamia/modules/saas/domain/AccountLog.java | 2 +- .../tools/dynamia/modules/saas/domain/AccountPayment.java | 6 +++--- .../tools/dynamia/modules/saas/domain/AccountReseller.java | 3 ++- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java index 03c45f6..d1a3e42 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java @@ -78,7 +78,7 @@ public class Account extends SimpleEntity implements Transferable { @Email(message = "ingreso direccion de correo valida ") @NotEmpty(message = "ingrese direccion de correo electronico") private String email; - @OneToOne + @ManyToOne @NotNull private AccountType type; @Temporal(jakarta.persistence.TemporalType.DATE) @@ -186,7 +186,7 @@ public class Account extends SimpleEntity implements Transferable { private String activationCoupon; private String redirect; - @OneToOne + @ManyToOne private AccountChannelSale channel; public Account() { diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountCharge.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountCharge.java index 57419fd..e59f946 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountCharge.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountCharge.java @@ -17,6 +17,7 @@ package tools.dynamia.modules.saas.domain; +import jakarta.persistence.ManyToOne; import tools.dynamia.domain.jpa.BaseEntity; import jakarta.persistence.Entity; @@ -29,7 +30,7 @@ @Table(name = "saas_charges") public class AccountCharge extends BaseEntity { - @OneToOne + @ManyToOne @NotNull private Account account; @NotNull diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountLog.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountLog.java index ca08d38..e8a543b 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountLog.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountLog.java @@ -34,7 +34,7 @@ public class AccountLog extends BaseEntity implements Transferable { - @OneToOne + @ManyToOne @NotNull(message = "Select account") private Account account; - @OneToOne + @ManyToOne private AccountType type; @NotEmpty(message = "Entrer payment reference") private String reference; @@ -60,7 +60,7 @@ public class AccountPayment extends BaseEntity implements Transferable Date: Thu, 31 Jul 2025 15:19:00 -0500 Subject: [PATCH 03/13] feat: enhance AccountService with comprehensive account management methods --- .../modules/saas/services/AccountService.java | 155 ++++++++++++++++-- 1 file changed, 144 insertions(+), 11 deletions(-) diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountService.java b/sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountService.java index 1c12853..01d9f8c 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountService.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountService.java @@ -1,4 +1,3 @@ - /* * Copyright (C) 2023 Dynamia Soluciones IT S.A.S - NIT 900302344-1 * Colombia / South America @@ -18,88 +17,222 @@ package tools.dynamia.modules.saas.services; -import tools.dynamia.integration.CacheManagerUtils; -import tools.dynamia.modules.saas.AccountConfig; +import jakarta.servlet.http.HttpServletRequest; import tools.dynamia.modules.saas.api.AccountStats; import tools.dynamia.modules.saas.api.dto.AccountDTO; import tools.dynamia.modules.saas.domain.Account; import tools.dynamia.modules.saas.domain.AccountPayment; -import jakarta.servlet.http.HttpServletRequest; - import java.math.BigDecimal; import java.util.List; /** + * Service interface for managing accounts in a SaaS application. + * Provides methods for retrieving, initializing, updating, and managing account payments and statistics. + * This service is typically used in multi-tenant applications where each tenant has its own account. + * It includes methods for handling account payments, checking account status, and managing account statistics. * @author Mario Serrano Leones */ public interface AccountService { + /** + * Gets the account associated with the specified subdomain. + * @param subdomain The tenant's subdomain. + * @return The corresponding Account entity, or null if not found. + */ Account getAccount(String subdomain); + /** + * Gets the account associated with the current HTTP request. + * @param request The HTTP request object. + * @return The corresponding Account entity, or null if not found. + */ Account getAccount(HttpServletRequest request); + /** + * Gets the account ID associated with the specified subdomain. + * @param subdomain The tenant's subdomain. + * @return The account ID, or null if not found. + */ Long getAccountId(String subdomain); + /** + * Gets the account associated with a custom domain. + * @param domain The custom domain. + * @return The corresponding Account entity, or null if not found. + */ Account getAccountByCustomDomain(String domain); + /** + * Sets the default account for the system. + * @param account The account to set as default. + */ void setDefaultAccount(Account account); + /** + * Gets the default account for the system. + * @return The default Account entity. + */ Account getDefaultAccount(); + /** + * Initializes a new account with default values. + * @return A new initialized Account entity. + */ Account initAccount(); - + /** + * Calculates and updates the payment value for the account. + * @param account The account to compute the payment value for. + */ void computeAccountPaymentValue(Account account); + /** + * Initializes the Account entity with default or required values. + * @param entity The Account entity to initialize. + */ void initAccount(Account entity); - + /** + * Initializes the account from an AccountDTO. + * @param accountDTO The DTO containing account data. + */ void initAccount(AccountDTO accountDTO); + /** + * Updates the statistics associated with the account. + * @param a The account whose statistics will be updated. + */ void updateStats(Account a); - + /** + * Updates the statistics associated with all accounts in the system. + */ void updateAllAccountsStats(); - + /** + * Updates the provided statistics for the given account. + * @param a The account whose statistics will be updated. + * @param stats List of statistics to update. + */ void updateStats(Account a, List stats); + /** + * Finds all accounts that are eligible for payment processing. + * @return List of payable accounts. + */ List findPayableAccounts(); + /** + * Finds the last payment made for the specified account. + * @param account The account to search payments for. + * @return The last AccountPayment, or null if none exists. + */ AccountPayment findLastPayment(Account account); + /** + * Checks if the specified account is overdue. + * @param account The account to check. + * @return True if the account is overdue, false otherwise. + */ boolean isOverdue(Account account); + /** + * Determines if the specified account should be suspended. + * @param account The account to check. + * @return True if the account should be suspended, false otherwise. + */ boolean shouldBeSuspended(Account account); - + /** + * Attempts to charge the specified account. + * @param account The account to charge. + * @return True if the charge was successful, false otherwise. + */ boolean chargeAccount(Account account); + /** + * Computes and updates the expiration date for the specified account. + * @param account The account to update. + */ void computeExpirationDate(Account account); + /** + * Checks the validity and status of the given payment. + * @param payment The payment to check. + */ void checkPayment(AccountPayment payment); - + /** + * Computes and updates the balance for the specified account. + * @param account The account to compute the balance for. + */ void computeBalance(Account account); + /** + * Checks if the specified account is about to expire. + * @param account The account to check. + * @return True if the account is about to expire, false otherwise. + */ boolean isAboutToExpire(Account account); + /** + * Gets the payment value for the specified account. + * @param account The account to retrieve the payment value for. + * @return The payment value as a BigDecimal. + */ BigDecimal getPaymentValue(Account account); + /** + * Gets the account associated with the specified account ID. + * @param accountId The account ID. + * @return The corresponding Account entity, or null if not found. + */ Account getAccountById(Long accountId); + /** + * Gets the account ID associated with a custom domain. + * @param domain The custom domain. + * @return The account ID, or null if not found. + */ Long getAccountIdByCustomDomain(String domain); + /** + * Gets the account associated with the specified name. + * @param name The name of the account. + * @return The corresponding Account entity, or null if not found. + */ Account getAccountByName(String name); + /** + * Logs a message for the specified account. + * @param account The account to log the message for. + * @param message The message to log. + */ void log(Account account, String message); + /** + * Finds all payments associated with the specified account. + * @param account The account to retrieve payments for. + * @return List of AccountPayment entities. + */ List findAllPayments(Account account); + /** + * Clears the cache for all accounts. + */ void clearCache(); + /** + * Clears the cache for a specific account identified by accountId and accountDomain. + * @param accountId The ID of the account. + * @param accountDomain The domain of the account. + */ void clearCache(Long accountId, String accountDomain); + /** + * Clears the cache for the specified account. + * @param account The account to clear the cache for. + */ void clearCache(Account account); } From 9421086a966bd0c00cd6001f7323c41cd3b6a92d Mon Sep 17 00:00:00 2001 From: Mario Serrano Date: Thu, 31 Jul 2025 15:19:26 -0500 Subject: [PATCH 04/13] feat: add AccountReseller and AccountResellerAgent management services with new fields and methods --- .../modules/saas/domain/AccountReseller.java | 53 +++++++++++++--- .../saas/services/AccountResellerService.java | 52 +++++++++++++++ .../impl/AccountResellerServiceImpl.java | 63 +++++++++++++++++++ .../descriptors/AccountResellerAgentForm.yml | 11 ++++ .../descriptors/AccountResellerAgentTable.yml | 11 ++++ .../descriptors/AccountResellerForm.yml | 3 + 6 files changed, 185 insertions(+), 8 deletions(-) create mode 100644 sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountResellerService.java create mode 100644 sources/core/src/main/java/tools/dynamia/modules/saas/services/impl/AccountResellerServiceImpl.java create mode 100644 sources/ui/src/main/resources/META-INF/descriptors/AccountResellerAgentForm.yml create mode 100644 sources/ui/src/main/resources/META-INF/descriptors/AccountResellerAgentTable.yml diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java index d57454a..97bf590 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java @@ -17,16 +17,16 @@ package tools.dynamia.modules.saas.domain; -import jakarta.persistence.ManyToOne; +import jakarta.persistence.*; import org.hibernate.annotations.BatchSize; import tools.dynamia.domain.contraints.NotEmpty; import tools.dynamia.domain.jpa.BaseEntity; import tools.dynamia.domain.jpa.ContactInfo; import tools.dynamia.domain.util.DomainUtils; -import jakarta.persistence.Entity; -import jakarta.persistence.OneToOne; -import jakarta.persistence.Table; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; @Entity @Table(name = "saas_resellers") @@ -34,9 +34,6 @@ public class AccountReseller extends BaseEntity { - public static AccountReseller findByMainAccount(Long accountId){ - return DomainUtils.lookupCrudService().findSingle(AccountReseller.class,"mainAccount.id",accountId); - } @NotEmpty private String name; @@ -48,6 +45,14 @@ public static AccountReseller findByMainAccount(Long accountId){ @ManyToOne private Account mainAccount; private double comissionRate; + @OneToMany + private List agents = new ArrayList<>(); + @Temporal(TemporalType.DATE) + private Date startDate = new Date(); + @Temporal(TemporalType.DATE) + private Date endDate = new Date(); + @Column(length = 1000) + private String comments; public String getName() { @@ -75,7 +80,7 @@ public void setIdType(String idType) { } public ContactInfo getContactInfo() { - if(contactInfo==null){ + if (contactInfo == null) { contactInfo = new ContactInfo(); } return contactInfo; @@ -117,6 +122,38 @@ public void setComissionRate(double comissionRate) { this.comissionRate = comissionRate; } + public List getAgents() { + return agents; + } + + public void setAgents(List agents) { + this.agents = agents; + } + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public String getComments() { + return comments; + } + + public void setComments(String comments) { + this.comments = comments; + } + @Override public String toString() { return name; diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountResellerService.java b/sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountResellerService.java new file mode 100644 index 0000000..9f2da61 --- /dev/null +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountResellerService.java @@ -0,0 +1,52 @@ +package tools.dynamia.modules.saas.services; + +import tools.dynamia.modules.saas.api.enums.AccountStatus; +import tools.dynamia.modules.saas.domain.Account; +import tools.dynamia.modules.saas.domain.AccountPayment; +import tools.dynamia.modules.saas.domain.AccountReseller; +import tools.dynamia.modules.saas.domain.AccountResellerAgent; + +import java.util.Date; +import java.util.List; + +/** + * Service for managing account resellers in a SaaS application. + * This service is typically used to handle operations related to resellers and their agents. + * It may include methods for retrieving, updating, and managing reseller information. + */ +public interface AccountResellerService { + + /** + * Finds all enabled resellers in the system. + * + * @return A list of AccountReseller entities that are enabled. + */ + List findAllEnabledResellers(); + + /** + * Finds all resellers in the system, ordered by creation date. + * + * @return A list of AccountReseller entities ordered by their creation date. + */ + List findAgentsByReseller(AccountReseller reseller); + + /** + * Finds all payments associated with a specific reseller. + * + * @param reseller The AccountReseller for which to find payments. + * @param from The start date for filtering payments. + * @param to The end date for filtering payments. + * @return A list of AccountPayment entities associated with the specified reseller. + */ + List findPaymentsByReseller(AccountReseller reseller, Date from, Date to); + + /** + * Finds all accounts associated with a specific reseller. + * + * @param reseller The AccountReseller for which to find accounts. + * @return A list of Account entities associated with the specified reseller. + */ + List findAccountsByReseller(AccountReseller reseller, AccountStatus status); + + +} diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/services/impl/AccountResellerServiceImpl.java b/sources/core/src/main/java/tools/dynamia/modules/saas/services/impl/AccountResellerServiceImpl.java new file mode 100644 index 0000000..5a17de7 --- /dev/null +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/services/impl/AccountResellerServiceImpl.java @@ -0,0 +1,63 @@ +package tools.dynamia.modules.saas.services.impl; + +import org.springframework.stereotype.Service; +import tools.dynamia.domain.query.Parameters; +import tools.dynamia.domain.query.QueryConditions; +import tools.dynamia.domain.query.QueryParameters; +import tools.dynamia.domain.services.AbstractService; +import tools.dynamia.domain.services.CrudService; +import tools.dynamia.modules.saas.api.enums.AccountStatus; +import tools.dynamia.modules.saas.domain.Account; +import tools.dynamia.modules.saas.domain.AccountPayment; +import tools.dynamia.modules.saas.domain.AccountReseller; +import tools.dynamia.modules.saas.domain.AccountResellerAgent; +import tools.dynamia.modules.saas.services.AccountResellerService; + +import java.util.Date; +import java.util.List; + +@Service +public class AccountResellerServiceImpl extends AbstractService implements AccountResellerService { + + public AccountResellerServiceImpl() { + } + + public AccountResellerServiceImpl(CrudService crudService) { + super(crudService); + } + + public AccountResellerServiceImpl(CrudService crudService, Parameters appParams) { + super(crudService, appParams); + } + + @Override + public List findAllEnabledResellers() { + return crudService().find(AccountReseller.class, QueryParameters.with("enabled", true) + .orderBy("creationDate", true)); + + } + + @Override + public List findAgentsByReseller(AccountReseller reseller) { + if (reseller == null) { + return List.of(); + } + + return crudService().find(AccountResellerAgent.class, QueryParameters.with("reseller", reseller) + .orderBy("name", true)); + } + + @Override + public List findPaymentsByReseller(AccountReseller reseller, Date from, Date to) { + return crudService().find(AccountPayment.class, QueryParameters.with("reseller", reseller) + .add("creationDate", QueryConditions.between(from, to)) + .add("finished", true)); + } + + @Override + public List findAccountsByReseller(AccountReseller reseller, AccountStatus status) { + return crudService().find(Account.class, QueryParameters.with("reseller", reseller) + .add("status", status) + .orderBy("creationDate", true)); + } +} diff --git a/sources/ui/src/main/resources/META-INF/descriptors/AccountResellerAgentForm.yml b/sources/ui/src/main/resources/META-INF/descriptors/AccountResellerAgentForm.yml new file mode 100644 index 0000000..b86250a --- /dev/null +++ b/sources/ui/src/main/resources/META-INF/descriptors/AccountResellerAgentForm.yml @@ -0,0 +1,11 @@ +view: form +beanClass: tools.dynamia.modules.saas.domain.AccountResellerAgent +fields: + name: + identification: + email: + phone: + + +layout: + columns: 2 \ No newline at end of file diff --git a/sources/ui/src/main/resources/META-INF/descriptors/AccountResellerAgentTable.yml b/sources/ui/src/main/resources/META-INF/descriptors/AccountResellerAgentTable.yml new file mode 100644 index 0000000..a58b48a --- /dev/null +++ b/sources/ui/src/main/resources/META-INF/descriptors/AccountResellerAgentTable.yml @@ -0,0 +1,11 @@ +view: table +beanClass: tools.dynamia.modules.saas.domain.AccountResellerAgent +fields: + name: + identification: + email: + phone: + + +layout: + columns: 2 \ No newline at end of file diff --git a/sources/ui/src/main/resources/META-INF/descriptors/AccountResellerForm.yml b/sources/ui/src/main/resources/META-INF/descriptors/AccountResellerForm.yml index 151d893..fad7cf6 100644 --- a/sources/ui/src/main/resources/META-INF/descriptors/AccountResellerForm.yml +++ b/sources/ui/src/main/resources/META-INF/descriptors/AccountResellerForm.yml @@ -13,6 +13,9 @@ fields: externalId: enabled: + agents: + component: crudview + layout: columns: 4 \ No newline at end of file From f8453f24a44578f990218dac6953f43a81025af1 Mon Sep 17 00:00:00 2001 From: Mario Serrano Date: Thu, 31 Jul 2025 15:19:31 -0500 Subject: [PATCH 05/13] feat: add AccountFormCustomizer to manage reseller and agent selection in account forms --- .../ui/customizers/AccountFormCustomizer.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 sources/ui/src/main/java/tools/dynamia/modules/saas/ui/customizers/AccountFormCustomizer.java diff --git a/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/customizers/AccountFormCustomizer.java b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/customizers/AccountFormCustomizer.java new file mode 100644 index 0000000..17bdb7c --- /dev/null +++ b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/customizers/AccountFormCustomizer.java @@ -0,0 +1,80 @@ +package tools.dynamia.modules.saas.ui.customizers; + +import org.zkoss.zk.ui.event.Events; +import org.zkoss.zul.Combobox; +import tools.dynamia.integration.Containers; +import tools.dynamia.modules.saas.domain.Account; +import tools.dynamia.modules.saas.domain.AccountReseller; +import tools.dynamia.modules.saas.domain.AccountResellerAgent; +import tools.dynamia.modules.saas.services.AccountResellerService; +import tools.dynamia.ui.UIMessages; +import tools.dynamia.viewers.ViewCustomizer; +import tools.dynamia.zk.util.ZKUtil; +import tools.dynamia.zk.viewers.form.FormView; + +import java.util.List; + +/** + * Customizes the Account form view to handle reseller and reseller agent information. + * It sets up the comboboxes for selecting resellers and their agents, and updates the + * agent combobox based on the selected reseller. + *

+ * This class listens for value changes in the form and updates the reseller and agent + * information accordingly. It ensures that the agent combobox is populated with agents + */ +public class AccountFormCustomizer implements ViewCustomizer> { + + @Override + public void customize(FormView view) { + + view.addEventListener(FormView.ON_VALUE_CHANGED, e -> { + setupResellerInfo(view); + }); + + + } + + /** + * Setup reseller and reseller agent information in the form view. + * + * @param view + */ + private void setupResellerInfo(FormView view) { + var resellers = view.getFieldComponent("reseller").getInputComponent(); + var resellerAgents = view.getFieldComponent("resellerAgent").getInputComponent(); + + var account = view.getValue(); + + if (resellers instanceof Combobox resellerCombo && resellerAgents instanceof Combobox agentCombo) { + + var service = Containers.get().findObject(AccountResellerService.class); + + ZKUtil.fillCombobox(resellerCombo, service.findAllEnabledResellers(), account.getReseller(), true); + + if (account.getReseller() != null) { + var agents = service.findAgentsByReseller(account.getReseller()); + fillComboboxAgents(agentCombo, agents, account); + } + + resellerCombo.addEventListener(Events.ON_CHANGE, e -> { + AccountReseller reseller = resellerCombo.getSelectedItem().getValue(); + var agents = service.findAgentsByReseller(reseller); + fillComboboxAgents(agentCombo, agents, account); + }); + + } + } + + private static void fillComboboxAgents(Combobox agentCombo, List agents, Account account) { + if (agents == null || agents.isEmpty()) { + agentCombo.setValue(null); + agentCombo.setPlaceholder(UIMessages.getLocalizedMessage("No agents available")); + return; + } + + ZKUtil.fillCombobox(agentCombo, agents, account.getResellerAgent(), true); + agentCombo.setPlaceholder(agents.isEmpty() ? + UIMessages.getLocalizedMessage("No agents available") : + UIMessages.getLocalizedMessage("Select an agent")); + } +} From 1411c3d1513d423c2f64a1c92eb3589ab1d58c9b Mon Sep 17 00:00:00 2001 From: Mario Serrano Date: Thu, 31 Jul 2025 15:20:00 -0500 Subject: [PATCH 06/13] feat: add reseller and agent fields to Account model and update AccountForm configuration Signed-off-by: Mario Serrano --- .../dynamia/modules/saas/domain/Account.java | 45 +++++++++++++++++++ .../META-INF/descriptors/AccountForm.yml | 33 ++++++++++++-- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java index d1a3e42..a9f4076 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java @@ -40,6 +40,7 @@ import java.io.Serial; import java.math.BigDecimal; +import java.time.LocalDate; import java.time.ZoneId; import java.util.ArrayList; import java.util.Date; @@ -167,6 +168,18 @@ public class Account extends SimpleEntity implements Transferable { @ManyToOne private AccountReseller reseller; + @ManyToOne + private AccountResellerAgent resellerAgent; + + @Lob + private String resellerComments; + + @Column(length = 50) + private String resellerInvoice; + + @Temporal(TemporalType.DATE) + private Date resellerInvoiceDate; + @ManyToOne private AccountRegion accountRegion; private boolean templateAccount; @@ -910,4 +923,36 @@ public boolean isTemplateAccount() { public void setTemplateAccount(boolean templateAccount) { this.templateAccount = templateAccount; } + + public AccountResellerAgent getResellerAgent() { + return resellerAgent; + } + + public void setResellerAgent(AccountResellerAgent resellerAgent) { + this.resellerAgent = resellerAgent; + } + + public String getResellerComments() { + return resellerComments; + } + + public void setResellerComments(String resellerComments) { + this.resellerComments = resellerComments; + } + + public String getResellerInvoice() { + return resellerInvoice; + } + + public void setResellerInvoice(String resellerInvoice) { + this.resellerInvoice = resellerInvoice; + } + + public Date getResellerInvoiceDate() { + return resellerInvoiceDate; + } + + public void setResellerInvoiceDate(Date resellerInvoiceDate) { + this.resellerInvoiceDate = resellerInvoiceDate; + } } diff --git a/sources/ui/src/main/resources/META-INF/descriptors/AccountForm.yml b/sources/ui/src/main/resources/META-INF/descriptors/AccountForm.yml index 1f9eab0..c5f212e 100644 --- a/sources/ui/src/main/resources/META-INF/descriptors/AccountForm.yml +++ b/sources/ui/src/main/resources/META-INF/descriptors/AccountForm.yml @@ -1,5 +1,6 @@ view: form beanClass: tools.dynamia.modules.saas.domain.Account +customizer: tools.dynamia.modules.saas.ui.customizers.AccountFormCustomizer autofields: false fields: @@ -24,7 +25,7 @@ fields: accountRegion: label: Region profile: - reseller: + channel: creationDate: category: @@ -57,8 +58,6 @@ fields: component: spiner status: customDomain: - params: - span: 2 maxUsers: description: Override Account Type Config params: @@ -97,11 +96,39 @@ fields: templateAccount: + reseller: + component: combobox + params: + readonly: true + + resellerAgent: + label: Agent + component: combobox + params: + readonly: true + + resellerInvoice: + label: Invoice + + resellerInvoiceDate: + label: Invoice Date + + resellerComments: + label: Comments + params: + span: 4 + multiline: true + height: 80px + groups: contactInfo: fields: [ contactFirstName, contactLastName,phoneNumber,mobileNumber,contactEmail, city,region, country, address, customerInfo ] + + resellerInfo: + fields: [ reseller, resellerAgent, resellerInvoice, resellerInvoiceDate, resellerComments ] + configuration: fields: [ locale, timeZone,skin, maxUsers,statusDescription,uuid,instanceUuid,creationDate,remote,autoInit,requiredInstanceUuid,redirect,templateAccount ] From b0f52f787819a8ba3cf56b683feaec782abfe68a Mon Sep 17 00:00:00 2001 From: Mario Serrano Date: Thu, 31 Jul 2025 15:25:39 -0500 Subject: [PATCH 07/13] feat: add AccountResellerAgent entity to manage agents for resellers Signed-off-by: Mario Serrano --- .../saas/domain/AccountResellerAgent.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountResellerAgent.java diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountResellerAgent.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountResellerAgent.java new file mode 100644 index 0000000..02ef09b --- /dev/null +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountResellerAgent.java @@ -0,0 +1,103 @@ +package tools.dynamia.modules.saas.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import tools.dynamia.domain.contraints.Email; +import tools.dynamia.domain.contraints.NotEmpty; +import tools.dynamia.domain.jpa.SimpleEntity; + +import java.io.Serializable; + +/** + * Entity representing an agent or seller for a reseller (AccountReseller). + * Contains basic contact information and a many-to-one relationship with AccountReseller. + *

+ * Fields: + *

    + *
  • name: Agent's full name (required)
  • + *
  • email: Contact email
  • + *
  • phone: Contact phone number
  • + *
  • address: Contact address
  • + *
  • identification: Identification number or code
  • + *
  • reseller: Associated AccountReseller
  • + *
+ */ +@Entity +public class AccountResellerAgent extends SimpleEntity implements Serializable { + + + @NotEmpty(message = "Agent name is required") + private String name; + + @Email + private String email; + + @Column(length = 20) + private String phone; + + @Column(length = 200) + private String address; + + @Column(length = 50) + private String identification; + + @ManyToOne(optional = false) + private AccountReseller reseller; + + + // Getters y setters + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getIdentification() { + return identification; + } + + public void setIdentification(String identification) { + this.identification = identification; + } + + public AccountReseller getReseller() { + return reseller; + } + + public void setReseller(AccountReseller reseller) { + this.reseller = reseller; + } + + @Override + public String toString() { + return name; + } +} From b2a9ecb66de600fa93546fb91ff9b0af5ad8ac58 Mon Sep 17 00:00:00 2001 From: Mario Serrano Leones Date: Thu, 31 Jul 2025 15:26:39 -0500 Subject: [PATCH 08/13] Update sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../java/tools/dynamia/modules/saas/domain/AccountReseller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java index 97bf590..d5f38be 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java @@ -45,7 +45,7 @@ public class AccountReseller extends BaseEntity { @ManyToOne private Account mainAccount; private double comissionRate; - @OneToMany + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) private List agents = new ArrayList<>(); @Temporal(TemporalType.DATE) private Date startDate = new Date(); From 8ddad9e45e7960728be98abd816afa9b625cb321 Mon Sep 17 00:00:00 2001 From: Mario Serrano Leones Date: Thu, 31 Jul 2025 15:26:49 -0500 Subject: [PATCH 09/13] Update sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/main/java/tools/dynamia/modules/saas/domain/Account.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java index a9f4076..78e4eac 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/Account.java @@ -40,7 +40,6 @@ import java.io.Serial; import java.math.BigDecimal; -import java.time.LocalDate; import java.time.ZoneId; import java.util.ArrayList; import java.util.Date; From 88a52afcc16f32bd654763a67483902145f9e464 Mon Sep 17 00:00:00 2001 From: Mario Serrano Leones Date: Thu, 31 Jul 2025 15:27:05 -0500 Subject: [PATCH 10/13] Update sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../java/tools/dynamia/modules/saas/domain/AccountReseller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java index d5f38be..127da02 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java @@ -50,7 +50,7 @@ public class AccountReseller extends BaseEntity { @Temporal(TemporalType.DATE) private Date startDate = new Date(); @Temporal(TemporalType.DATE) - private Date endDate = new Date(); + private Date endDate = null; @Column(length = 1000) private String comments; From a25fe16cf97f2bc51b15b9607b7125a4f34cafb2 Mon Sep 17 00:00:00 2001 From: Mario Serrano Leones Date: Thu, 31 Jul 2025 15:27:22 -0500 Subject: [PATCH 11/13] Update sources/ui/src/main/java/tools/dynamia/modules/saas/ui/customizers/AccountFormCustomizer.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../modules/saas/ui/customizers/AccountFormCustomizer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/customizers/AccountFormCustomizer.java b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/customizers/AccountFormCustomizer.java index 17bdb7c..ec1a951 100644 --- a/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/customizers/AccountFormCustomizer.java +++ b/sources/ui/src/main/java/tools/dynamia/modules/saas/ui/customizers/AccountFormCustomizer.java @@ -57,6 +57,10 @@ private void setupResellerInfo(FormView view) { } resellerCombo.addEventListener(Events.ON_CHANGE, e -> { + if (resellerCombo.getSelectedItem() == null) { + UIMessages.showMessage("Please select a valid reseller."); + return; + } AccountReseller reseller = resellerCombo.getSelectedItem().getValue(); var agents = service.findAgentsByReseller(reseller); fillComboboxAgents(agentCombo, agents, account); From e15a279599cdc0d684cc3036ae6613de58532fff Mon Sep 17 00:00:00 2001 From: Mario Serrano Leones Date: Thu, 31 Jul 2025 15:27:32 -0500 Subject: [PATCH 12/13] Update sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountResellerService.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../modules/saas/services/AccountResellerService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountResellerService.java b/sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountResellerService.java index 9f2da61..4bae26d 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountResellerService.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/services/AccountResellerService.java @@ -24,9 +24,10 @@ public interface AccountResellerService { List findAllEnabledResellers(); /** - * Finds all resellers in the system, ordered by creation date. + * Finds all agents associated with a specific reseller. * - * @return A list of AccountReseller entities ordered by their creation date. + * @param reseller The AccountReseller for which to find agents. + * @return A list of AccountResellerAgent entities associated with the specified reseller. */ List findAgentsByReseller(AccountReseller reseller); From b6808e5952ee7935dfd3a2674ae433bdd8dd1980 Mon Sep 17 00:00:00 2001 From: Mario Serrano Date: Thu, 31 Jul 2025 15:28:31 -0500 Subject: [PATCH 13/13] feat: update AccountReseller entity to include orphanRemoval for agents relationship Signed-off-by: Mario Serrano --- .../tools/dynamia/modules/saas/domain/AccountReseller.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java index 127da02..5821346 100644 --- a/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java +++ b/sources/core/src/main/java/tools/dynamia/modules/saas/domain/AccountReseller.java @@ -34,7 +34,6 @@ public class AccountReseller extends BaseEntity { - @NotEmpty private String name; private String identification; @@ -45,7 +44,7 @@ public class AccountReseller extends BaseEntity { @ManyToOne private Account mainAccount; private double comissionRate; - @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "reseller") private List agents = new ArrayList<>(); @Temporal(TemporalType.DATE) private Date startDate = new Date();