ironing out erp bugs

This commit is contained in:
2025-09-27 01:08:30 +02:00
parent 2bb5dbd8fa
commit b0c2eb352e
19 changed files with 142 additions and 119 deletions

View File

@@ -26,7 +26,7 @@ public class AdditionalDataController {
@GetMapping("/transaction/{transactionId}") @GetMapping("/transaction/{transactionId}")
public ResponseEntity<AdditionalData> getByTransactionId(@PathVariable UUID transactionId) { public ResponseEntity<AdditionalData> getByTransactionId(@PathVariable UUID transactionId) {
return additionalDataService.getAdditionalDataRepository().findByTransactionId(transactionId) return additionalDataService.getAdditionalDataRepository().findByTransactionId(transactionId.toString())
.map(ResponseEntity::ok) .map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build()); .orElse(ResponseEntity.notFound().build());
} }

View File

@@ -4,6 +4,8 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.util.UUID;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@@ -15,4 +17,5 @@ public class AuthResponse {
private String phone; private String phone;
private String firstName; private String firstName;
private String lastName; private String lastName;
private UUID uuid;
} }

View File

@@ -21,9 +21,7 @@ import zw.qantra.tm.domain.enums.RequestType;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class AdditionalData extends BaseEntity { public class AdditionalData extends BaseEntity {
@OneToOne private String transactionId;
@JoinColumn(name = "transaction_id")
private Transaction transaction;
@Column(columnDefinition = "TEXT") @Column(columnDefinition = "TEXT")
private String jsonString; private String jsonString;

View File

@@ -19,4 +19,5 @@ public class Provider extends BaseEntity {
private String category; private String category;
private String accountFieldName; private String accountFieldName;
private String integrationProcessorLabel; private String integrationProcessorLabel;
private double percentageCommission;
} }

View File

@@ -2,6 +2,7 @@ package zw.qantra.tm.domain.models;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Enumerated; import jakarta.persistence.Enumerated;
import jakarta.persistence.EnumType; import jakarta.persistence.EnumType;
@@ -24,6 +25,7 @@ public class TransactionEvent extends BaseEntity {
private String event; private String event;
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private Status status; private Status status;
@Column(columnDefinition = "TEXT")
private String message; private String message;
private String data; private String data;
} }

View File

@@ -11,6 +11,6 @@ import java.util.UUID;
@Repository @Repository
public interface AdditionalDataRepository extends JpaRepository<AdditionalData, UUID> { public interface AdditionalDataRepository extends JpaRepository<AdditionalData, UUID> {
Optional<AdditionalData> findByTransactionId(UUID transactionId); Optional<AdditionalData> findByTransactionId(String transactionId);
Optional<AdditionalData> findByTransactionIdAndRequestType(UUID transactionId, RequestType requestType); Optional<AdditionalData> findByTransactionIdAndRequestType(String transactionId, RequestType requestType);
} }

View File

@@ -9,7 +9,7 @@ import java.util.UUID;
@Repository @Repository
public interface UserRepository extends JpaRepository<User, Long> { public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findById(String uid); Optional<User> findById(UUID uid);
Optional<User> findByUsername(String username); Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email); Optional<User> findByEmail(String email);
Optional<User> findByPhone(String phone); Optional<User> findByPhone(String phone);

View File

