improving logical flow of transactions & onboarding

This commit is contained in:
2025-10-27 23:18:47 +02:00
parent b5e891afed
commit 506d8a8e41
9 changed files with 61 additions and 25 deletions

View File

@@ -89,7 +89,7 @@ public class OnboardingController {
.setId(request.getWorkflowId())
.setState("verifyOtp") // The state you want to transition to
.setNextActivation(DateTime.now()) // Schedule immediate execution
.setStateVariables(new HashMap<>(){{ put("body", Utils.toJson(request)); }})
.setStateVariables(new HashMap<>(){{ put("verifyRequest", Utils.toJson(request)); }})
.setStateText("Resumed from manual state")
.build();
@@ -117,7 +117,7 @@ public class OnboardingController {
.setId(otpRequest.getWorkflowId())
.setState("resendOtp") // The state you want to transition to
.setNextActivation(DateTime.now()) // Schedule immediate execution
.setStateVariables(new HashMap<>(){{ put("body", Utils.toJson(otpRequest)); }})
.setStateVariables(new HashMap<>(){{ put("resend", Utils.toJson(otpRequest)); }})
.setStateText("Resumed from manual state")
.build();

View File

@@ -13,6 +13,7 @@ public class RegisterRequest {
private String password;
private String photoUrl;
private Long workflowId;
private String tempUid;
@NotBlank(message = "Email is required")
@Email(message = "Email should be valid")

View File

@@ -41,5 +41,5 @@ public class BaseEntity {
private LocalDateTime updatedAt; // Or Date, Timestamp
@Column(columnDefinition = "BOOLEAN DEFAULT FALSE")
private Boolean deleted;
private Boolean deleted = false;
}

View File

@@ -3,6 +3,7 @@ package zw.qantra.tm.domain.models;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.SQLRestriction;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@@ -14,6 +15,7 @@ import java.util.List;
@Table(name = "users")
@Data
@EqualsAndHashCode(callSuper = true)
@SQLRestriction("deleted = false")
public class User extends BaseEntity implements UserDetails {
@Column(unique = true, nullable = false)

View File

@@ -7,6 +7,7 @@ import org.springframework.stereotype.Repository;
import zw.qantra.tm.domain.enums.RequestType;
import zw.qantra.tm.domain.models.Transaction;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@@ -14,4 +15,5 @@ import java.util.UUID;
public interface TransactionRepository extends JpaRepository<Transaction, UUID>, JpaSpecificationExecutor<Transaction> {
Transaction findByTraceAndType(String trace, RequestType type);
Optional<Transaction> findByReference(String reference);
List<Transaction> findAllByUserId(String uid);
}

View File

@@ -29,6 +29,13 @@ public class TransactionService {
private final TransactionRepository transactionRepository;
private final AdditionalDataService additionalDataService;
public void reassignTransactions(String oldUid, String newUid){
List<Transaction> transactions = transactionRepository.findAllByUserId(oldUid);
transactions.forEach(transaction -> {
transaction.setUserId(newUid);
transactionRepository.save(transaction);
});
}
public Transaction findById(UUID id) {
Transaction transaction = transactionRepository.findById(id).orElseThrow(() -> new ApiException("Transaction not found for ID: " + id));

View File

@@ -22,6 +22,7 @@ public class UserService implements UserDetailsService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final JwtUtils jwtUtils;
private final TransactionService transactionService;
public User fetchUserByUsername(String username) {
return userRepository.findByUsername(username)
@@ -52,10 +53,19 @@ public class UserService implements UserDetailsService {
user.setPhone(request.getPhone());
user.setWorkflowId(request.getWorkflowId());
user.setPhotoUrl(request.getPhotoUrl());
user.setDeleted(false);
User savedUser = userRepository.save(user);
String token = jwtUtils.generateToken(savedUser);
// reassign anonymous transactions to the new user
// if we fail we move on, not a critical step
try {
transactionService.reassignTransactions(request.getTempUid(), savedUser.getId().toString());
}catch (Exception e) {
e.printStackTrace();
}
return new AuthResponse(token, "Bearer", savedUser.getUsername(),
savedUser.getEmail(), savedUser.getPhone(), savedUser.getFirstName(), savedUser.getLastName(), savedUser.getId());
}

View File

@@ -66,7 +66,7 @@ public class ZesaConfirmationProcessor implements TransactionProcessorInterface
transaction.setStatus(Status.SUCCESS);
transaction.setResponseCode((String) body.get("responseCode"));
transaction.setAdditionalData(body.get("additionalData"));
transactionService.save(transaction);
transaction.setCreditPhone(transaction.getDebitPhone());
AdditionalData additionalData = AdditionalData.builder()
.transactionId(transaction.getId().toString())
@@ -81,7 +81,7 @@ public class ZesaConfirmationProcessor implements TransactionProcessorInterface
transaction.setErrorMessage((String) body.get("errorMessage"));
}
return transactionService.save(transaction);
return transaction;
}
@Override

View File

@@ -30,10 +30,7 @@ import zw.qantra.tm.rest.RestService;
import org.springframework.http.HttpHeaders;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.*;
@Component
public class RegistrationWorkflow extends WorkflowDefinition {
@@ -151,7 +148,7 @@ public class RegistrationWorkflow extends WorkflowDefinition {
}
public NextAction verifyOtp(StateExecution execution) {
VerifyRequest payload = execution.getVariable("body", VerifyRequest.class);
VerifyRequest payload = execution.getVariable("verifyRequest", VerifyRequest.class);
boolean otpValid = otpService.verifyOtp(payload.getUsername(), payload.getOtpCode(), payload.getOtpType());
if(!otpValid) {
@@ -162,7 +159,7 @@ public class RegistrationWorkflow extends WorkflowDefinition {
}
public void resendOtp(StateExecution execution) {
OtpRequest otpRequest = execution.getVariable("body", OtpRequest.class);
OtpRequest otpRequest = execution.getVariable("resend", OtpRequest.class);
Otp otp = otpService.generateOtp(otpRequest.getUsername(), "REGISTRATION");
String string = String.format("Your Peak verification code is: %s", otp.getOtp());
emailService.sendSimpleMessage(otpRequest.getUsername(), "Peak verification code", string);
@@ -174,7 +171,7 @@ public class RegistrationWorkflow extends WorkflowDefinition {
log.info("OTP verified successfully");
log.info("Begin user registration");
VerifyRequest request = execution.getVariable("body", VerifyRequest.class);
VerifyRequest request = execution.getVariable("verifyRequest", VerifyRequest.class);
try {
RegisterRequest registerRequest = execution.getVariable("registerRequest", RegisterRequest.class);
@@ -220,9 +217,27 @@ public class RegistrationWorkflow extends WorkflowDefinition {
headers.add("Authorization", basicAuth);
headers.add("Cookie", sid);
// check if customer exists first
String customerName = registerRequest.getFirstName() + " " + registerRequest.getLastName();
try {
LinkedHashMap checkResponse = restService.getAs(erpnextUrl + "/v2/document/Customer" +
"?fields=[\"name\"]&filters=[[\"name\", \"=\", " +
"\"" + customerName + "\"]]", headers, LinkedHashMap.class);
List<LinkedHashMap> dataList = (List) checkResponse.get("data");
if(!dataList.isEmpty()){
User user = userService.fetchUserByUsername(registerRequest.getUsername());
user.setErpCustomerRef(customerName);
userService.getUserRepository().save(user);
return NextAction.stopInState(DONE, "Customer already exists in ERP. Ignoring update: " +
registerRequest.getUsername());
}
// if not proceed
CustomerDto customerDto = CustomerDto.builder()
.doctype("Customer")
.customerName(registerRequest.getFirstName() + " " + registerRequest.getLastName())
.customerName(customerName)
.customerGroup("Commercial")
.territory("All Territories")
.customerType("Individual")
@@ -232,7 +247,6 @@ public class RegistrationWorkflow extends WorkflowDefinition {
.docstatus(1)
.build();
try {
LinkedHashMap response = restService.postAs(
erpnextUrl + "/v2/document/Customer", customerDto, headers, LinkedHashMap.class);
@@ -245,7 +259,7 @@ public class RegistrationWorkflow extends WorkflowDefinition {
} catch (Exception e) {
e.printStackTrace();
return NextAction.retryAfter(DateTime.now().plusSeconds(30), "Failed to update ERP: " + e.getMessage());
return NextAction.retryAfter(DateTime.now().plusMinutes(5), "Failed to update ERP: " + e.getMessage());
}
return NextAction.stopInState(DONE, "ERP updated successfully for: " + registerRequest.getUsername());