adjusting the payment workflow

This commit is contained in:
2025-10-23 23:54:42 +02:00
parent 38173a2370
commit 22ecc4b589
5 changed files with 73 additions and 45 deletions

View File

@@ -84,13 +84,13 @@ public class TransactonController {
return ResponseEntity.ok(transactionService.findById(id)); return ResponseEntity.ok(transactionService.findById(id));
} }
@GetMapping("/poll/{id}") @GetMapping("/integration/{id}")
public ResponseEntity<Object> poll(@PathVariable UUID id) throws ExecutionException, InterruptedException { public ResponseEntity<Object> integration(@PathVariable UUID id) throws ExecutionException, InterruptedException {
Transaction transaction = transactionService.findById(id); Transaction transaction = transactionService.findById(id);
// Create an update to move the workflow to the next state // Create an update to move the workflow to the next state
WorkflowInstance update = new WorkflowInstance.Builder() WorkflowInstance update = new WorkflowInstance.Builder()
.setId(transaction.getWorkflowId()) .setId(transaction.getWorkflowId())
.setState("pollStatus") // The state you want to transition to .setState("integration") // The state you want to transition to
.setNextActivation(DateTime.now()) // Schedule immediate execution .setNextActivation(DateTime.now()) // Schedule immediate execution
.setStateText("Resumed from manual state") .setStateText("Resumed from manual state")
.build(); .build();
@@ -111,6 +111,33 @@ public class TransactonController {
return ResponseEntity.ok(response); return ResponseEntity.ok(response);
} }
@GetMapping("/poll/{id}")
public ResponseEntity<Object> poll(@PathVariable UUID id) throws ExecutionException, InterruptedException {
Transaction transaction = transactionService.findById(id);
// Create an update to move the workflow to the next state
WorkflowInstance update = new WorkflowInstance.Builder()
.setId(transaction.getWorkflowId())
.setState("pollStatus") // The state you want to transition to
.setNextActivation(DateTime.now()) // Schedule immediate execution
.setStateText("Resumed from manual state")
.build();
WorkflowInstanceAction action = new WorkflowInstanceAction.Builder(update)
.setType(WorkflowInstanceAction.WorkflowActionType.externalChange)
.setExecutionEnd(DateTime.now())
.build();
// Apply the update
workflowInstanceService.updateWorkflowInstance(update, action);
CompletableFuture<Object> future = workflowUtils
.waitForState(transaction.getWorkflowId(), new String[]{"purchaseInvoice", "failed"},
15000, 50);
Object response = future.get();
return ResponseEntity.ok(response);
}
@GetMapping("/request/{id}/{authType}") @GetMapping("/request/{id}/{authType}")
public ResponseEntity<Object> request(@PathVariable UUID id, @PathVariable AuthType authType) public ResponseEntity<Object> request(@PathVariable UUID id, @PathVariable AuthType authType)
throws ExecutionException, InterruptedException { throws ExecutionException, InterruptedException {
@@ -118,14 +145,15 @@ public class TransactonController {
Transaction transaction = transactionService.findById(id); Transaction transaction = transactionService.findById(id);
transaction.setType(RequestType.REQUEST); transaction.setType(RequestType.REQUEST);
transaction.setAuthType(authType); transaction.setAuthType(authType);
transactionService.save(transaction); transaction = transactionService.save(transaction);
// Create an update to move the workflow to the next state // Create an update to move the workflow to the next state
Transaction finalTransaction = transaction;
WorkflowInstance update = new WorkflowInstance.Builder() WorkflowInstance update = new WorkflowInstance.Builder()
.setId(transaction.getWorkflowId()) .setId(transaction.getWorkflowId())
.setState("salesInvoice") // The state you want to transition to .setState("gatewayPayment") // The state you want to transition to
.setNextActivation(DateTime.now()) // Schedule immediate execution .setNextActivation(DateTime.now()) // Schedule immediate execution
.setStateVariables(new HashMap<>(){{ put("request", Utils.toJson(transaction)); }}) .setStateVariables(new HashMap<>(){{ put("request", Utils.toJson(finalTransaction)); }})
.setStateText("Resumed from manual state") .setStateText("Resumed from manual state")
.build(); .build();

View File

@@ -16,6 +16,7 @@ import zw.qantra.tm.domain.models.Transaction;
import zw.qantra.tm.domain.services.TransactionService; import zw.qantra.tm.domain.services.TransactionService;
import zw.qantra.tm.domain.workflows.handlers.HandlerInterface; import zw.qantra.tm.domain.workflows.handlers.HandlerInterface;
import zw.qantra.tm.domain.workflows.handlers.WorkflowHandlerFactory; import zw.qantra.tm.domain.workflows.handlers.WorkflowHandlerFactory;
import zw.qantra.tm.exceptions.ApiException;
import zw.qantra.tm.utils.Utils; import zw.qantra.tm.utils.Utils;
import java.io.IOException; import java.io.IOException;
@@ -39,13 +40,13 @@ public class TransactionWorkflow extends WorkflowDefinition {
public static final State CONFIRM = new State("confirm"); public static final State CONFIRM = new State("confirm");
public static final State RECIPIENTS_UPDATE = new State("recipientsUpdate"); public static final State RECIPIENTS_UPDATE = new State("recipientsUpdate");
public static final State CONFIRM_SUCCESS = new State("confirmSuccess", WorkflowStateType.manual); public static final State CONFIRM_SUCCESS = new State("confirmSuccess", WorkflowStateType.manual);
public static final State SALES_INVOICE = new State("salesInvoice");
public static final State GATEWAY_PAYMENT = new State("gatewayPayment"); public static final State GATEWAY_PAYMENT = new State("gatewayPayment");
public static final State GATEWAY_PAYMENT_SUCCESS = new State("gatewayPaymentSuccess", WorkflowStateType.manual); public static final State GATEWAY_PAYMENT_SUCCESS = new State("gatewayPaymentSuccess", WorkflowStateType.manual);
public static final State JOURNAL_ENTRY = new State("journalEntry");
public static final State POLL_STATUS = new State("pollStatus"); public static final State POLL_STATUS = new State("pollStatus");
public static final State SALES_INVOICE = new State("salesInvoice");
public static final State JOURNAL_ENTRY = new State("journalEntry");
public static final State SALES_PAYMENT = new State("salesPayment"); public static final State SALES_PAYMENT = new State("salesPayment");
public static final State PURCHASE_INVOICE = new State("purchaseInvoice"); public static final State PURCHASE_INVOICE = new State("purchaseInvoice", WorkflowStateType.manual);
public static final State INTEGRATION = new State("integration"); public static final State INTEGRATION = new State("integration");
public static final State PURCHASE_INVOICE_PAYMENT = new State("purchaseInvoicePayment"); public static final State PURCHASE_INVOICE_PAYMENT = new State("purchaseInvoicePayment");
@@ -59,11 +60,11 @@ public class TransactionWorkflow extends WorkflowDefinition {
permit(CLEANUP, CONFIRM); permit(CLEANUP, CONFIRM);
permit(CONFIRM, RECIPIENTS_UPDATE, FAILED); permit(CONFIRM, RECIPIENTS_UPDATE, FAILED);
permit(RECIPIENTS_UPDATE, CONFIRM_SUCCESS); permit(RECIPIENTS_UPDATE, CONFIRM_SUCCESS);
permit(CONFIRM_SUCCESS, SALES_INVOICE); permit(CONFIRM_SUCCESS, GATEWAY_PAYMENT);
permit(SALES_INVOICE, GATEWAY_PAYMENT);
permit(GATEWAY_PAYMENT, GATEWAY_PAYMENT_SUCCESS, FAILED); permit(GATEWAY_PAYMENT, GATEWAY_PAYMENT_SUCCESS, FAILED);
permit(GATEWAY_PAYMENT_SUCCESS, POLL_STATUS); permit(GATEWAY_PAYMENT_SUCCESS, POLL_STATUS);
permit(POLL_STATUS, JOURNAL_ENTRY, FAILED); permit(POLL_STATUS, SALES_INVOICE, FAILED);
permit(SALES_INVOICE, JOURNAL_ENTRY);
permit(JOURNAL_ENTRY, SALES_PAYMENT); permit(JOURNAL_ENTRY, SALES_PAYMENT);
permit(SALES_PAYMENT, PURCHASE_INVOICE); permit(SALES_PAYMENT, PURCHASE_INVOICE);
permit(PURCHASE_INVOICE, INTEGRATION); permit(PURCHASE_INVOICE, INTEGRATION);
@@ -126,27 +127,10 @@ public class TransactionWorkflow extends WorkflowDefinition {
log.info("Confirm complete"); log.info("Confirm complete");
} }
public NextAction salesInvoice(StateExecution execution) {
try {
// cannot cast direct to Transaction object because of some jackson mapper hullabaloo :(
LinkedHashMap map = execution.getVariable("request", LinkedHashMap.class);
byte[] json = objectMapper.writeValueAsBytes(map);
Transaction transaction = objectMapper.readValue(json, Transaction.class);
transaction = process(transaction, "salesInvoice");
execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(GATEWAY_PAYMENT,
"Sales invoice generated - id:" + transaction.getErpSalesRef());
}catch (Exception e) {
e.printStackTrace();
return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage());
}
}
public NextAction gatewayPayment(StateExecution execution) { public NextAction gatewayPayment(StateExecution execution) {
try { try {
// cannot cast direct to Transaction object because of some jackson mapper hullabaloo :( // cannot cast direct to Transaction object because of some jackson mapper hullabaloo :(
LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class); LinkedHashMap map = execution.getVariable("request", LinkedHashMap.class);
byte[] json = objectMapper.writeValueAsBytes(map); byte[] json = objectMapper.writeValueAsBytes(map);
Transaction transaction = objectMapper.readValue(json, Transaction.class); Transaction transaction = objectMapper.readValue(json, Transaction.class);
@@ -172,13 +156,30 @@ public class TransactionWorkflow extends WorkflowDefinition {
transaction = process(transaction, "pollStatus"); transaction = process(transaction, "pollStatus");
execution.setVariable("body", Utils.toJson(transaction)); execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(JOURNAL_ENTRY, "Polling complete"); return NextAction.moveToState(SALES_INVOICE, "Polling complete");
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return NextAction.moveToState(FAILED, e.getMessage()); return NextAction.moveToState(FAILED, e.getMessage());
} }
} }
public NextAction salesInvoice(StateExecution execution) {
try {
// cannot cast direct to Transaction object because of some jackson mapper hullabaloo :(
LinkedHashMap map = execution.getVariable("request", LinkedHashMap.class);
byte[] json = objectMapper.writeValueAsBytes(map);
Transaction transaction = objectMapper.readValue(json, Transaction.class);
transaction = process(transaction, "salesInvoice");
execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(JOURNAL_ENTRY,
"Sales invoice generated - id:" + transaction.getErpSalesRef());
}catch (Exception e) {
e.printStackTrace();
return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage());
}
}
public NextAction journalEntry(StateExecution execution) { public NextAction journalEntry(StateExecution execution) {
try { try {
LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class); LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class);
@@ -211,20 +212,13 @@ public class TransactionWorkflow extends WorkflowDefinition {
} }
} }
public NextAction purchaseInvoice(StateExecution execution) { public void purchaseInvoice(StateExecution execution) throws IOException {
try {
LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class); LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class);
byte[] json = objectMapper.writeValueAsBytes(map); byte[] json = objectMapper.writeValueAsBytes(map);
Transaction transaction = objectMapper.readValue(json, Transaction.class); Transaction transaction = objectMapper.readValue(json, Transaction.class);
transaction = process(transaction, "purchaseInvoice"); transaction = process(transaction, "purchaseInvoice");
execution.setVariable("body", Utils.toJson(transaction)); execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(INTEGRATION,
"Purchase invoice generated - id: " + transaction.getErpPurchaseRef());
}catch (Exception e) {
e.printStackTrace();
return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage());
}
} }
public NextAction integration(StateExecution execution) { public NextAction integration(StateExecution execution) {

View File

@@ -117,7 +117,7 @@ public class PaymentEntryHandler implements HandlerInterface {
.paidTo(cashAccount) .paidTo(cashAccount)
.paidAmount(amount) .paidAmount(amount)
.receivedAmount(amount) .receivedAmount(amount)
.referenceNo(transaction.getSdkActionId()) .referenceNo(transaction.getId().toString())
.referenceDate(transaction.getCreatedAt().toLocalDate().toString()) .referenceDate(transaction.getCreatedAt().toLocalDate().toString())
.postingDate(transaction.getCreatedAt().toLocalDate().toString()) .postingDate(transaction.getCreatedAt().toLocalDate().toString())
.references(Arrays.asList(paymentReferenceDto)) .references(Arrays.asList(paymentReferenceDto))

View File

@@ -68,6 +68,9 @@ public class PurchaseInvoiceHandler implements HandlerInterface {
.build(); .build();
transactionEventService.save(transactionEvent); transactionEventService.save(transactionEvent);
if(transaction.getErpPurchaseRef() != null)
return transaction;
if(transaction.getStatus() == Status.SUCCESS){ if(transaction.getStatus() == Status.SUCCESS){
try { try {
logger.info("Creating purchase invoice in ERPNext for successful transaction"); logger.info("Creating purchase invoice in ERPNext for successful transaction");

View File

@@ -69,6 +69,9 @@ public class PurchasePaymentEntryHandler implements HandlerInterface {
.build(); .build();
transactionEventService.save(transactionEvent); transactionEventService.save(transactionEvent);
if(transaction.getErpPurchasePaymentRef() != null)
return transaction;
if(transaction.getStatus() == Status.SUCCESS){ if(transaction.getStatus() == Status.SUCCESS){
try { try {
logger.info("Creating purchase payment entry in ERPNext for successful transaction"); logger.info("Creating purchase payment entry in ERPNext for successful transaction");