@@ -28,10 +28,7 @@ import zw.qantra.tm.domain.services.processors.handlers.FormattingHandler;
import zw.qantra.tm.domain.services.processors.handlers.LogicPipeline; import zw.qantra.tm.domain.services.processors.handlers.LogicPipeline;
import zw.qantra.tm.domain.services.processors.handlers.RecipientsUpdateHandler; import zw.qantra.tm.domain.services.processors.handlers.RecipientsUpdateHandler;
import zw.qantra.tm.domain.services.processors.handlers.ValidationHandler; import zw.qantra.tm.domain.services.processors.handlers.ValidationHandler;
import zw.qantra.tm.domain.services.processors.handlers.erp.JournalEntryHandler; import zw.qantra.tm.domain.services.processors.handlers.erp.*;
import zw.qantra.tm.domain.services.processors.handlers.erp.PaymentEntryHandler;
import zw.qantra.tm.domain.services.processors.handlers.erp.PurchaseInvoiceHandler;
import zw.qantra.tm.domain.services.processors.handlers.erp.SalesInvoiceHandler;
import zw.qantra.tm.domain.models.TransactionEvent; import zw.qantra.tm.domain.models.TransactionEvent;
import zw.qantra.tm.exceptions.ApiException; import zw.qantra.tm.exceptions.ApiException;
import zw.qantra.tm.utils.Utils; import zw.qantra.tm.utils.Utils;
@@ -50,6 +47,12 @@ public class TransactionService {
private final RecipientService recipientService; private final RecipientService recipientService;
private final TransactionEventService transactionEventService; private final TransactionEventService transactionEventService;
private final SalesInvoiceHandler salesInvoiceHandler;
private final JournalEntryHandler journalEntryHandler;
private final PaymentEntryHandler paymentEntryHandler;
private final PurchaseInvoiceHandler purchaseInvoiceHandler;
private final PurchasePaymentEntryHandler purchasePaymentEntryHandler;
public Transaction integration(Transaction transaction) { public Transaction integration(Transaction transaction) {
transaction.setType(RequestType.INTEGRATION); transaction.setType(RequestType.INTEGRATION);
@@ -62,8 +65,8 @@ public class TransactionService {
LogicPipeline<Transaction, Transaction> transactionLogic = LogicPipeline<Transaction, Transaction> transactionLogic =
new LogicPipeline<>(new ValidationHandler()) new LogicPipeline<>(new ValidationHandler())
.addHandler(new JournalEntryHandler()) // todo: do this in a separate thread .addHandler(journalEntryHandler) // todo: do this in a separate thread
.addHandler(new PaymentEntryHandler()); // todo: do this in a separate thread .addHandler(paymentEntryHandler); // todo: do this in a separate thread
transaction = transactionLogic.execute(transaction); transaction = transactionLogic.execute(transaction);
Provider provider = providerService.getProviderRepository() Provider provider = providerService.getProviderRepository()
@@ -84,7 +87,7 @@ public class TransactionService {
transaction.setIntegrationStatus(transaction.getStatus()); transaction.setIntegrationStatus(transaction.getStatus());
transactionRepository.save(transaction); transactionRepository.save(transaction);
}catch (Exception e){ }catch (Exception e){
e.printStackTrace(); e.getCause().printStackTrace();
transaction.setStatus(Status.FAILED); transaction.setStatus(Status.FAILED);
transaction.setResponseCode("01"); transaction.setResponseCode("01");
transaction.setErrorMessage(e.getMessage()); transaction.setErrorMessage(e.getMessage());
@@ -94,8 +97,8 @@ public class TransactionService {
// run post integration logic // run post integration logic
LogicPipeline<Transaction, Transaction> postTransactionLogic = LogicPipeline<Transaction, Transaction> postTransactionLogic =
new LogicPipeline<>(new PurchaseInvoiceHandler()) // todo: do this in a separate thread new LogicPipeline<>(purchaseInvoiceHandler) // todo: do this in a separate thread
.addHandler(new PurchaseInvoiceHandler()); // todo: do this in a separate thread .addHandler(purchasePaymentEntryHandler); // todo: do this in a separate thread
transaction = postTransactionLogic.execute(transaction); transaction = postTransactionLogic.execute(transaction);
transactionEvent.setStatus(transaction.getStatus()); transactionEvent.setStatus(transaction.getStatus());
@@ -204,7 +207,7 @@ public class TransactionService {
// run post transaction logic // run post transaction logic
LogicPipeline<Transaction, Transaction> postTransactionLogic = LogicPipeline<Transaction, Transaction> postTransactionLogic =
new LogicPipeline<>(new RecipientsUpdateHandler(recipientService)) new LogicPipeline<>(new RecipientsUpdateHandler(recipientService))
.addHandler(new SalesInvoiceHandler());// todo: do this in a separate thread .addHandler(salesInvoiceHandler);// todo: do this in a separate thread
transaction = postTransactionLogic.execute(transaction); transaction = postTransactionLogic.execute(transaction);
transactionEvent.setStatus(transaction.getStatus()); transactionEvent.setStatus(transaction.getStatus());
@@ -267,7 +270,7 @@ public class TransactionService {
try { try {
AdditionalData additionalData = additionalDataService.getAdditionalDataRepository() AdditionalData additionalData = additionalDataService.getAdditionalDataRepository()
.findByTransactionIdAndRequestType(id, RequestType.INTEGRATION).orElseThrow(() -> .findByTransactionIdAndRequestType(id.toString(), RequestType.INTEGRATION).orElseThrow(() ->
new ApiException("Additional data not found for transaction ID: " + id + " and request type: " + RequestType.INTEGRATION)); new ApiException("Additional data not found for transaction ID: " + id + " and request type: " + RequestType.INTEGRATION));
transaction.setAdditionalData(Utils.fromJson(additionalData.getJsonString(), List.class)); transaction.setAdditionalData(Utils.fromJson(additionalData.getJsonString(), List.class));
} catch (ApiException e) { } catch (ApiException e) {

View File

@@ -57,7 +57,7 @@ public class UserService implements UserDetailsService {
String token = jwtUtils.generateToken(savedUser); String token = jwtUtils.generateToken(savedUser);
return new AuthResponse(token, "Bearer", savedUser.getUsername(), return new AuthResponse(token, "Bearer", savedUser.getUsername(),
savedUser.getEmail(), savedUser.getPhone(), savedUser.getFirstName(), savedUser.getLastName()); savedUser.getEmail(), savedUser.getPhone(), savedUser.getFirstName(), savedUser.getLastName(), savedUser.getId());
} }
public AuthResponse login(LoginRequest request) { public AuthResponse login(LoginRequest request) {
@@ -71,6 +71,6 @@ public class UserService implements UserDetailsService {
String token = jwtUtils.generateToken(user); String token = jwtUtils.generateToken(user);
return new AuthResponse(token, "Bearer", user.getUsername(), return new AuthResponse(token, "Bearer", user.getUsername(),
user.getEmail(), user.getPhone(), user.getFirstName(), user.getLastName()); user.getEmail(), user.getPhone(), user.getFirstName(), user.getLastName(), user.getId());
} }
} }

View File

@@ -69,7 +69,7 @@ public class EconetConfirmationProcessor implements TransactionProcessorInterfac
transactionService.save(transaction); transactionService.save(transaction);
AdditionalData additionalData = AdditionalData.builder() AdditionalData additionalData = AdditionalData.builder()
.transaction(transaction) .transactionId(transaction.getId().toString())
.requestType(RequestType.CONFIRM) .requestType(RequestType.CONFIRM)
.jsonString(Utils.toJson(body.get("additionalData"))) .jsonString(Utils.toJson(body.get("additionalData")))
.build(); .build();

View File

@@ -69,7 +69,7 @@ public class ZesaConfirmationProcessor implements TransactionProcessorInterface
transactionService.save(transaction); transactionService.save(transaction);
AdditionalData additionalData = AdditionalData.builder() AdditionalData additionalData = AdditionalData.builder()
.transaction(transaction) .transactionId(transaction.getId().toString())
.requestType(RequestType.CONFIRM) .requestType(RequestType.CONFIRM)
.jsonString(Utils.toJson(body.get("additionalData"))) .jsonString(Utils.toJson(body.get("additionalData")))
.build(); .build();

View File

@@ -1,5 +1,6 @@
package zw.qantra.tm.domain.services.processors.handlers.erp; package zw.qantra.tm.domain.services.processors.handlers.erp;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -16,6 +17,7 @@ import zw.qantra.tm.domain.enums.Status;
import zw.qantra.tm.domain.models.Transaction; import zw.qantra.tm.domain.models.Transaction;
import zw.qantra.tm.domain.models.TransactionEvent; import zw.qantra.tm.domain.models.TransactionEvent;
import zw.qantra.tm.domain.models.User; import zw.qantra.tm.domain.models.User;
import zw.qantra.tm.domain.repositories.TransactionRepository;
import zw.qantra.tm.domain.services.TransactionEventService; import zw.qantra.tm.domain.services.TransactionEventService;
import zw.qantra.tm.domain.services.TransactionService; import zw.qantra.tm.domain.services.TransactionService;
import zw.qantra.tm.domain.services.UserService; import zw.qantra.tm.domain.services.UserService;
@@ -24,23 +26,17 @@ import zw.qantra.tm.exceptions.ApiException;
import zw.qantra.tm.rest.RestService; import zw.qantra.tm.rest.RestService;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.*;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
@Service @Service
@RequiredArgsConstructor
public class JournalEntryHandler implements LogicHandler<Transaction, Transaction>{ public class JournalEntryHandler implements LogicHandler<Transaction, Transaction>{
Logger logger = LoggerFactory.getLogger(JournalEntryHandler.class); Logger logger = LoggerFactory.getLogger(JournalEntryHandler.class);
@Autowired private final RestService restService;
private RestService restService; private final UserService userService;
@Autowired private final TransactionEventService transactionEventService;
private UserService userService; private final TransactionRepository transactionRepository;
@Autowired
private TransactionEventService transactionEventService;
@Autowired
private TransactionService transactionService;
@Value("${erpnext.url}") @Value("${erpnext.url}")
private String erpnextUrl; private String erpnextUrl;
@@ -104,28 +100,20 @@ public class JournalEntryHandler implements LogicHandler<Transaction, Transactio
headers.add("Authorization", basicAuth); headers.add("Authorization", basicAuth);
headers.add("Cookie", sid); headers.add("Cookie", sid);
User user = userService.getUserRepository().findById(transaction.getUserId()).orElse(null); User user = userService.getUserRepository().findById(UUID.fromString(transaction.getUserId())).orElse(null);
if(user == null) if(user == null)
throw new ApiException("Could not find user"); throw new ApiException("Could not find user");
JournalEntryAccountDto cashAccEntry = JournalEntryAccountDto.builder()
.doctype("Journal Entry Account")
.account(cashAccount)
.debitInAccountCurrency(transaction.getAmount())
.costCenter(costCenter)
.build();
JournalEntryAccountDto expenseEntry = JournalEntryAccountDto.builder() JournalEntryAccountDto expenseEntry = JournalEntryAccountDto.builder()
.doctype("Journal Entry Account") .doctype("Journal Entry Account")
.account(gatewayExpenseAccount) .account(gatewayExpenseAccount)
.debitInAccountCurrency(transaction.getGatewayCharge()) .debitInAccountCurrency(transaction.getGatewayCharge())
.costCenter(costCenter) .costCenter(costCenter)
.build(); .build();
JournalEntryAccountDto accRecievable = JournalEntryAccountDto.builder() JournalEntryAccountDto cashAccEntry = JournalEntryAccountDto.builder()
.doctype("Journal Entry Account") .doctype("Journal Entry Account")
.account(accountsReceivableAccount) .account(cashAccount)
.creditInAccountCurrency(transaction.getAmount().add(transaction.getGatewayCharge())) .creditInAccountCurrency(transaction.getGatewayCharge())
.party("Customer")
.costCenter(costCenter)
.build(); .build();
JournalEntryDto journalEntryDto = JournalEntryDto.builder() JournalEntryDto journalEntryDto = JournalEntryDto.builder()
@@ -133,7 +121,7 @@ public class JournalEntryHandler implements LogicHandler<Transaction, Transactio
.company(erpnextCompany) .company(erpnextCompany)
.postingDate(transaction.getCreatedAt().toLocalDate().toString()) .postingDate(transaction.getCreatedAt().toLocalDate().toString())
.docstatus(1) .docstatus(1)
.accounts(Arrays.asList(cashAccEntry, expenseEntry, accRecievable)) .accounts(Arrays.asList(cashAccEntry, expenseEntry))
.build(); .build();
try { try {
@@ -164,6 +152,6 @@ public class JournalEntryHandler implements LogicHandler<Transaction, Transactio
transactionEventService.save(transactionEvent); transactionEventService.save(transactionEvent);
return transactionService.save(transaction); return transactionRepository.save(transaction);
} }
} }

View File

@@ -1,5 +1,6 @@
package zw.qantra.tm.domain.services.processors.handlers.erp; package zw.qantra.tm.domain.services.processors.handlers.erp;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -14,6 +15,7 @@ import zw.qantra.tm.domain.enums.Status;
import zw.qantra.tm.domain.models.Transaction; import zw.qantra.tm.domain.models.Transaction;
import zw.qantra.tm.domain.models.TransactionEvent; import zw.qantra.tm.domain.models.TransactionEvent;
import zw.qantra.tm.domain.models.User; import zw.qantra.tm.domain.models.User;
import zw.qantra.tm.domain.repositories.TransactionRepository;
import zw.qantra.tm.domain.services.TransactionEventService; import zw.qantra.tm.domain.services.TransactionEventService;
import zw.qantra.tm.domain.services.TransactionService; import zw.qantra.tm.domain.services.TransactionService;
import zw.qantra.tm.domain.services.UserService; import zw.qantra.tm.domain.services.UserService;
@@ -23,23 +25,17 @@ import zw.qantra.tm.rest.RestService;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.*;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
@Service @Service
@RequiredArgsConstructor
public class PaymentEntryHandler implements LogicHandler<Transaction, Transaction>{ public class PaymentEntryHandler implements LogicHandler<Transaction, Transaction>{
Logger logger = LoggerFactory.getLogger(PaymentEntryHandler.class); Logger logger = LoggerFactory.getLogger(PaymentEntryHandler.class);
@Autowired private final RestService restService;
private RestService restService; private final UserService userService;
@Autowired private final TransactionEventService transactionEventService;
private UserService userService; private final TransactionRepository transactionRepository;
@Autowired
private TransactionEventService transactionEventService;
@Autowired
private TransactionService transactionService;
@Value("${erpnext.url}") @Value("${erpnext.url}")
private String erpnextUrl; private String erpnextUrl;
@@ -99,7 +95,7 @@ public class PaymentEntryHandler implements LogicHandler<Transaction, Transactio
headers.add("Authorization", basicAuth); headers.add("Authorization", basicAuth);
headers.add("Cookie", sid); headers.add("Cookie", sid);
User user = userService.getUserRepository().findById(transaction.getUserId()).orElse(null); User user = userService.getUserRepository().findById(UUID.fromString(transaction.getUserId())).orElse(null);
if(user == null) if(user == null)
throw new ApiException("Could not find user"); throw new ApiException("Could not find user");
@@ -156,6 +152,6 @@ public class PaymentEntryHandler implements LogicHandler<Transaction, Transactio
transactionEventService.save(transactionEvent); transactionEventService.save(transactionEvent);
return transactionService.save(transaction); return transactionRepository.save(transaction);
} }
} }

View File

@@ -1,5 +1,6 @@
package zw.qantra.tm.domain.services.processors.handlers.erp; package zw.qantra.tm.domain.services.processors.handlers.erp;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -10,9 +11,12 @@ import org.springframework.stereotype.Service;
import zw.qantra.tm.domain.dtos.erp.*; import zw.qantra.tm.domain.dtos.erp.*;
import zw.qantra.tm.domain.enums.RequestType; import zw.qantra.tm.domain.enums.RequestType;
import zw.qantra.tm.domain.enums.Status; import zw.qantra.tm.domain.enums.Status;
import zw.qantra.tm.domain.models.Provider;
import zw.qantra.tm.domain.models.Transaction; import zw.qantra.tm.domain.models.Transaction;
import zw.qantra.tm.domain.models.TransactionEvent; import zw.qantra.tm.domain.models.TransactionEvent;
import zw.qantra.tm.domain.models.User; import zw.qantra.tm.domain.models.User;
import zw.qantra.tm.domain.repositories.TransactionRepository;
import zw.qantra.tm.domain.services.ProviderService;
import zw.qantra.tm.domain.services.TransactionEventService; import zw.qantra.tm.domain.services.TransactionEventService;
import zw.qantra.tm.domain.services.TransactionService; import zw.qantra.tm.domain.services.TransactionService;
import zw.qantra.tm.domain.services.UserService; import zw.qantra.tm.domain.services.UserService;
@@ -20,21 +24,22 @@ import zw.qantra.tm.domain.services.processors.handlers.LogicHandler;
import zw.qantra.tm.exceptions.ApiException; import zw.qantra.tm.exceptions.ApiException;
import zw.qantra.tm.rest.RestService; import zw.qantra.tm.rest.RestService;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
@Service @Service
@RequiredArgsConstructor
public class PurchaseInvoiceHandler implements LogicHandler<Transaction, Transaction>{ public class PurchaseInvoiceHandler implements LogicHandler<Transaction, Transaction>{
Logger logger = LoggerFactory.getLogger(PurchaseInvoiceHandler.class); Logger logger = LoggerFactory.getLogger(PurchaseInvoiceHandler.class);
@Autowired private final RestService restService;
private RestService restService; private final UserService userService;
@Autowired private final TransactionEventService transactionEventService;
private UserService userService; private final TransactionRepository transactionRepository;
@Autowired private final ProviderService providerService;
private TransactionEventService transactionEventService;
@Autowired
private TransactionService transactionService;
@Value("${erpnext.url}") @Value("${erpnext.url}")
private String erpnextUrl; private String erpnextUrl;
@@ -94,28 +99,36 @@ public class PurchaseInvoiceHandler implements LogicHandler<Transaction, Transac
headers.add("Authorization", basicAuth); headers.add("Authorization", basicAuth);
headers.add("Cookie", sid); headers.add("Cookie", sid);
User user = userService.getUserRepository().findById(transaction.getUserId()).orElse(null); User user = userService.getUserRepository().findById(UUID.fromString(transaction.getUserId())).orElse(null);
if(user == null) if(user == null)
throw new ApiException("Could not find user"); throw new ApiException("Could not find user");
PurchaseInvoiceItemDto purchaseInvoiceItemDto = PurchaseInvoiceItemDto.builder() PurchaseInvoiceItemDto purchaseInvoiceItemDto = PurchaseInvoiceItemDto.builder()
.doctype("Purchase Invoice Item") .doctype("Purchase Invoice Item")
.uom("Unit") .uom("Nos")
.itemCode(transaction.getBillName()) .itemCode(transaction.getProviderLabel())
.itemName(transaction.getBillName()) .itemName(transaction.getProviderLabel())
.qty(transaction.getAmount()) .qty(transaction.getAmount())
.rate(1) .rate(1)
.amount(transaction.getAmount()) .amount(transaction.getAmount())
.build(); .build();
Provider provider = providerService.getProviderRepository()
.findByClientId(transaction.getBillClientId());
BigDecimal commissionPercentage = BigDecimal.valueOf(provider.getPercentageCommission())
.divide(BigDecimal.valueOf(100), 2, RoundingMode.CEILING);
BigDecimal commission = transaction.getAmount().multiply(commissionPercentage)
.setScale(2, RoundingMode.CEILING);
BigDecimal amount = transaction.getAmount().subtract(commission);
PurchaseTaxDto purchaseTaxDto = PurchaseTaxDto.builder() PurchaseTaxDto purchaseTaxDto = PurchaseTaxDto.builder()
.doctype("Purchase Tax") .doctype("Purchase Taxes and Charges")
.chargeType("On Net Total") .chargeType("On Net Total")
.accountHead(erpnextCommissionAccount) .accountHead(erpnextCommissionAccount)
.rate(transaction.getGatewayCharge().negate()) .rate(commission.negate())
.costCenter(erpnextCostCenter) .costCenter(erpnextCostCenter)
.taxAmount(transaction.getGatewayCharge().negate()) .taxAmount(commission.negate())
.total(transaction.getAmount().subtract(transaction.getGatewayCharge())) .total(amount)
.description("Discounts Received") .description("Discounts Received")
.build(); .build();
@@ -130,8 +143,8 @@ public class PurchaseInvoiceHandler implements LogicHandler<Transaction, Transac
.isPaid(0) .isPaid(0)
.currency(transaction.getDebitCurrency().toString()) .currency(transaction.getDebitCurrency().toString())
.conversionRate(1) .conversionRate(1)
.netTotal(transaction.getAmount().subtract(transaction.getGatewayCharge())) .netTotal(amount)
.grandTotal(transaction.getAmount().subtract(transaction.getGatewayCharge())) .grandTotal(amount)
.items(Collections.singletonList(purchaseInvoiceItemDto)) .items(Collections.singletonList(purchaseInvoiceItemDto))
.taxes(Collections.singletonList(purchaseTaxDto)) .taxes(Collections.singletonList(purchaseTaxDto))
.build(); .build();
@@ -164,6 +177,6 @@ public class PurchaseInvoiceHandler implements LogicHandler<Transaction, Transac
transactionEventService.save(transactionEvent); transactionEventService.save(transactionEvent);
return transactionService.save(transaction); return transactionRepository.save(transaction);
} }
} }

View File

@@ -1,5 +1,6 @@
package zw.qantra.tm.domain.services.processors.handlers.erp; package zw.qantra.tm.domain.services.processors.handlers.erp;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -11,9 +12,12 @@ import zw.qantra.tm.domain.dtos.erp.PaymentEntryDto;
import zw.qantra.tm.domain.dtos.erp.PaymentReferenceDto; import zw.qantra.tm.domain.dtos.erp.PaymentReferenceDto;
import zw.qantra.tm.domain.enums.RequestType; import zw.qantra.tm.domain.enums.RequestType;
import zw.qantra.tm.domain.enums.Status; import zw.qantra.tm.domain.enums.Status;
import zw.qantra.tm.domain.models.Provider;
import zw.qantra.tm.domain.models.Transaction; import zw.qantra.tm.domain.models.Transaction;
import zw.qantra.tm.domain.models.TransactionEvent; import zw.qantra.tm.domain.models.TransactionEvent;
import zw.qantra.tm.domain.models.User; import zw.qantra.tm.domain.models.User;
import zw.qantra.tm.domain.repositories.TransactionRepository;
import zw.qantra.tm.domain.services.ProviderService;
import zw.qantra.tm.domain.services.TransactionEventService; import zw.qantra.tm.domain.services.TransactionEventService;
import zw.qantra.tm.domain.services.TransactionService; import zw.qantra.tm.domain.services.TransactionService;
import zw.qantra.tm.domain.services.UserService; import zw.qantra.tm.domain.services.UserService;
@@ -22,24 +26,20 @@ import zw.qantra.tm.exceptions.ApiException;
import zw.qantra.tm.rest.RestService; import zw.qantra.tm.rest.RestService;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.*;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
@Service @Service
@RequiredArgsConstructor
public class PurchasePaymentEntryHandler implements LogicHandler<Transaction, Transaction>{ public class PurchasePaymentEntryHandler implements LogicHandler<Transaction, Transaction>{
Logger logger = LoggerFactory.getLogger(PurchasePaymentEntryHandler.class); Logger logger = LoggerFactory.getLogger(PurchasePaymentEntryHandler.class);
@Autowired private final RestService restService;
private RestService restService; private final UserService userService;
@Autowired private final TransactionEventService transactionEventService;
private UserService userService; private final TransactionRepository transactionRepository;
@Autowired private final ProviderService providerService;
private TransactionEventService transactionEventService;
@Autowired
private TransactionService transactionService;
@Value("${erpnext.url}") @Value("${erpnext.url}")
private String erpnextUrl; private String erpnextUrl;
@@ -57,7 +57,7 @@ public class PurchasePaymentEntryHandler implements LogicHandler<Transaction, Tr
private String accountsReceivableAccount; private String accountsReceivableAccount;
@Value("${erpnext.accounts.cash}") @Value("${erpnext.accounts.cash}")
private String cashAccount; private String cashAccount;
@Value("erpnext.accounts.payable") @Value("${erpnext.accounts.payable}")
private String payableAccount; private String payableAccount;
@Override @Override
@@ -101,11 +101,17 @@ public class PurchasePaymentEntryHandler implements LogicHandler<Transaction, Tr
headers.add("Authorization", basicAuth); headers.add("Authorization", basicAuth);
headers.add("Cookie", sid); headers.add("Cookie", sid);
User user = userService.getUserRepository().findById(transaction.getUserId()).orElse(null); User user = userService.getUserRepository().findById(UUID.fromString(transaction.getUserId())).orElse(null);
if(user == null) if(user == null)
throw new ApiException("Could not find user"); throw new ApiException("Could not find user");
BigDecimal amount = transaction.getAmount().subtract(transaction.getGatewayCharge()); Provider provider = providerService.getProviderRepository()
.findByClientId(transaction.getBillClientId());
BigDecimal commissionPercentage = BigDecimal.valueOf(provider.getPercentageCommission())
.divide(BigDecimal.valueOf(100), 2, RoundingMode.CEILING);
BigDecimal commission = transaction.getAmount().multiply(commissionPercentage)
.setScale(2, RoundingMode.CEILING);
BigDecimal amount = transaction.getAmount().subtract(commission);
PaymentReferenceDto paymentReferenceDto = PaymentReferenceDto.builder() PaymentReferenceDto paymentReferenceDto = PaymentReferenceDto.builder()
.referenceDoctype("Purchase Invoice") .referenceDoctype("Purchase Invoice")
@@ -113,6 +119,8 @@ public class PurchasePaymentEntryHandler implements LogicHandler<Transaction, Tr
.allocatedAmount(amount) .allocatedAmount(amount)
.build(); .build();
String refNo = transaction.getCreditRef().isBlank() ? transaction.getId().toString() : transaction.getCreditRef();
PaymentEntryDto paymentEntryDto = PaymentEntryDto.builder() PaymentEntryDto paymentEntryDto = PaymentEntryDto.builder()
.doctype("Payment Entry") .doctype("Payment Entry")
.company(erpnextCompany) .company(erpnextCompany)
@@ -124,7 +132,7 @@ public class PurchasePaymentEntryHandler implements LogicHandler<Transaction, Tr
.paidTo(payableAccount) .paidTo(payableAccount)
.paidAmount(amount) .paidAmount(amount)
.receivedAmount(amount) .receivedAmount(amount)
.referenceNo(transaction.getCreditRef()) .referenceNo(refNo)
.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))
@@ -158,6 +166,6 @@ public class PurchasePaymentEntryHandler implements LogicHandler<Transaction, Tr
transactionEventService.save(transactionEvent); transactionEventService.save(transactionEvent);
return transactionService.save(transaction); return transactionRepository.save(transaction);
} }
} }

