public static async Task SourceUnsourcedOrders( [TimerTrigger("0 */5 * * * *")] TimerInfo timer, // every 5 mins [CosmosDB(ConnectionStringSetting = "AzureCosmosDBConnectionString"), SwaggerIgnore] DocumentClient documentClient, ILogger log) { // Get orders within past 3 days in atg-orders container that are not in the orders container var query = new SqlQuerySpec { QueryText = "SELECT VALUE c FROM c WHERE c.lastModifiedDate > DateTimeAdd(\"dd\", -03, GetCurrentDateTime())" }; var ordersCollectionUri = UriFactory.CreateDocumentCollectionUri("sourcing-engine", "orders"); var atgOrdersCollectionUri = UriFactory.CreateDocumentCollectionUri("sourcing-engine", "atg-orders"); var atgOrderDocs = documentClient.CreateDocumentQuery <Document>(atgOrdersCollectionUri, query, option).AsEnumerable(); // Run sourcing engine on unsourced orders foreach (var atgOrderDoc in atgOrderDocs) { AtgOrderReq atgOrderReq = (dynamic)atgOrderDoc; query = new SqlQuerySpec { QueryText = "SELECT * FROM c WHERE c.id = @id", Parameters = new SqlParameterCollection() { new SqlParameter("@id", atgOrderReq.atgOrderId) } }; var orderDoc = documentClient.CreateDocumentQuery <Document>(ordersCollectionUri, query, option).AsEnumerable().FirstOrDefault(); var sourcingController = InitializeSourcingController(log); // If the order does not exist in orders container, run sourcing engine try { if (orderDoc == null) { log.LogInformation($"Order ID: {atgOrderReq.atgOrderId}"); log.LogInformation(@"Order: {Order}", atgOrderReq); var atgOrderRes = new AtgOrderRes(atgOrderReq); await sourcingController.StartSourcing(documentClient, atgOrderRes); } else // ensure that each line item has a shipFrom location { AtgOrderRes atgOrderRes = (dynamic)orderDoc; var requiresSourcing = OrderController.ValidateItemShipFroms(atgOrderRes.items); if (requiresSourcing) { await sourcingController.StartSourcing(documentClient, atgOrderRes); } } } catch (Exception ex) { log.LogWarning(@"Missing required field: {E}", ex); var title = "Error in SourceUnsourcedOrders: "; var text = $"Error message: {ex.Message}. Stacktrace: {ex.StackTrace}"; var teamsMessage = new TeamsMessage(title, text, "red", errorLogsUrl); teamsMessage.LogToTeams(teamsMessage); log.LogError(title + @"{E}", ex); } } }
public static async Task ReSourceBackorderedItems( [TimerTrigger("0 */15 * * * *")] TimerInfo timer, // every 15 mins [CosmosDB(ConnectionStringSetting = "AzureCosmosDBConnectionString"), SwaggerIgnore] DocumentClient documentClient, ILogger log) { try { var currentHour = OrderController.GetCurrentEasternHour(); log.LogInformation($"Current Hour: {currentHour}"); // Only run within 6am - 7pm EST if (currentHour < 6 || currentHour >= 19) { return; } // Get open orders with backordered item(s) var query = new SqlQuerySpec { QueryText = @" SELECT VALUE c FROM c JOIN s IN c.sourcing JOIN i IN s.items WHERE c.orderComplete = false AND c.claimed = false AND (i.sourcingMessage = 'Backordered.' OR CONTAINS(i.sourcingMessage, 'does not have the required quantity'))" }; var manualOrdersCollectionUri = UriFactory.CreateDocumentCollectionUri("sourcing-engine", "manual-orders"); var manualOrderDocs = documentClient.CreateDocumentQuery <Document>(manualOrdersCollectionUri, query, option).AsEnumerable(); // Get the matching ATG Order from query results and run sourcing foreach (var manualOrderDoc in manualOrderDocs) { try { ManualOrder manualOrder = (dynamic)manualOrderDoc; log.LogInformation($"Order ID: {manualOrder.atgOrderId}"); var atgOrderDoc = await OrderController.GetOrder <AtgOrderRes>(manualOrder.atgOrderId, documentClient); AtgOrderRes atgOrder = (dynamic)atgOrderDoc; await InitializeSourcingController(log).StartSourcing(documentClient, atgOrder); } catch (Exception ex) { var title = "Error in ReSourceBackorderedItems foreach loop."; var teamsMessage = new TeamsMessage(title, $"Error message: {ex.Message}. Stacktrace: {ex.StackTrace}", "yellow", errorLogsUrl); teamsMessage.LogToTeams(teamsMessage); log.LogError(ex, title); } } } catch (Exception ex) { var title = "Error in ReSourceBackorderedItems."; var teamsMessage = new TeamsMessage(title, $"Error message: {ex.Message}. Stacktrace: {ex.StackTrace}", "red", errorLogsUrl); teamsMessage.LogToTeams(teamsMessage); log.LogError(ex, title); } }
public async Task CreateOrUpdateManualOrder( [CosmosDBTrigger( databaseName: "sourcing-engine", #if RELEASE collectionName: "orders", LeaseCollectionName = "sourcing-leases", #endif #if DEBUG collectionName: "test-orders", LeaseCollectionName = "test-sourcing-leases", #endif ConnectionStringSetting = "AzureCosmosDBConnectionString", CreateLeaseCollectionIfNotExists = true), SwaggerIgnore] IReadOnlyList <Document> documents, [CosmosDB(ConnectionStringSetting = "AzureCosmosDBConnectionString"), SwaggerIgnore] DocumentClient documentClient, ILogger log) { log.LogInformation(@"documents: {Documents}", documents); foreach (var document in documents) { try { AtgOrderRes atgOrderRes = (dynamic)document; log.LogInformation($"Order ID: {atgOrderRes.atgOrderId}"); if (atgOrderRes.processSourcing) { log.LogInformation("Manual order not required because processSourcing is true."); continue; } log.LogInformation(@"atgOrderRes: {AtgOrderRes}", atgOrderRes); var manualOrdersContainerName = Environment.GetEnvironmentVariable("MANUAL_ORDERS_CONTAINER_NAME"); var uri = UriFactory.CreateDocumentCollectionUri("sourcing-engine", manualOrdersContainerName); var manualOrderDoc = await OrderController.GetOrder <ManualOrder>(atgOrderRes.atgOrderId, documentClient); log.LogInformation(@"manualOrderDoc: {ManualOrderDoc}", manualOrderDoc); var orderController = new OrderController(new LocationController()); if (manualOrderDoc == null) { log.LogInformation("Creating new ManualOrder."); var manualOrder = orderController.CreateManualOrder(atgOrderRes); await documentClient.CreateDocumentAsync(uri, manualOrder); } else { log.LogInformation("Updating existing ManualOrder."); var updatedManualOrder = orderController.UpdateManualOrder(atgOrderRes, manualOrderDoc); await documentClient.ReplaceDocumentAsync(manualOrderDoc.SelfLink, updatedManualOrder); } } catch (Exception ex) { var title = "Error in CreateOrUpdateManualOrder"; log.LogError(ex, title); #if RELEASE var teamsMessage = new TeamsMessage(title, $"Error message: {ex.Message}. Stacktrace: {ex.StackTrace}", "red", errorLogsUrl); teamsMessage.LogToTeams(teamsMessage); #endif } } }
public async Task <double> EstimateShippingCost(double weight, ShippingAddress shipTo, ShippingAddress shipFrom, AtgOrderRes atgOrderRes) { _logger.LogInformation("EstimateShippingCost start"); var retryPolicy = Policy.Handle <Exception>().Retry(3, (ex, count) => { var title = "Error in EstimateShippingCost"; _logger.LogWarning($"{title}. Retrying..."); if (count == 3) { var teamsMessage = new TeamsMessage(title, $"Error: {ex.Message}. Stacktrace: {ex.StackTrace}", "yellow", SourcingEngineFunctions.errorLogsUrl); teamsMessage.LogToTeams(teamsMessage); _logger.LogError(ex, title); } }); return(await retryPolicy.Execute(async() => { var requestBody = new ShipQuoteRequest { RateType = "Ground", // Default to Ground unless shipping next day or second day OriginAddress = shipFrom, DestinationAddress = shipTo, Package = new Package() { Weight = weight } }; if (atgOrderRes.shipping.shipViaCode == "SECOND_DAY") { requestBody.RateType = "Second Day Air"; } else if (atgOrderRes.shipping.shipViaCode == "NEXT_DAY") { requestBody.RateType = "Next Day Air"; } var jsonRequest = JsonConvert.SerializeObject(requestBody); var baseUrl = "https://ups-microservices.azurewebsites.net/api/rating"; var client = new RestClient(baseUrl); var request = new RestRequest(Method.POST) .AddHeader("Content-Type", "application/json") .AddQueryParameter("code", Environment.GetEnvironmentVariable("QUOTE_SHIPMENT_KEY")) .AddParameter("application/json; charset=utf-8", jsonRequest, ParameterType.RequestBody); var response = await client.ExecuteAsync(request); _logger.LogInformation(@"Quote shipment response status code: {0}. Content: {1}", response.StatusCode, response.Content); if (response?.StatusCode != HttpStatusCode.OK) { throw new ArgumentException("Est Shipping Cost returned bad request object."); } double.TryParse(response.Content, out double rate); _logger.LogInformation($"Branch {shipFrom.branchNumber} Estimated Shipping Cost: {rate}"); if (rate == 0) { var errorTitle = "Warning in EstimateShippingCost."; var message = "Ship Quote returned $0 estimate."; var teamsMessage = new TeamsMessage(errorTitle, message, "yellow", SourcingEngineFunctions.errorLogsUrl); teamsMessage.LogToTeams(teamsMessage); _logger.LogWarning(message); } _logger.LogInformation("EstimateShippingCost finish"); return rate; })); }
/// <summary> /// Sets the Trilogie fields on the ATG Order, incld. error message, status and order ID. /// </summary> /// <param name="trilogieReq">Request containing the results of submitting the order to Trilogie.</param> /// <param name="atgOrder">ATG Order object containing line items with shipFrom values.</param> public static async Task SetTrilogieFieldsOnATGOrder(TrilogieRequest trilogieReq, AtgOrderRes atgOrder) { atgOrder.trilogieErrorMessage = trilogieReq.TrilogieErrorMessage; atgOrder.trilogieOrderId = trilogieReq.TrilogieOrderId; atgOrder.trilogieStatus = trilogieReq.TrilogieStatus.ToString(); #if DEBUG // Sets to false if order failed in Trilogie atgOrder.processSourcing = trilogieReq.TrilogieStatus == TrilogieStatus.Pass; #endif }
/// <summary> /// Returns the matching line on the ATG order. Useful when working from the AllLines class and needing to get the matching /// ATG order line. /// </summary> /// <param name="lineId">Index of the line.</param> /// <returns>Matching line on the ATG order.</returns> public ItemRes GetOrderItemByLineId(int lineId, AtgOrderRes atgOrderRes) { return(atgOrderRes.items.FirstOrDefault(i => i.lineId == lineId)); }