diff --git a/pom.xml b/pom.xml
index 8774713..38cdf57 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
nl.andrewl
coyotecredit
- 1.2.0
+ 1.3.0
coyotecredit
Banking and stock trading application to teach students.
diff --git a/src/main/java/nl/andrewl/coyotecredit/ctl/dto/FullAccountData.java b/src/main/java/nl/andrewl/coyotecredit/ctl/dto/FullAccountData.java
index df65b31..37ce7cb 100644
--- a/src/main/java/nl/andrewl/coyotecredit/ctl/dto/FullAccountData.java
+++ b/src/main/java/nl/andrewl/coyotecredit/ctl/dto/FullAccountData.java
@@ -12,6 +12,8 @@ public record FullAccountData (
String number,
String name,
boolean admin,
+ long userId,
+ String username,
boolean userAdmin,// If the current user is an admin of the exchange this account is in.
boolean userIsOwner,// If the current user is the owner of this account.
ExchangeData exchange,
diff --git a/src/main/java/nl/andrewl/coyotecredit/ctl/exchange/ExchangeController.java b/src/main/java/nl/andrewl/coyotecredit/ctl/exchange/ExchangeController.java
index a491221..10f10cb 100644
--- a/src/main/java/nl/andrewl/coyotecredit/ctl/exchange/ExchangeController.java
+++ b/src/main/java/nl/andrewl/coyotecredit/ctl/exchange/ExchangeController.java
@@ -1,6 +1,7 @@
package nl.andrewl.coyotecredit.ctl.exchange;
import lombok.RequiredArgsConstructor;
+import nl.andrewl.coyotecredit.ctl.exchange.dto.AddAccountPayload;
import nl.andrewl.coyotecredit.ctl.exchange.dto.AddSupportedTradeablePayload;
import nl.andrewl.coyotecredit.ctl.exchange.dto.EditExchangePayload;
import nl.andrewl.coyotecredit.ctl.exchange.dto.InviteUserPayload;
@@ -66,6 +67,18 @@ public class ExchangeController {
return "redirect:/users/" + user.getId();
}
+ @GetMapping(path = "/{exchangeId}/createAccount")
+ public String getCreateAccountPage(@PathVariable long exchangeId, @AuthenticationPrincipal User user) {
+ exchangeService.ensureAdminAccount(exchangeId, user);
+ return "exchange/create_account";
+ }
+
+ @PostMapping(path = "/{exchangeId}/createAccount")
+ public String postCreateAccount(@PathVariable long exchangeId, @AuthenticationPrincipal User user, @ModelAttribute AddAccountPayload payload) {
+ exchangeService.addAccount(exchangeId, user, payload);
+ return "redirect:/exchanges/" + exchangeId + "/accounts";
+ }
+
@GetMapping(path = "/{exchangeId}/removeAccount/{accountId}")
public String getRemoveAccountPage(@PathVariable long exchangeId, @PathVariable long accountId, @AuthenticationPrincipal User user) {
exchangeService.ensureAdminAccount(exchangeId, user);
diff --git a/src/main/java/nl/andrewl/coyotecredit/ctl/exchange/dto/AddAccountPayload.java b/src/main/java/nl/andrewl/coyotecredit/ctl/exchange/dto/AddAccountPayload.java
new file mode 100644
index 0000000..e898c46
--- /dev/null
+++ b/src/main/java/nl/andrewl/coyotecredit/ctl/exchange/dto/AddAccountPayload.java
@@ -0,0 +1,8 @@
+package nl.andrewl.coyotecredit.ctl.exchange.dto;
+
+public record AddAccountPayload(
+ String username,
+ String password,
+ String email,
+ String accountName
+) {}
diff --git a/src/main/java/nl/andrewl/coyotecredit/dao/UserRepository.java b/src/main/java/nl/andrewl/coyotecredit/dao/UserRepository.java
index 0ebe683..4869d0c 100644
--- a/src/main/java/nl/andrewl/coyotecredit/dao/UserRepository.java
+++ b/src/main/java/nl/andrewl/coyotecredit/dao/UserRepository.java
@@ -12,4 +12,5 @@ public interface UserRepository extends JpaRepository {
boolean existsByUsername(String username);
Optional findByEmail(String email);
+ boolean existsByEmail(String email);
}
diff --git a/src/main/java/nl/andrewl/coyotecredit/model/Account.java b/src/main/java/nl/andrewl/coyotecredit/model/Account.java
index d193895..84b21b1 100644
--- a/src/main/java/nl/andrewl/coyotecredit/model/Account.java
+++ b/src/main/java/nl/andrewl/coyotecredit/model/Account.java
@@ -3,6 +3,7 @@ package nl.andrewl.coyotecredit.model;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
+import lombok.Setter;
import javax.persistence.*;
import java.math.BigDecimal;
@@ -53,7 +54,7 @@ public class Account {
* Administrators have special permissions to add and remove other accounts,
* custom tradeables, exchange rates, and more.
*/
- @Column(nullable = false)
+ @Column(nullable = false) @Setter
private boolean admin;
/**
diff --git a/src/main/java/nl/andrewl/coyotecredit/service/AccountService.java b/src/main/java/nl/andrewl/coyotecredit/service/AccountService.java
index 75f9660..b39fec6 100644
--- a/src/main/java/nl/andrewl/coyotecredit/service/AccountService.java
+++ b/src/main/java/nl/andrewl/coyotecredit/service/AccountService.java
@@ -115,23 +115,6 @@ public class AccountService {
notificationRepository.save(new UserNotification(recipient.getUser(), recipientMessage));
}
- public static record AccountData (
- long id,
- String accountNumber,
- String exchangeName
- ) {}
-
- @Transactional(readOnly = true)
- public List getAccountsOverview(User user) {
- return accountRepository.findAllByUser(user).stream()
- .map(a -> new AccountData(
- a.getId(),
- a.getNumber(),
- a.getExchange().getName()
- ))
- .toList();
- }
-
@Transactional(readOnly = true)
public FullAccountData getAccountData(User user, long accountId) {
Account account = accountRepository.findById(accountId)
@@ -149,6 +132,8 @@ public class AccountService {
account.getNumber(),
account.getName(),
account.isAdmin(),
+ account.getUser().getId(),
+ account.getUser().getUsername(),
userAccount.isAdmin(),
account.getUser().getId().equals(user.getId()),
new ExchangeData(
diff --git a/src/main/java/nl/andrewl/coyotecredit/service/ExchangeService.java b/src/main/java/nl/andrewl/coyotecredit/service/ExchangeService.java
index 1597a92..fbd4225 100644
--- a/src/main/java/nl/andrewl/coyotecredit/service/ExchangeService.java
+++ b/src/main/java/nl/andrewl/coyotecredit/service/ExchangeService.java
@@ -13,6 +13,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
@@ -41,6 +42,7 @@ public class ExchangeService {
private final ExchangeInvitationRepository invitationRepository;
private final UserNotificationRepository notificationRepository;
private final JavaMailSender mailSender;
+ private final PasswordEncoder passwordEncoder;
@Value("${coyote-credit.base-url}")
private String baseUrl;
@@ -117,6 +119,23 @@ public class ExchangeService {
return exchange;
}
+ @Transactional
+ public void addAccount(long exchangeId, User user, AddAccountPayload payload) {
+ Exchange exchange = getExchangeIfAdmin(exchangeId, user);
+ if (userRepository.existsByUsername(payload.username())) {
+ throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Username is taken.");
+ }
+ if (userRepository.existsByEmail(payload.email())) {
+ throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "There is already an account with that email.");
+ }
+ User newUser = userRepository.save(new User(payload.username(), passwordEncoder.encode(payload.password()), payload.email()));
+ Account account = accountRepository.save(new Account(AccountNumberUtils.generate(), newUser, payload.accountName(), exchange));
+ for (var t : exchange.getAllTradeables()) {
+ account.getBalances().add(new Balance(account, t, BigDecimal.ZERO));
+ }
+ accountRepository.save(account);
+ }
+
@Transactional
public void removeAccount(long exchangeId, long accountId, User user) {
Exchange exchange = exchangeRepository.findById(exchangeId)
diff --git a/src/main/java/nl/andrewl/coyotecredit/service/UserService.java b/src/main/java/nl/andrewl/coyotecredit/service/UserService.java
index 6e05891..f754bdf 100644
--- a/src/main/java/nl/andrewl/coyotecredit/service/UserService.java
+++ b/src/main/java/nl/andrewl/coyotecredit/service/UserService.java
@@ -67,6 +67,9 @@ public class UserService {
if (userRepository.existsByUsername(payload.username())) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Username is already taken.");
}
+ if (userRepository.existsByEmail(payload.email())) {
+ throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "There is already an account with that email.");
+ }
User user = new User(payload.username(), passwordEncoder.encode(payload.password()), payload.email());
user = userRepository.save(user);
if (payload.inviteCode() != null) {
diff --git a/src/main/resources/templates/account.html b/src/main/resources/templates/account.html
index 6c23e5f..bb17e94 100644
--- a/src/main/resources/templates/account.html
+++ b/src/main/resources/templates/account.html
@@ -25,6 +25,8 @@
+ Username
+