diff --git a/src/main/java/com/impactupgrade/nucleus/environment/EnvironmentConfig.java b/src/main/java/com/impactupgrade/nucleus/environment/EnvironmentConfig.java index d79d39277..67eeed3ed 100644 --- a/src/main/java/com/impactupgrade/nucleus/environment/EnvironmentConfig.java +++ b/src/main/java/com/impactupgrade/nucleus/environment/EnvironmentConfig.java @@ -165,7 +165,7 @@ public static class Recaptcha { public static class QuickBooks extends Platform { public String realmId = ""; - public String accountId = ""; + public String apiBaseUrl = ""; } public QuickBooks quickbooks = new QuickBooks(); diff --git a/src/main/java/com/impactupgrade/nucleus/service/segment/QuickBooksAccountingPlatformService.java b/src/main/java/com/impactupgrade/nucleus/service/segment/QuickBooksAccountingPlatformService.java index 943c033bb..17e06b80f 100644 --- a/src/main/java/com/impactupgrade/nucleus/service/segment/QuickBooksAccountingPlatformService.java +++ b/src/main/java/com/impactupgrade/nucleus/service/segment/QuickBooksAccountingPlatformService.java @@ -1,5 +1,6 @@ package com.impactupgrade.nucleus.service.segment; +import com.google.gson.Gson; import com.impactupgrade.nucleus.environment.Environment; import com.impactupgrade.nucleus.model.CrmContact; import com.impactupgrade.nucleus.model.PaymentGatewayDeposit; @@ -31,6 +32,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -40,24 +42,32 @@ @Slf4j public class QuickBooksAccountingPlatformService implements AccountingPlatformService { + private String clientId; + private String clientSecret; + private String accessToken; private String refreshToken; private Long lastTokenUpdateTimestamp; - private static final String CLIENT_ID = "ABlbf9rOF8Z9M3G5OlStMfi2wmNkB4jJMDXtad6yZpfPkMvVlz"; - private static final String CLIENT_SECRET = "HdI0aUhPI7wfmFFP1Wl4MLMtHwn7sLgXf83EkuPu"; - - private static final String QB_API_BASE_URL = "https://sandbox-quickbooks.api.intuit.com"; - private static final String REALM_ID = "4620816365179776870"; - private static final Long ACCESS_TOKEN_EXPIRES_IN = 3600l; + private static final Gson GSON = new Gson(); + + private String realmId; + private String apiBaseUrl; public String name() { return "quickbooks"; } - public void init(Environment env) { - // TODO: + public void init(Environment environment) { + this.clientId = environment.getConfig().quickbooks.clientId; + this.clientSecret = environment.getConfig().quickbooks.clientSecret; + + this.accessToken = environment.getConfig().quickbooks.accessToken; + this.refreshToken = environment.getConfig().quickbooks.refreshToken; + + this.realmId = environment.getConfig().quickbooks.realmId; + this.apiBaseUrl = environment.getConfig().quickbooks.apiBaseUrl; } public void addDeposits(List paymentGatewayDeposits) { @@ -77,7 +87,10 @@ public void addDeposits(List paymentGatewayDeposits) { // Check if some transactions already exist List existingPayments; try { + log.info("Getting existing payments..."); existingPayments = getExistingPayments(); + log.info("Payments found: {}", existingPayments.size()); + } catch (Exception e) { log.error("Failed to get payments info! {}", getExceptionDetails(e)); return; @@ -87,10 +100,10 @@ public void addDeposits(List paymentGatewayDeposits) { if (CollectionUtils.isEmpty(existingPayments)) { transactionsToProcess.addAll(transactions); } else { - Set existingPaymentsSources = getPaymentSources(existingPayments); + Set existingPaymentsPrivateNotes = getPaymentPrivateNotes(existingPayments); for (PaymentGatewayEvent transaction : transactions) { String transactionId = transaction.getTransactionId(); - if (existingPaymentsSources.contains(transactionId)) { + if (existingPaymentsPrivateNotes.contains(transactionId)) { log.info("Transaction id {} already exists. Skipping...", transactionId); } else { transactionsToProcess.add(transaction); @@ -103,10 +116,15 @@ public void addDeposits(List paymentGatewayDeposits) { return; } + log.info("Transactions to process: {}", transactionsToProcess.size()); + // Get or create contacts List existingCustomers; try { + log.info("Getting existing customers..."); existingCustomers = getExistingCutomers(); + log.info("Customers found: {}", existingCustomers.size()); + } catch (Exception e) { log.error("Failed to get customers info! {}", getExceptionDetails(e)); return; @@ -114,18 +132,18 @@ public void addDeposits(List paymentGatewayDeposits) { Map existingCustomerMapped = mapCustomers(existingCustomers); List crmContacts = collectCrmContacts(transactions); + Map crmContactMapped = mapCrmContacts(crmContacts); + List contactsToCreate = new ArrayList<>(); if (CollectionUtils.isEmpty(existingCustomers)) { // Need to create customer for every crm contact contactsToCreate.addAll(crmContacts); } else { - for (CrmContact crmContact : crmContacts) { - if (StringUtils.isEmpty(crmContact.firstName) || StringUtils.isEmpty(crmContact.lastName)) { - log.info("Either first name or last name are empty! Skipping contact {}...", crmContact.id); - continue; - } - String displayName = getCrmContactDisplayName(crmContact); + for (Map.Entry entry : crmContactMapped.entrySet()) { + String displayName = entry.getKey(); + CrmContact crmContact = entry.getValue(); + if (existingCustomerMapped.containsKey(displayName)) { log.info("Customer with name {} already exists. Skipping...", displayName); } else { @@ -172,6 +190,13 @@ private List collectTransactions(List getPaymentPrivateNotes(List existingPayments) { + if (CollectionUtils.isEmpty(existingPayments)) { + return Collections.emptySet(); + } + return existingPayments.stream().map(Payment::getPrivateNote).collect(Collectors.toSet()); + } + private List collectCrmContacts(List transactions) { if (CollectionUtils.isEmpty(transactions)) { return Collections.emptyList(); @@ -179,25 +204,36 @@ private List collectCrmContacts(List transactio return transactions.stream().map(PaymentGatewayEvent::getCrmContact).collect(Collectors.toList()); } - private Set getPaymentSources(List payments) { - if (CollectionUtils.isEmpty(payments)) { - return Collections.emptySet(); + private Map mapCustomers(List customers) { + if (CollectionUtils.isEmpty(customers)) { + return Collections.emptyMap(); } - return payments.stream().map(Payment::getTxnSource).collect(Collectors.toSet()); + return customers.stream().collect(Collectors.toMap(c -> c.getDisplayName(), c -> c)); } - private static Map mapCustomers(List customers) { - if (CollectionUtils.isEmpty(customers)) { + private Map mapCrmContacts(List crmContacts) { + if (CollectionUtils.isEmpty(crmContacts)) { return Collections.emptyMap(); } - return customers.stream().collect(Collectors.toMap(c -> c.getDisplayName(), c -> c)); + Map crmContactMapped = new HashMap<>(); + + for (CrmContact crmContact : crmContacts) { + if (StringUtils.isEmpty(crmContact.firstName) || StringUtils.isEmpty(crmContact.lastName)) { + log.info("Either first name or last name are empty! Skipping contact {}...", crmContact.id); + continue; + } + String displayName = getCrmContactDisplayName(crmContact); + crmContactMapped.computeIfAbsent(displayName, k -> crmContact); + } + + return crmContactMapped; } private String getCrmContactDisplayName(CrmContact crmContact) { if (Objects.isNull(crmContact)) { return null; } - return crmContact.firstName + " " + crmContact.firstName; + return crmContact.firstName + " " + crmContact.lastName; } private String getExceptionDetails(Exception e) { @@ -216,7 +252,7 @@ private void getAccessToken() throws OAuthException { log.info("Token is valid. No need to refresh."); } else { - OAuth2Config oauth2Config = new OAuth2Config.OAuth2ConfigBuilder(CLIENT_ID, CLIENT_SECRET) //set client id, secret + OAuth2Config oauth2Config = new OAuth2Config.OAuth2ConfigBuilder(clientId, clientSecret) //set client id, secret .callDiscoveryAPI(com.intuit.oauth2.config.Environment.SANDBOX) // call discovery API to populate urls .buildConfig(); OAuth2PlatformClient client = new OAuth2PlatformClient(oauth2Config); @@ -270,7 +306,9 @@ private Customer asCustomer(CrmContact crmContact) { Customer customer = new Customer(); customer.setGivenName(crmContact.firstName); customer.setFamilyName(crmContact.lastName); - customer.setDisplayName(crmContact.firstName + " " + crmContact.lastName); + String displayName = getCrmContactDisplayName(crmContact); + customer.setDisplayName(displayName); + customer.setContactName(displayName); return customer; } @@ -317,8 +355,10 @@ private Payment asPayment(PaymentGatewayEvent transaction) { } Payment payment = new Payment(); // TODO: set all required fields (QB requires only amount and cutomer ref/id) - payment.setTotalAmt(new BigDecimal(transaction.getTransactionOriginalAmountInDollars())); - payment.setTxnSource(transaction.getTransactionId()); // ? + BigDecimal transactionAmount = new BigDecimal(transaction.getTransactionOriginalAmountInDollars()); + payment.setTotalAmt(transactionAmount); + payment.setTxnDate(transaction.getTransactionDate().getTime()); + payment.setPrivateNote(transaction.getTransactionId()); return payment; } @@ -337,10 +377,10 @@ private List createPayments(List payments) throws FMSException } private QueryResult getQueryResult(String sqlQuery) throws FMSException { - Config.setProperty(Config.BASE_URL_QBO, QB_API_BASE_URL + "/v3/company"); + Config.setProperty(Config.BASE_URL_QBO, apiBaseUrl + "/v3/company"); OAuth2Authorizer oauth = new OAuth2Authorizer(accessToken); - Context context = new Context(oauth, ServiceType.QBO, REALM_ID); + Context context = new Context(oauth, ServiceType.QBO, realmId); DataService service = new DataService(context); QueryResult queryResult = service.executeQuery(sqlQuery); @@ -363,10 +403,10 @@ private List getEntities(Query query) throws FMSException } private T addEntity(T entity) throws FMSException { - Config.setProperty(Config.BASE_URL_QBO, QB_API_BASE_URL + "/v3/company"); // ? + Config.setProperty(Config.BASE_URL_QBO, apiBaseUrl + "/v3/company"); // ? OAuth2Authorizer oauth = new OAuth2Authorizer(accessToken); - Context context = new Context(oauth, ServiceType.QBO, REALM_ID); + Context context = new Context(oauth, ServiceType.QBO, realmId); DataService service = new DataService(context); T added = service.add(entity);