updating sequence of flows

This commit is contained in:
2025-11-02 15:32:42 +02:00
parent 50057b8b93
commit 683da10748
4 changed files with 108 additions and 101 deletions

View File

@@ -89,12 +89,44 @@ public class TransactonController {
@GetMapping("/integration/{id}") @GetMapping("/integration/{id}")
public ResponseEntity<Object> integration(@PathVariable UUID id) throws ExecutionException, InterruptedException { public ResponseEntity<Object> integration(@PathVariable UUID id) throws ExecutionException, InterruptedException {
LogUtils.logPrettyJson(logger, "incoming integration", id);
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("integration") // 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
.setStateVariables(new HashMap<>(){{ put("body", Utils.toJson(transaction)); }})
.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[]{"purchaseInvoicePaymentSuccess", "failed"},
15000, 50);
Object response = future.get();
LogUtils.logPrettyJson(logger, "outgoing transaction", response);
return ResponseEntity.ok(response);
}
@GetMapping("/poll/{id}")
public ResponseEntity<Object> poll(@PathVariable UUID id) throws ExecutionException, InterruptedException {
LogUtils.logPrettyJson(logger, "incoming poll", id);
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
.setStateVariables(new HashMap<>(){{ put("body", Utils.toJson(transaction)); }})
.setStateText("Resumed from manual state") .setStateText("Resumed from manual state")
.build(); .build();
@@ -115,34 +147,6 @@ 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();
LogUtils.logPrettyJson(logger, "outgoing transaction", response);
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 {

View File

@@ -47,9 +47,9 @@ public class TransactionWorkflow extends WorkflowDefinition {
public static final State JOURNAL_ENTRY = new State("journalEntry"); 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");
public static final State PURCHASE_INVOICE_SUCCESS = new State("purchaseInvoiceSuccess", 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");
public static final State PURCHASE_INVOICE_PAYMENT_SUCCESS = new State("purchaseInvoicePaymentSuccess", WorkflowStateType.manual);
public static final State TRAN_FAILED = new State("failed", WorkflowStateType.manual); public static final State TRAN_FAILED = new State("failed", WorkflowStateType.manual);
public static final State DONE = new State("done", WorkflowStateType.end); public static final State DONE = new State("done", WorkflowStateType.end);
@@ -63,15 +63,15 @@ public class TransactionWorkflow extends WorkflowDefinition {
permit(RECIPIENTS_UPDATE, CONFIRM_SUCCESS); permit(RECIPIENTS_UPDATE, CONFIRM_SUCCESS);
permit(CONFIRM_SUCCESS, GATEWAY_PAYMENT); permit(CONFIRM_SUCCESS, GATEWAY_PAYMENT);
permit(GATEWAY_PAYMENT, GATEWAY_PAYMENT_SUCCESS, TRAN_FAILED); permit(GATEWAY_PAYMENT, GATEWAY_PAYMENT_SUCCESS, TRAN_FAILED);
permit(GATEWAY_PAYMENT_SUCCESS, POLL_STATUS); permit(GATEWAY_PAYMENT_SUCCESS, INTEGRATION);
permit(INTEGRATION, PURCHASE_INVOICE, TRAN_FAILED);
permit(PURCHASE_INVOICE, PURCHASE_INVOICE_PAYMENT);
permit(PURCHASE_INVOICE_PAYMENT, PURCHASE_INVOICE_PAYMENT_SUCCESS);
permit(PURCHASE_INVOICE_PAYMENT_SUCCESS, POLL_STATUS);
permit(POLL_STATUS, SALES_INVOICE, TRAN_FAILED); permit(POLL_STATUS, SALES_INVOICE, TRAN_FAILED);
permit(SALES_INVOICE, JOURNAL_ENTRY); permit(SALES_INVOICE, JOURNAL_ENTRY);
permit(JOURNAL_ENTRY, SALES_PAYMENT); permit(JOURNAL_ENTRY, SALES_PAYMENT);
permit(SALES_PAYMENT, PURCHASE_INVOICE); permit(SALES_PAYMENT, DONE);
permit(PURCHASE_INVOICE, PURCHASE_INVOICE_SUCCESS);
permit(PURCHASE_INVOICE_SUCCESS, INTEGRATION);
permit(INTEGRATION, PURCHASE_INVOICE_PAYMENT, TRAN_FAILED);
permit(PURCHASE_INVOICE_PAYMENT, DONE);
} }
public NextAction calculateCharges(StateExecution execution) { public NextAction calculateCharges(StateExecution execution) {
@@ -169,6 +169,68 @@ public class TransactionWorkflow extends WorkflowDefinition {
log.info("Gateway payment initiation complete"); log.info("Gateway payment initiation complete");
} }
public NextAction integration(StateExecution execution) throws IOException {
LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class);
byte[] json = objectMapper.writeValueAsBytes(map);
Transaction transaction = objectMapper.readValue(json, Transaction.class);
try {
HandlerInterface handlerInterface =
(HandlerInterface) workflowHandlerFactory.getWorkflowHandler("integration");
transaction = (Transaction) handlerInterface.process(transaction);
execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(PURCHASE_INVOICE, "Integration request successful");
} catch (Exception e) {
log.info(e.getMessage());
execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(TRAN_FAILED, e.getMessage());
}
}
public NextAction purchaseInvoice(StateExecution execution) throws Exception {
try {
LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class);
byte[] json = objectMapper.writeValueAsBytes(map);
Transaction transaction = objectMapper.readValue(json, Transaction.class);
HandlerInterface handlerInterface =
(HandlerInterface) workflowHandlerFactory.getWorkflowHandler("purchaseInvoice");
transaction = (Transaction) handlerInterface.process(transaction);
execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(PURCHASE_INVOICE_PAYMENT,
"Purchase invoice successful - id: " + transaction.getErpPurchaseRef());
}catch (Exception e) {
e.printStackTrace();
return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage());
}
}
public NextAction purchaseInvoicePayment(StateExecution execution) {
try {
LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class);
byte[] json = objectMapper.writeValueAsBytes(map);
Transaction transaction = objectMapper.readValue(json, Transaction.class);
HandlerInterface handlerInterface =
(HandlerInterface) workflowHandlerFactory.getWorkflowHandler("purchaseInvoicePayment");
transaction = (Transaction) handlerInterface.process(transaction);
execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(PURCHASE_INVOICE_PAYMENT_SUCCESS,
"Purchase invoice payment generated - id: " + transaction.getErpPurchasePaymentRef());
}catch (Exception e) {
e.printStackTrace();
return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage());
}
}
public void purchaseInvoicePaymentSuccess(StateExecution execution) {
log.info("Purchase invoice payment complete");
}
public NextAction pollStatus(StateExecution execution) throws IOException { public NextAction pollStatus(StateExecution execution) throws IOException {
// 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("body", LinkedHashMap.class);
@@ -185,7 +247,7 @@ public class TransactionWorkflow extends WorkflowDefinition {
} catch (Exception e) { } catch (Exception e) {
log.info(e.getMessage()); log.info(e.getMessage());
execution.setVariable("body", Utils.toJson(transaction)); execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(TRAN_FAILED, e.getMessage()); return NextAction.retryAfter(DateTime.now().plusSeconds(30), e.getMessage());
} }
} }
@@ -239,73 +301,11 @@ public class TransactionWorkflow extends WorkflowDefinition {
transaction = (Transaction) handlerInterface.process(transaction); transaction = (Transaction) handlerInterface.process(transaction);
execution.setVariable("body", Utils.toJson(transaction)); execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(PURCHASE_INVOICE, return NextAction.moveToState(DONE,
"Sales payment successful - id: " + transaction.getErpSalesPaymentRef()); "Sales payment successful - id: " + transaction.getErpSalesPaymentRef());
}catch (Exception e) { }catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage()); return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage());
} }
} }
public NextAction purchaseInvoice(StateExecution execution) throws Exception {
try {
LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class);
byte[] json = objectMapper.writeValueAsBytes(map);
Transaction transaction = objectMapper.readValue(json, Transaction.class);
HandlerInterface handlerInterface =
(HandlerInterface) workflowHandlerFactory.getWorkflowHandler("purchaseInvoice");
transaction = (Transaction) handlerInterface.process(transaction);
execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(PURCHASE_INVOICE_SUCCESS,
"Purchase invoice successful - id: " + transaction.getErpPurchaseRef());
}catch (Exception e) {
e.printStackTrace();
return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage());
}
}
public void purchaseInvoiceSuccess(StateExecution execution) {
log.info("Purchase invoice payment complete");
}
public NextAction integration(StateExecution execution) throws IOException {
LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class);
byte[] json = objectMapper.writeValueAsBytes(map);
Transaction transaction = objectMapper.readValue(json, Transaction.class);
try {
HandlerInterface handlerInterface =
(HandlerInterface) workflowHandlerFactory.getWorkflowHandler("integration");
transaction = (Transaction) handlerInterface.process(transaction);
execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(PURCHASE_INVOICE_PAYMENT, "Integration request successful");
} catch (Exception e) {
log.info(e.getMessage());
execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(TRAN_FAILED, e.getMessage());
}
}
public NextAction purchaseInvoicePayment(StateExecution execution) {
try {
LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class);
byte[] json = objectMapper.writeValueAsBytes(map);
Transaction transaction = objectMapper.readValue(json, Transaction.class);
HandlerInterface handlerInterface =
(HandlerInterface) workflowHandlerFactory.getWorkflowHandler("purchaseInvoicePayment");
transaction = (Transaction) handlerInterface.process(transaction);
execution.setVariable("body", Utils.toJson(transaction));
return NextAction.moveToState(DONE,
"Purchase invoice payment generated - id: " + transaction.getErpPurchasePaymentRef());
}catch (Exception e) {
e.printStackTrace();
return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage());
}
}
} }