View File

@@ -1,5 +1,7 @@
package zw.qantra.tm.domain.services.processors.handlers.erp; package zw.qantra.tm.domain.services.processors.handlers.erp;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -15,6 +17,7 @@ import zw.qantra.tm.domain.enums.Status;
import zw.qantra.tm.domain.models.Transaction; import zw.qantra.tm.domain.models.Transaction;
import zw.qantra.tm.domain.models.TransactionEvent; import zw.qantra.tm.domain.models.TransactionEvent;
import zw.qantra.tm.domain.models.User; import zw.qantra.tm.domain.models.User;
import zw.qantra.tm.domain.repositories.TransactionRepository;
import zw.qantra.tm.domain.services.TransactionEventService; import zw.qantra.tm.domain.services.TransactionEventService;
import zw.qantra.tm.domain.services.TransactionService; import zw.qantra.tm.domain.services.TransactionService;
import zw.qantra.tm.domain.services.UserService; import zw.qantra.tm.domain.services.UserService;
@@ -23,23 +26,17 @@ import zw.qantra.tm.exceptions.ApiException;
import zw.qantra.tm.rest.RestService; import zw.qantra.tm.rest.RestService;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.*;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
@Service @Service
@RequiredArgsConstructor
public class SalesInvoiceHandler implements LogicHandler<Transaction, Transaction>{ public class SalesInvoiceHandler implements LogicHandler<Transaction, Transaction>{
Logger logger = LoggerFactory.getLogger(SalesInvoiceHandler.class); Logger logger = LoggerFactory.getLogger(SalesInvoiceHandler.class);
@Autowired private final RestService restService;
private RestService restService; private final UserService userService;
@Autowired private final TransactionEventService transactionEventService;
private UserService userService; private final TransactionRepository transactionRepository;
@Autowired
private TransactionEventService transactionEventService;
@Autowired
private TransactionService transactionService;
@Value("${erpnext.url}") @Value("${erpnext.url}")
private String erpnextUrl; private String erpnextUrl;
@@ -64,7 +61,7 @@ public class SalesInvoiceHandler implements LogicHandler<Transaction, Transactio
.build(); .build();
transactionEventService.save(transactionEvent); transactionEventService.save(transactionEvent);
if(transaction.getStatus() == Status.SUCCESS){ if(transaction.getStatus() == Status.PENDING){
try { try {
logger.info("Creating sales invoice in ERPNext for successful transaction"); logger.info("Creating sales invoice in ERPNext for successful transaction");
@@ -95,7 +92,7 @@ public class SalesInvoiceHandler implements LogicHandler<Transaction, Transactio
headers.add("Authorization", basicAuth); headers.add("Authorization", basicAuth);
headers.add("Cookie", sid); headers.add("Cookie", sid);
User user = userService.getUserRepository().findById(transaction.getUserId()).orElse(null); User user = userService.getUserRepository().findById(UUID.fromString(transaction.getUserId())).orElse(null);
if(user == null) if(user == null)
throw new ApiException("Could not find user"); throw new ApiException("Could not find user");
@@ -107,7 +104,7 @@ public class SalesInvoiceHandler implements LogicHandler<Transaction, Transactio
.company(erpnextCompany) .company(erpnextCompany)
.docstatus(1) .docstatus(1)
.items(Arrays.asList(SalesInvoiceItemDto.builder() .items(Arrays.asList(SalesInvoiceItemDto.builder()
.itemCode(transaction.getBillName()) .itemCode(transaction.getProviderLabel())
.qty(1) .qty(1)
.rate(transaction.getAmount().add(transaction.getGatewayCharge())) .rate(transaction.getAmount().add(transaction.getGatewayCharge()))
.amount(transaction.getAmount().add(transaction.getGatewayCharge())) .amount(transaction.getAmount().add(transaction.getGatewayCharge()))
@@ -142,6 +139,6 @@ public class SalesInvoiceHandler implements LogicHandler<Transaction, Transactio
transactionEventService.save(transactionEvent); transactionEventService.save(transactionEvent);
return transactionService.save(transaction); return transactionRepository.save(transaction);
} }
} }

View File

@@ -75,13 +75,13 @@ public class StewardIntegrationProcessor implements TransactionProcessorInterfac
if (body.get("status").equals("SUCCESS")) { if (body.get("status").equals("SUCCESS")) {
transaction.setStatus(Status.SUCCESS); transaction.setStatus(Status.SUCCESS);
transaction.setResponseCode((String) body.get("responseCode")); transaction.setResponseCode((String) body.get("responseCode"));
transaction.setCreditRef((String) body.get("creditReference")); transaction.setCreditRef((String) body.get("creditRef"));
transaction.setAdditionalData(body.get("additionalData")); transaction.setAdditionalData(body.get("additionalData"));
transaction.setErrorMessage(null); // clear any previous error message transaction.setErrorMessage(null); // clear any previous error message
transactionService.save(transaction); transactionService.save(transaction);
AdditionalData additionalData = new AdditionalData(); AdditionalData additionalData = new AdditionalData();
additionalData.setTransaction(transaction); additionalData.setTransactionId(transaction.getId().toString());
additionalData.setRequestType(RequestType.INTEGRATION); additionalData.setRequestType(RequestType.INTEGRATION);
additionalData.setJsonString(Utils.toJson(body.get("additionalData"))); additionalData.setJsonString(Utils.toJson(body.get("additionalData")));
additionalDataService.getAdditionalDataRepository().save(additionalData); additionalDataService.getAdditionalDataRepository().save(additionalData);

View File

@@ -1,6 +1,7 @@
package zw.qantra.tm.domain.services.processors.payments; package zw.qantra.tm.domain.services.processors.payments;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@@ -19,7 +20,10 @@ import java.math.BigInteger;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
@Service("REQUEST_MPGS_WEB") @Service("REQUEST_MPGS_WEB")
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -123,6 +127,12 @@ public class MPGSWebPaymentProcessor implements TransactionProcessorInterface {
LinkedHashMap body = (LinkedHashMap) response.get("body"); LinkedHashMap body = (LinkedHashMap) response.get("body");
// todo: REMOVE THIS
body.put("status", "SUCCESS");
LinkedHashMap hashMap = new LinkedHashMap();
hashMap.put("creditReference", RandomStringUtils.randomNumeric(10));
body.put("transaction", hashMap);
// only update tran status if polling returns a success // only update tran status if polling returns a success
if(!body.get("status").equals("SUCCESS")){ if(!body.get("status").equals("SUCCESS")){
logger.error(body.get("errorMessage").toString()); logger.error(body.get("errorMessage").toString());

View File

@@ -34,9 +34,13 @@ erpnext.api.key=1102c6da0312f75
erpnext.api.secret=e3d95692bcf16c7 erpnext.api.secret=e3d95692bcf16c7
erpnext.username=Administrator erpnext.username=Administrator
erpnext.password=admin erpnext.password=admin
erpnext.company=Qantra Payments erpnext.company=Qantra
erpnext.item=Payment Service erpnext.accounts.cash=Bank Account - Q
erpnext.income.account=5111 - Sales Income - Q erpnext.accounts.gatewayexpense=Gateway Expenses - Q
erpnext.accounts.accountsreceivable=Debtors - Q
erpnext.accounts.commission=Discounts Received - Q
erpnext.accounts.payable=Creditors - Q
erpnext.income.account=Sales Income - Q
erpnext.cost.center=Main - Q erpnext.cost.center=Main - Q
erpnext.tax.account=VAT - Q erpnext.tax.account=VAT - Q
erpnext.tax.rate=15.0 erpnext.tax.rate=15.0