public void RunAutomatic() { var batchState = _batchRepository.Retrieve(); _executionLogService.Log("Refreshing Inventory from Shopify"); Run(batchState.ShopifyProductsGetEnd); }
private void UpsertOrderAndCustomer(Order order) { var monsterCustomerRecord = _shopifyCustomerPull.UpsertCustomer(order.customer); using (var transaction = _orderRepository.BeginTransaction()) { var existingOrder = _orderRepository.RetrieveOrder(order.id); if (existingOrder == null) { var newOrder = new ShopifyOrder(); newOrder.ShopifyOrderId = order.id; newOrder.ShopifyOrderNumber = order.name; newOrder.ShopifyTotalPrice = order.total_price; newOrder.ShopifyFinancialStatus = order.financial_status; newOrder.ShopifyFulfillmentStatus = order.fulfillment_status; newOrder.ShopifyIsCancelled = order.IsCancelled; newOrder.ShopifyAreAllItemsRefunded = order.AreAllLineItemsRefunded; newOrder.ShopifyTotalQuantity = order.NetOrderedQuantity; newOrder.NeedsOrderPut = false; newOrder.NeedsTransactionGet = true; newOrder.ErrorCount = 0; newOrder.Ignore = false; newOrder.CustomerMonsterId = monsterCustomerRecord.MonsterId; newOrder.DateCreated = DateTime.UtcNow; newOrder.LastUpdated = DateTime.UtcNow; _executionLogService.Log(LogBuilder.DetectedNewShopifyOrder(newOrder)); _orderRepository.InsertOrder(newOrder); } else { existingOrder.ShopifyTotalPrice = order.total_price; existingOrder.ShopifyFinancialStatus = order.financial_status; existingOrder.ShopifyFulfillmentStatus = order.fulfillment_status; existingOrder.ShopifyIsCancelled = order.IsCancelled; existingOrder.ShopifyAreAllItemsRefunded = order.AreAllLineItemsRefunded; existingOrder.ShopifyTotalQuantity = order.NetOrderedQuantity; if (existingOrder.StatusChangeDetected(order)) { existingOrder.NeedsOrderPut = true; } existingOrder.NeedsTransactionGet = true; existingOrder.LastUpdated = DateTime.UtcNow; _executionLogService.Log(LogBuilder.DetectedUpdateShopifyOrder(existingOrder)); _orderRepository.SaveChanges(); } _shopifyJsonService.Upsert(ShopifyJsonType.Order, order.id, order.SerializeToJson()); transaction.Commit(); } }
public void StartEndToEndSync(string cron) { RecurringJob.AddOrUpdate <JobRunner>( RecurringEndToEndSyncJobId, x => x.TriggerEndToEndSync(_instanceContext.InstanceId), cron, TimeZoneInfo.Utc); _executionLogService.Log("End-to-End Sync - Recurring Job Started "); }
public void RunAutomatic() { var batchState = _batchStateRepository.Retrieve(); _executionLogService.Log("Refreshing Stock Items from Acumatica"); RunStockItems(batchState.AcumaticaStockItemGetEnd); _executionLogService.Log("Refreshing Inventory Status from Acumatica"); RunInventoryStatus(batchState.AcumaticaInventoryStatusGetEnd); }
private bool TestIsShopifyPutEnabled() { if (MonsterConfig.Settings.DisableShopifyPut) { _executionLogService.Log(LogBuilder.ShopifyPutDisabled()); return(false); } else { return(true); } }
private AcumaticaSalesOrder FindAndUpsertSalesOrderData(SalesOrder order) { using (var transaction = _orderRepository.BeginTransaction()) { var orderNbr = order.OrderNbr.value; var shopifyOrderId = order.CustomerOrder.value.ToLongAlt(-1); var orderRecord = _orderRepository.RetrieveSalesOrder(orderNbr); bool unknownRef = false; if (orderRecord == null) { // Skip Sales Orders that were not intentionally loaded into Acumatica // orderRecord = _orderRepository.FindSalesOrderByShopifyId(shopifyOrderId); if (orderRecord == null) { return(null); } unknownRef = true; } if (unknownRef) { _executionLogService.Log( LogBuilder.FillingUnknownAcumaticaSalesOrderRef(orderRecord.ShopifyOrder, orderRecord)); } _executionLogService.Log(LogBuilder.DetectedUpdateAcumaticaOrder(orderRecord)); _acumaticaJsonService.Upsert( AcumaticaJsonType.SalesOrderShipments, orderNbr, SalesOrderType.SO, order.SerializeToJson()); // Again, to be tested // // orderRecord.AcumaticaQtyTotal = (int)order.Details.Sum(x => x.OrderQty.value); orderRecord.Ingest(order); orderRecord.LastUpdated = DateTime.UtcNow; _orderRepository.SaveChanges(); transaction.Commit(); return(orderRecord); } }
public void SyncOrdersToAcumatica() { var settings = _settingsRepository.RetrieveSettings(); var msg = $"Starting Order Sync with {settings.MaxParallelAcumaticaSyncs} worker(s)"; _executionLogService.Log(msg); ServicePointManager.DefaultConnectionLimit = 100; var queue = _acumaticaOrderSync.BuildOrderPutQueue(); // This can be abstracted var threads = new List <Thread>(); for (var i = 0; i < settings.MaxParallelAcumaticaSyncs; i++) { var t = new Thread(() => OrderSyncInChildScope(queue)); t.Start(); threads.Add(t); } foreach (var t in threads) { t.Join(); } }
private void PullTransactionsFromShopify(ShopifyOrder orderRecord) { var transactionsJson = _orderApi.RetrieveTransactions(orderRecord.ShopifyOrderId); var transactions = transactionsJson.DeserializeFromJson <TransactionList>(); var order = _shopifyJsonService.RetrieveOrder(orderRecord.ShopifyOrderId); foreach (var transaction in transactions.transactions) { var transactionRecord = _orderRepository.RetrieveTransaction(transaction.id); if (transactionRecord != null) { transactionRecord.LastUpdated = DateTime.UtcNow; _orderRepository.SaveChanges(); continue; } using (var dbTransaction = _orderRepository.BeginTransaction()) { var record = new ShopifyTransaction(); if (transaction.kind == TransactionKind.Refund) { record.IsSyncableToPayment = true; var refund = order.RefundByTransaction(transaction.id); if (refund != null) { record.ShopifyRefundId = refund.id; record.IsPureCancel = refund.IsPureCancel; } } if (transaction.kind == TransactionKind.Capture || transaction.kind == TransactionKind.Sale) { record.IsSyncableToPayment = true; } record.ShopifyOrderId = transaction.order_id; record.ShopifyTransactionId = transaction.id; record.ShopifyStatus = transaction.status; record.ShopifyKind = transaction.kind; record.ShopifyGateway = transaction.gateway; record.ShopifyAmount = transaction.amount; record.ShopifyOrderMonsterId = orderRecord.MonsterId; record.DateCreated = DateTime.UtcNow; record.LastUpdated = DateTime.UtcNow; _logService.Log(LogBuilder.DetectedNewShopifyTransaction(record)); _orderRepository.InsertTransaction(record); _shopifyJsonService.Upsert( ShopifyJsonType.Transaction, transaction.id, transaction.SerializeToJson()); dbTransaction.Commit(); } } orderRecord.NeedsTransactionGet = false; _orderRepository.SaveChanges(); }
public bool DetectCurrentJobInterrupt() { if (!_currentScopeMonitorId.HasValue) { throw new Exception("Must invoke IdentifyCurrentJobType() before calling this method"); } if (IsInterrupted(_currentScopeMonitorId.Value)) { _executionLogService.Log(LogBuilder.JobExecutionIsInterrupted()); return(true); } else { return(false); } }
public AcumaticaCustomer PushCustomer(ShopifyCustomer shopifyRecord) { if (shopifyRecord.HasMatch()) { // Already matched - push updates to Acumatica // UpdateCustomerInAcumatica(shopifyRecord); return(shopifyRecord.AcumaticaCustomer); } else { // No matching record // var customerInAcumatica = FindAcumaticaCustomer(shopifyRecord); if (customerInAcumatica == null) { // Unable to locate Customer in Acumatica? Push brand new Customer to Acumatica API // _logService.Log(LogBuilder.CreateAcumaticaCustomer(shopifyRecord)); var newCustomer = BuildCustomer(shopifyRecord); var newCustomerResult = _customerClient.WriteCustomer(newCustomer); // Then create a SQL record thereof // var acumaticaRecord = CreateAcumaticaCustomerRecord(shopifyRecord, newCustomerResult); return(acumaticaRecord); } else { // Found Customer in Acumatica! Create a SQL record for it... // _logService.Log(LogBuilder.AutomatchingCustomers(shopifyRecord, customerInAcumatica)); var acumaticaRecord = CreateAcumaticaCustomerRecord(shopifyRecord, customerInAcumatica); // ... and then now push an update from Shopify into Acumatica // TODO - make this update in Acumatica optional // UpdateCustomerInAcumatica(shopifyRecord); return(acumaticaRecord); } } }
public void ReconcileSettingsWithRefData() { var referenceData = RetrieveRefData(); var settings = _settingsRepository.RetrieveSettings(); if (referenceData.TimeZones.All(x => x.TimeZoneId != settings.AcumaticaTimeZone)) { settings.AcumaticaTimeZone = null; } if (!referenceData .ItemClasses.Any( x => x.ItemClass == settings.AcumaticaDefaultItemClass && x.PostingClass == settings.AcumaticaDefaultPostingClass)) { settings.AcumaticaDefaultItemClass = null; settings.AcumaticaDefaultPostingClass = null; } if (settings.AcumaticaTaxableCategory != null && referenceData.TaxCategories.All(x => x != settings.AcumaticaTaxableCategory)) { _logService.Log($"Tax Category {settings.AcumaticaTaxableCategory} is missing from Acumatica"); settings.AcumaticaTaxableCategory = null; } if (settings.AcumaticaTaxExemptCategory != null && referenceData.TaxCategories.All(x => x != settings.AcumaticaTaxExemptCategory)) { _logService.Log($"Tax Category {settings.AcumaticaTaxExemptCategory} is missing from Acumatica"); settings.AcumaticaTaxExemptCategory = null; } if (settings.AcumaticaTaxZone != null && referenceData.TaxZones.All(x => x != settings.AcumaticaTaxZone)) { _logService.Log($"Tax Zone {settings.AcumaticaTaxZone} is missing from Acumatica"); settings.AcumaticaTaxZone = null; } if (settings.AcumaticaCustomerClass != null && referenceData.CustomerClasses.All(x => x != settings.AcumaticaCustomerClass)) { _logService.Log($"Customer Class {settings.AcumaticaCustomerClass} is missing from Acumatica"); settings.AcumaticaCustomerClass = null; } _settingsRepository.SaveChanges(); }
public ActionResult StartEndToEndRecurring(int scheduleId) { var option = RecurringSchedule.Options.First(x => x.Id == scheduleId); _logRepository.Log($"Scheduling Recurring End-to-End Sync - run {option.Desc}"); _recurringJobService.StartEndToEndSync(option.Cron); var settings = _settingsRepository.RetrieveSettings(); settings.LastRecurringSchedule = scheduleId; _settingsRepository.SaveChanges(); return(JsonNetResult.Success()); }
public void Run() { if (MonsterConfig.Settings.DisableShopifyPut) { _logService.Log(LogBuilder.ShopifyPutDisabled()); return; } var salesOrderRefs = _syncOrderRepository.RetrieveUnsyncedSoShipments(); RunWorker(salesOrderRefs); }
public void TestConnection() { try { _executionLogService.Log("Connecting to Shopify..."); _orderApi.RetrieveCount(); _stateRepository.UpdateSystemState(x => x.ShopifyConnState, StateCode.Ok); } catch (Exception ex) { _logger.Error(ex); _stateRepository.UpdateSystemState(x => x.ShopifyConnState, StateCode.SystemFault); } }
public void Run() { if (MonsterConfig.Settings.DisableShopifyPut) { _executionLogService.Log(LogBuilder.ShopifyPutDisabled()); return; } RunPriceCostWeightUpdates(); var settings = _settingsRepository.RetrieveSettings(); if (settings.InventorySyncAvailableQty) { RunInventoryUpdate(); } }
private int RunStockItemImport(AcumaticaStockItemImportContext context, ShopifyVariant variant) { var matchingShopifySkus = _syncRepository.RetrieveNonMissingVariants(variant.StandardizedSku()); if (matchingShopifySkus.Count > 1) { _logService.Log($"Stock Item Import: {variant.LogDescriptor()} has duplicates in Shopify - aborting"); return(StockItemPutResult.NoAction); } // Attempt to Auto-match // if (variant.IsMatched()) { _logService.Log($"Stock Item Import: {variant.LogDescriptor()} already matched - aborting"); return(StockItemPutResult.NoAction); } var stockItem = _syncRepository.RetrieveStockItem(variant.StandardizedSku()); if (stockItem != null) { if (stockItem.IsMatched()) { var msg = $"Stock Item Import: {variant.LogDescriptor()} SKU already synchronized"; _logService.Log(msg); return(StockItemPutResult.NoAction); } else { var msg = $"Stock Item Import: auto-matched {stockItem.LogDescriptor()} to {variant.LogDescriptor()}"; _logService.Log(msg); _syncRepository.InsertItemSync(variant, stockItem, context.IsSyncEnabled); return(StockItemPutResult.Synchronized); } } // Abort any further processing if (context.SynchronizeOnly == true) { return(StockItemPutResult.NoAction); } // With neither duplicates or Auto-matching having succeeded, // ... we'll create a new Stock Item in Acumatica // StockItemPush(context, variant); context.VariantsForNextInventoryReceipt.Add(variant); return(StockItemPutResult.CreatedStockItem); }
public void RunAddToProduct(ShopifyAddVariantImportContext context) { if (MonsterConfig.Settings.DisableShopifyPut) { _logService.Log(LogBuilder.ShopifyPutDisabled()); return; } // Attempt to auto-match Item Ids SKU's that exists // AutomatchExistingSkus(context); // Creates payload for Shopify API only including valid Variants not in Shopify // var createVariantList = CleanAndBuildVariantPayload(context.AcumaticaItemIds); var shopifyProductRecord = _syncInventoryRepository.RetrieveProduct(context.ShopifyProductId); // Add Variants thru Shopify API // foreach (var createVariant in createVariantList) { _logService.Log(LogBuilder.CreatingShopifyVariant(createVariant)); // Invoke Shopify API and create new Variant // var json = new { variant = createVariant }.SerializeToJson(); var resultJson = _productApi.AddVariant(context.ShopifyProductId, json); var result = resultJson.DeserializeFromJson <VariantParent>(); // Create Variant Record and Sync // var variantRecord = _shopifyInventoryGet.CreateNewVariantRecord(shopifyProductRecord.MonsterId, result.variant); CreateSyncRecord(createVariant.sku.StandardizedSku(), variantRecord); } // Need to refresh local cache of Product, so Inventory Levels records reflect current state // _shopifyInventoryGet.Run(context.ShopifyProductId); foreach (var stockItemId in context.AcumaticaItemIds) { // Update the Inventory data // RunInventoryUpdate(stockItemId); } }
public void RunOrder(long shopifyOrderId) { try { CorrectSalesOrderWithUnknownRef(shopifyOrderId); // *** SAVE THIS, JONES! - This little branch of logic increases throughput!! // var orderPreAction = _pendingActionService.Create(shopifyOrderId).OrderAction; if (orderPreAction.ActionCode == ActionCode.UpdateInAcumatica && !orderPreAction.IsValid) { _acumaticaOrderPaymentPut.ProcessOrder(shopifyOrderId); } var orderAction = _pendingActionService.Create(shopifyOrderId).OrderAction; if (!orderAction.IsValid) { _logService.Log(LogBuilder.SkippingInvalidShopifyOrder(shopifyOrderId)); return; } if (orderAction.ActionCode == ActionCode.CreateInAcumatica) { CreateSalesOrder(shopifyOrderId); _acumaticaOrderPaymentPut.ProcessOrder(shopifyOrderId); return; } if (orderAction.ActionCode == ActionCode.UpdateInAcumatica) { _acumaticaOrderPaymentPut.ProcessOrder(shopifyOrderId); UpdateExistingSalesOrder(shopifyOrderId); return; } if (orderAction.ActionCode == ActionCode.CreateBlankSyncRecord) { CreateBlankSalesOrderRecord(shopifyOrderId); return; } } catch (Exception ex) { _systemLogger.Error(ex); _logService.Log($"Encountered error syncing Shopify Order {shopifyOrderId}"); _syncOrderRepository.IncreaseOrderErrorCount(shopifyOrderId); } }
// Disconnects Warehouses from Deactivated Locations // public void Run() { var locations = _repository.RetrieveDeactivatedMatchedLocations(); foreach (var location in locations) { // The ToList() allows us to mutate the collection foreach (var sync in location.ShopAcuWarehouseSyncs.ToList()) { using (var transaction = _repository.BeginTransaction()) { var log = $"Removed match from Deactivated Location {location.ShopifyLocationName} " + $"to Warehouse {sync.AcumaticaWarehouse.AcumaticaWarehouseId}"; _repository.DeleteWarehouseSync(sync); _executionLogService.Log(log); transaction.Commit(); } } } }
private void ExecuteJob(Guid instanceId, Action action, long jobMonitorId) { _instanceContext.Initialize(instanceId); _jobMonitoringService.RegisterCurrentScopeMonitorId(jobMonitorId); var monitor = _jobMonitoringService.RetrieveCurrentScopeMonitor(); try { if (!InstanceLock.Acquire(instanceId.ToString())) { var msg = $"Failed to acquire lock '{InstanceLock.MethodName}'"; _executionLogService.Log(msg, LogLevel.Debug); return; } if (_jobMonitoringService.IsCorrupted(jobMonitorId)) { var msg = $"Job is missing or corrupted"; _executionLogService.Log(msg); _jobMonitoringService.CleanupPostExecution(jobMonitorId); InstanceLock.Free(instanceId.ToString()); return; } if (_jobMonitoringService.IsInterrupted(jobMonitorId)) { var msg = $"Job is missing or has received stop signal"; _executionLogService.Log(msg); _jobMonitoringService.CleanupPostExecution(jobMonitorId); InstanceLock.Free(instanceId.ToString()); return; } // Phew - we made it! Execute the requested task // _executionLogService.Log( BackgroundJobType.Name[monitor.BackgroundJobType] + " - starting"); action(); _jobMonitoringService.CleanupPostExecution(jobMonitorId); // *** IMPORTANT - do not refactor this to use-finally, else it will // ... break concurrency locking // InstanceLock.Free(instanceId.ToString()); } catch (Exception ex) { InstanceLock.Free(instanceId.ToString()); // If this is One-Time Job, this will remove the Monitor now that the Job has failed // _jobMonitoringService.CleanupPostExecution(jobMonitorId); _executionLogService.Log( BackgroundJobType.Name[monitor.BackgroundJobType] + " - encountered an error"); _logger.Error(ex); } finally { _executionLogService.Log( BackgroundJobType.Name[monitor.BackgroundJobType] + " - finished"); } }
private void ProcessOrderAux(long shopifyOrderId) { try { ProcessOrder(shopifyOrderId); } catch (Exception ex) { _systemLogger.Error(ex); _logService.Log($"Encounter error syncing Payments for Shopify Order {shopifyOrderId}"); _syncOrderRepository.IncreaseOrderErrorCount(shopifyOrderId); } }
public void TestConnection() { _executionLogService.Log("Testing Acumatica Connection"); _acumaticaHttpContext.SessionRun(() => { }, throwException: true); }