View File

@@ -75,6 +75,9 @@ public class IntegrationHandler implements HandlerInterface {
throw new ApiException("Transaction failed: " + transaction.getErrorMessage() ); throw new ApiException("Transaction failed: " + transaction.getErrorMessage() );
}else { }else {
transaction.setErrorMessage(null); transaction.setErrorMessage(null);
// if we are here it means payment was a success
// make sure it is marked appropriately
transaction.setPaymentStatus(transaction.getStatus());
} }
Transaction t1 = Utils.deepCopy(transaction, Transaction.class); Transaction t1 = Utils.deepCopy(transaction, Transaction.class);

View File

@@ -27,7 +27,7 @@ public class PollStatusHandler implements HandlerInterface {
Logger logger = LoggerFactory.getLogger(TransactionService.class); Logger logger = LoggerFactory.getLogger(TransactionService.class);
// if the transaction is already successful, return it // if the transaction is already successful, return it
if(transaction.getIntegrationStatus() == Status.SUCCESS){ if(transaction.getPollingStatus() == Status.SUCCESS){
return transaction; return transaction;
} }
@@ -58,7 +58,7 @@ public class PollStatusHandler implements HandlerInterface {
// if transaction wasn't successful, throw and halt workflow // if transaction wasn't successful, throw and halt workflow
if(transaction.getStatus() != Status.SUCCESS){ if(transaction.getStatus() != Status.SUCCESS){
transactionService.save(transaction); transactionService.save(transaction);
throw new ApiException("Transaction failed: " + transaction.getErrorMessage() ); throw new ApiException("Verification pending: " + transaction.getErrorMessage() );
}else { }else {
transaction.setErrorMessage(null); transaction.setErrorMessage(null);
} }