public RecurringInvoices(VendorService vendorService, QuickBooksOnlineClient quickBooksClient, decimal taxRate, ILogging logging) { VendorService = vendorService; QuickBooksClient = quickBooksClient; TaxRate = taxRate; Logging = logging; }
public void Run(APIGatewayProxyRequest request, APIGatewayProxyResponse response, FinanceUser user) { var databaseClient = new DatabaseClient <QuickBooksOnlineConnection>(new AmazonDynamoDBClient(), new ConsoleLogger()); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, databaseClient, new ConsoleLogger()); var accountingClient = new AccountingQuickBooksOnlineClient(qboClient); var journal = new PrivateAccountingJournal <JournalEntry>(new AmazonDynamoDBClient(), accountingClient); var journalEntry = JsonConvert.DeserializeObject <JournalEntry>(request.Body); if (string.Equals(journalEntry.Type ?? string.Empty, Accounting.Constants.TYPE_INCOME, StringComparison.OrdinalIgnoreCase)) { journalEntry.TaxCode = PrivateAccounting.Constants.QUICKBOOKS_TAX_RATE; } var validation = new List <string>(); validation.AddRange(GetDateValidation(journalEntry.Date)); if (!string.Equals(journalEntry.Type ?? string.Empty, Accounting.Constants.TYPE_INCOME, StringComparison.Ordinal) && !string.Equals(journalEntry.Type ?? string.Empty, Accounting.Constants.TYPE_EXPENSE, StringComparison.Ordinal)) { validation.Add($"Type is required and must be {Accounting.Constants.TYPE_INCOME} or {Accounting.Constants.TYPE_EXPENSE}"); } if (journalEntry.Amount.GetValueOrDefault() <= 0) { validation.Add("Amount must be present and greater than 0"); } if (validation.Any()) { response.StatusCode = 400; response.Body = new JObject { { "error", JArray.FromObject(validation) } }.ToString(); return; } journal.Save(journalEntry); response.Body = new JObject().ToString(); }
public void Run(APIGatewayProxyRequest request, APIGatewayProxyResponse response, FinanceUser user) { var dbClient = new AmazonDynamoDBClient(); var qboDbClient = new DatabaseClient <QuickBooksOnlineConnection>(dbClient, new ConsoleLogger()); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, qboDbClient, new ConsoleLogger()); var vendorDataClient = new DatabaseClient <Vendor>(dbClient, new ConsoleLogger()); var vendor = vendorDataClient.Get(new Vendor { Id = request.QueryStringParameters["id"] }).Result; var customer = qboClient.Query <Customer>($"select * from customer where Id = '{vendor.QuickBooksOnlineId}'").First(); var json = new CustomerPaymentSettingsModel { Id = vendor.Id, QuickBooksOnlineId = vendor.QuickBooksOnlineId, PaymentFrequency = vendor.PaymentFrequency, RentPrice = vendor.RentPrice, Memo = vendor.Memo, FirstName = customer.GivenName, LastName = customer.FamilyName, DisplayName = customer.DisplayName, Spots = vendor.Spots }; response.Body = JsonConvert.SerializeObject(json); }
private void CreateExpense(QuickBooksOnlineClient client, string txnDate, decimal amount, string account, string privateNote) { const int paymentAccountUndepositedFunds = 24; var expense = new Purchase { TxnDate = txnDate, PaymentType = "Cash", AccountRef = new Reference { Value = paymentAccountUndepositedFunds.ToString() }, Line = new List <PurchaseLine> { new PurchaseLine { DetailType = "AccountBasedExpenseLineDetail", Amount = amount, AccountBasedExpenseLineDetail = new AccountBasedExpenseLineDetail { TaxCodeRef = new Reference { Value = "NON" }, AccountRef = new Reference { Value = account }, BillableStatus = "NotBillable" } } }, PrivateNote = privateNote }; client.Create(expense); }
public static CashBasisIncomeReport RunReport( DateTimeOffset start, DateTimeOffset end, QuickBooksOnlineClient qboClient, List <int> ignoredCustomerIds) { var salesStart = start.AddDays(-365); var salesEnd = end.AddDays(365); string salesReceiptQuery = $"select * from SalesReceipt Where TxnDate >= '{salesStart:yyyy-MM-dd}' and TxnDate <= '{salesEnd:yyyy-MM-dd}'"; var salesReceipts = qboClient.QueryAll <SalesReceipt>(salesReceiptQuery); var paymentQuery = $"select * from Payment Where TxnDate >= '{salesStart:yyyy-MM-dd}' and TxnDate <= '{salesEnd:yyyy-MM-dd}'"; var payments = qboClient.QueryAll <Payment>(paymentQuery); payments = payments .Where(x => x.MetaData.CreateTime >= start && x.MetaData.CreateTime < end.AddDays(1) && !ignoredCustomerIds.Select(y => y.ToString()).Contains(x.CustomerRef.Value) ) .ToList(); salesReceipts = salesReceipts .Where(x => x.MetaData.CreateTime >= start && x.MetaData.CreateTime < end.AddDays(1) && !ignoredCustomerIds.Select(y => y.ToString()).Contains(x.CustomerRef.Value) ) .ToList(); return(new CashBasisIncomeReport { Payments = payments, SalesReceipts = salesReceipts }); }
public void Run(APIGatewayProxyRequest request, APIGatewayProxyResponse response, FinanceUser user) { var databaseClient = new DatabaseClient <QuickBooksOnlineConnection>(new AmazonDynamoDBClient(), new ConsoleLogger()); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, databaseClient, new ConsoleLogger()); var customers = qboClient.QueryAll <Customer>("select * from customer"); response.Body = JsonConvert.SerializeObject(customers); }
public decimal GetTaxRate(QuickBooksOnlineClient client, int taxCodeId) { var taxCode = client.Query <TaxCode>($"select * from TaxCode where Id = '{taxCodeId}'").Single(); var taxRateId = taxCode.SalesTaxRateList.TaxRateDetail.Single().TaxRateRef.Value; var taxRateEntity = client.Query <TaxRate>($"select * from TaxRate where Id = '{taxRateId}'").Single(); var taxRate = taxRateEntity.RateValue.GetValueOrDefault() / 100; return(taxRate); }
public void Run(APIGatewayProxyRequest request, APIGatewayProxyResponse response, FinanceUser user) { var databaseClient = new DatabaseClient <QuickBooksOnlineConnection>(new AmazonDynamoDBClient(), new ConsoleLogger()); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, databaseClient, new ConsoleLogger()); var accountingClient = new AccountingQuickBooksOnlineClient(qboClient); var journal = new PrivateAccountingJournal <JournalEntry>(new AmazonDynamoDBClient(), accountingClient); var results = journal.SendToAccounting(); response.Body = JsonConvert.SerializeObject(results); }
public string FunctionHandler(ILambdaContext context) { var databaseClient = new DatabaseClient <QuickBooksOnlineConnection>(new AmazonDynamoDBClient(), new ConsoleLogger()); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, databaseClient, new ConsoleLogger()); var taxRate = new Tax().GetTaxRate(qboClient, Constants.QUICKBOOKS_RENTAL_TAX_RATE); var recurringInvoices = new RecurringInvoices(new VendorService(new AmazonDynamoDBClient()), qboClient, taxRate, new ConsoleLogger()); recurringInvoices.CreateInvoicesForFrequency(DateTime.UtcNow.Date, RecurringInvoices.Frequency.Weekly); Console.WriteLine("Scheduled function PropertyRentalManagement.CreateWeeklyInvoices completed"); return("Scheduled function PropertyRentalManagement.CreateWeeklyInvoices completed"); }
public void Run(APIGatewayProxyRequest request, APIGatewayProxyResponse response, FinanceUser user) { var receipt = JsonConvert.DeserializeObject <Receipt>(request.Body); var dbClient = new AmazonDynamoDBClient(); var logger = new ConsoleLogger(); var receiptDbClient = new DatabaseClient <ReceiptSaveResult>(dbClient, logger); var spotReservationDbClient = new DatabaseClient <SpotReservation>(dbClient, logger); var vendorDbClient = new DatabaseClient <Vendor>(dbClient, logger); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, new DatabaseClient <QuickBooksOnlineConnection>(dbClient, logger), logger); var allActiveCustomers = qboClient .QueryAll <Customer>("select * from customer") .ToDictionary(x => x.Id); var activeVendors = new ActiveVendorSearch() .GetActiveVendors(allActiveCustomers, vendorDbClient) .ToList(); var spotClient = new DatabaseClient <Spot>(dbClient, logger); var spotReservationCheck = new SpotReservationCheck(spotClient, spotReservationDbClient, activeVendors, allActiveCustomers); var validation = new ReceiptValidation(spotReservationCheck).Validate(receipt).Result; if (validation.Any()) { response.StatusCode = 400; response.Body = new JObject { { "error", JArray.FromObject(validation) } }.ToString(); return; } var taxRate = new Tax().GetTaxRate(qboClient, PropertyRentalManagement.Constants.QUICKBOOKS_RENTAL_TAX_RATE); var cardPayment = new CardPayment(logger, Configuration.CLOVER_MI_PUEBLO_PRIVATE_TOKEN); var receiptService = new ReceiptSave(receiptDbClient, qboClient, taxRate, spotReservationDbClient, logger, cardPayment); string customerId = receipt.Customer.Id; if (string.IsNullOrWhiteSpace(customerId)) { var customer = new Customer { DisplayName = receipt.Customer.Name }; customer = qboClient.Create(customer); customerId = customer.Id.ToString(); } var vendor = activeVendors.FirstOrDefault(x => x.QuickBooksOnlineId.GetValueOrDefault().ToString() == customerId) ?? vendorDbClient.Create(VendorService.CreateModel(int.Parse(customerId), null, null, null)); receipt.Id = string.IsNullOrWhiteSpace(receipt.Id) ? Guid.NewGuid().ToString() : receipt.Id; // Needed until UI is deployed. var receiptResult = receiptService.SaveReceipt(receipt, customerId, user.FirstName, user.LastName, user.Email, vendor, IpLookup.GetIp(request)); response.Body = JsonConvert.SerializeObject(receiptResult); }
public ReceiptSave( DatabaseClient <ReceiptSaveResult> receiptDbClient, QuickBooksOnlineClient quickBooksClient, decimal taxRate, DatabaseClient <SpotReservation> spotReservationDbClient, ILogging logger, CardPayment cardPayment) { ReceiptDbClient = receiptDbClient; QuickBooksClient = quickBooksClient; TaxRate = taxRate; SpotReservationDbClient = spotReservationDbClient; Logger = logger; CardPayment = cardPayment; }
public void Run(APIGatewayProxyRequest request, APIGatewayProxyResponse response, FinanceUser user) { var databaseClient = new DatabaseClient <QuickBooksOnlineConnection>(new AmazonDynamoDBClient(), new ConsoleLogger()); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, databaseClient, new ConsoleLogger()); var day = JsonConvert.DeserializeObject <CalendarDay>(request.Body); var date = new DateTime(day.Year, day.Month, day.DayOfMonth, 0, 0, 0, DateTimeKind.Utc); Console.WriteLine($"{user.Email} is creating monthly invoices for {date:yyyy-MM-dd}"); var taxRate = new Tax().GetTaxRate(qboClient, PropertyRentalManagement.Constants.QUICKBOOKS_RENTAL_TAX_RATE); var recurringInvoices = new RecurringInvoices(new VendorService(new AmazonDynamoDBClient()), qboClient, taxRate, new ConsoleLogger()); var monthlyInvoices = recurringInvoices.CreateInvoicesForFrequency(date, RecurringInvoices.Frequency.Monthly); var invoiceJson = JsonConvert.SerializeObject(monthlyInvoices); response.Body = invoiceJson; }
public void Run(APIGatewayProxyRequest request, APIGatewayProxyResponse response, FinanceUser user) { var dbClient = new AmazonDynamoDBClient(); var qboDbClient = new DatabaseClient <QuickBooksOnlineConnection>(dbClient, new ConsoleLogger()); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, qboDbClient, new ConsoleLogger()); var vendorDataClient = new DatabaseClient <Vendor>(dbClient, new ConsoleLogger()); var vendor = vendorDataClient.Get(new Vendor { Id = request.QueryStringParameters["id"] }).Result; var start = request.QueryStringParameters["start"]; var end = request.QueryStringParameters["end"]; var invoices = qboClient.QueryAll <Invoice>($"select * from Invoice where CustomerRef = '{vendor.QuickBooksOnlineId}' and TxnDate >= '{start}' and TxnDate <= '{end}' ORDERBY TxnDate DESC"); invoices.AddRange(qboClient.QueryAll <Invoice>($"select * from Invoice where CustomerRef = '{vendor.QuickBooksOnlineId}' and TxnDate < '{start}' and Balance > '0' ORDERBY TxnDate DESC")); response.Body = JsonConvert.SerializeObject(invoices); }
public void Run(APIGatewayProxyRequest request, APIGatewayProxyResponse response, FinanceUser user) { var start = request.QueryStringParameters["start"]; var end = request.QueryStringParameters["end"]; var startDate = DateTime.ParseExact(start, "yyyy-MM-dd", CultureInfo.InvariantCulture); var endDate = DateTime.ParseExact(end, "yyyy-MM-dd", CultureInfo.InvariantCulture); var easternStart = Configuration.LakeLandMiPuebloTimeZone.AtLeniently(LocalDateTime.FromDateTime(startDate)); var easternEnd = Configuration.LakeLandMiPuebloTimeZone.AtLeniently(LocalDateTime.FromDateTime(endDate)); var dbClient = new AmazonDynamoDBClient(); var qboDbClient = new DatabaseClient <QuickBooksOnlineConnection>(dbClient, new ConsoleLogger()); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, qboDbClient, new ConsoleLogger()); var report = PropertyRentalManagement.Reports.IncomeReport.RunReport( easternStart.ToDateTimeOffset(), easternEnd.ToDateTimeOffset(), qboClient, PropertyRentalManagement.Constants.NonRentalCustomerIds); var income = new List <Income>(); foreach (var item in report.Payments) { income.Add(new Income { AccountingId = item.Id.ToString(), RecordType = "payment", Customer = item.CustomerRef.Name, Amount = item.TotalAmount.ToString(), Date = item.MetaData.CreateTime.ToString("O") }); } foreach (var item in report.SalesReceipts) { income.Add(new Income { AccountingId = item.Id.ToString(), RecordType = "salesReceipt", Customer = item.CustomerRef.Name, Amount = item.TotalAmount.ToString(), Date = item.MetaData.CreateTime.ToString("O") }); } income = income.OrderBy(x => x.Date).ToList(); response.Body = JsonConvert.SerializeObject(income); }
public void Run(APIGatewayProxyRequest request, APIGatewayProxyResponse response, FinanceUser user) { List <string> errors = new List <string>(); var dbClient = new AmazonDynamoDBClient(); var logger = new ConsoleLogger(); var spotClient = new DatabaseClient <Spot>(dbClient, logger); var vendorDataClient = new DatabaseClient <Vendor>(dbClient, logger); var databaseClient = new DatabaseClient <QuickBooksOnlineConnection>(dbClient, logger); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, databaseClient, logger); var vendorUpdates = JsonConvert.DeserializeObject <Vendor>(request.Body); if (vendorUpdates.Memo != null && vendorUpdates.Memo.Length > 4000) { errors.Add("Memo can't exceed 4,000 characters"); } var spotReservationDbClient = new DatabaseClient <SpotReservation>(dbClient, logger); var allActiveCustomers = qboClient .QueryAll <Customer>("select * from customer") .ToDictionary(x => x.Id); var allActiveVendors = new ActiveVendorSearch() .GetActiveVendors(allActiveCustomers, vendorDataClient) .ToList(); var spotReservationCheck = new SpotReservationCheck(spotClient, spotReservationDbClient, allActiveVendors, allActiveCustomers); ZonedClock easternClock = SystemClock.Instance.InZone(Configuration.LakeLandMiPuebloTimeZone); LocalDate easternToday = easternClock.GetCurrentDate(); while (easternToday.DayOfWeek != IsoDayOfWeek.Sunday) { easternToday = easternToday.PlusDays(1); } string easternRentalDate = easternToday.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); errors.AddRange(spotReservationCheck.GetSpotConflicts(vendorUpdates.Spots, easternRentalDate, vendorUpdates.Id).Result); if (errors.Any()) { response.StatusCode = 400; response.Body = new JObject { { "error", JArray.FromObject(errors) } }.ToString(); return; } var updated = vendorDataClient.Update(vendorUpdates); response.Body = JsonConvert.SerializeObject(updated); }
private void CreateSalesReceipt( QuickBooksOnlineClient client, int?customerId, string taxCode, int product, DateTime date, decimal amount, string memo) { var salesReceipt = new SalesReceipt { CustomerRef = new PropertyRentalManagement.QuickBooksOnline.Models.Reference { Value = customerId.ToString() }, TxnDate = date.ToString("yyyy-MM-dd"), TotalAmount = amount, PrivateNote = memo, Line = new List <SalesLine> { new SalesLine { Amount = amount, DetailType = "SalesItemLineDetail", SalesItemLineDetail = new SalesItemLineDetail { ItemRef = new PropertyRentalManagement.QuickBooksOnline.Models.Reference { Value = product.ToString() }, Quantity = 1, UnitPrice = amount, TaxCodeRef = new PropertyRentalManagement.QuickBooksOnline.Models.Reference { Value = Constants.QUICKBOOKS_INVOICE_LINE_TAXABLE } } } }, TxnTaxDetail = new TxnTaxDetail { TxnTaxCodeRef = new PropertyRentalManagement.QuickBooksOnline.Models.Reference { Value = taxCode } } }; client.Create(salesReceipt); }
public void Run(APIGatewayProxyRequest request, APIGatewayProxyResponse response, FinanceUser user) { var dbClient = new AmazonDynamoDBClient(); var qboDbClient = new DatabaseClient <QuickBooksOnlineConnection>(dbClient, new ConsoleLogger()); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, qboDbClient, new ConsoleLogger()); var vendorClient = new DatabaseClient <PropertyRentalManagement.DatabaseModel.Vendor>(dbClient, new ConsoleLogger()); var nonRentalCustomerIds = PropertyRentalManagement.Constants.NonRentalCustomerIds; var allActiveCustomers = qboClient.QueryAll <Customer>("select * from customer") .Where(x => !nonRentalCustomerIds.Contains(x.Id.GetValueOrDefault())) .ToDictionary(x => x.Id); var activeVendors = new ActiveVendorSearch().GetActiveVendors(allActiveCustomers, vendorClient) .ToDictionary(x => x.QuickBooksOnlineId); var newCustomers = allActiveCustomers.Where(x => !activeVendors.ContainsKey(x.Key)); foreach (var newCustomer in newCustomers) { var vendor = VendorService.CreateModel(newCustomer.Key, null, null, null); vendorClient.Create(vendor); activeVendors.Add(vendor.QuickBooksOnlineId, vendor); } var json = new List <CustomerPaymentSettingsModel>(); foreach (var vendor in activeVendors.Values) { var customer = allActiveCustomers[vendor.QuickBooksOnlineId]; json.Add(new CustomerPaymentSettingsModel { Id = vendor.Id, QuickBooksOnlineId = vendor.QuickBooksOnlineId, PaymentFrequency = vendor.PaymentFrequency, RentPrice = vendor.RentPrice, Memo = vendor.Memo, FirstName = customer.GivenName, LastName = customer.FamilyName, DisplayName = customer.DisplayName, Balance = customer.Balance, Spots = vendor.Spots }); } response.Body = JsonConvert.SerializeObject(json); }
public void SendToAccounting(DateTime date, ILogging logger) { var databaseClient = new DatabaseClient <QuickBooksOnlineConnection>(new AmazonDynamoDBClient(), logger); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, databaseClient, logger); var originalMonth = date.Month; var client = new CloverClient( Environment.GetEnvironmentVariable("CLOVER_MI_PUEBLO_CHICKEN_ACCESS_TOKEN"), Environment.GetEnvironmentVariable("CLOVER_MI_PUEBLO_CHICKEN_MERCHANT_ID"), logger ); var restaurantCustomer = qboClient .QueryAll <Customer>($"select * from Customer where Id = '{PrivateAccounting.Constants.LakelandMiPuebloAccountRestaurant}'") .First(); var cashTenderType = client.QueryAll <Tender>($"tenders") .Single(x => string.Equals(x.LabelKey, "com.clover.tender.cash", StringComparison.OrdinalIgnoreCase)); do { var salesReceipts = qboClient.QueryAll <SalesReceipt>( $"select * from SalesReceipt where CustomerRef = '{PrivateAccounting.Constants.LakelandMiPuebloAccountRestaurant}' and TxnDate = '{date:yyyy-MM-dd}'"); if (salesReceipts.Any()) { logger.Log($"Skipping {date:yyyy-MM-dd}, because a sales receipt already exists."); } else { var today = new DateTimeOffset(date).ToUnixTimeMilliseconds(); var tomorrow = new DateTimeOffset(date.AddDays(1)).ToUnixTimeMilliseconds(); var result = client.QueryAll <Payment>($"payments?filter=createdTime>={today}&filter=createdTime<{tomorrow}"); var cashPayments = result.Where(x => x.Tender.Id == cashTenderType.Id).ToList(); var cardPayments = result.Where(x => x.Tender.Id != cashTenderType.Id).ToList(); var cashTotal = GetTotal(cashPayments); var cardTotal = GetTotal(cardPayments); if (cashTotal > 0) { CreateSalesReceipt( qboClient, restaurantCustomer.Id, restaurantCustomer.DefaultTaxCodeRef.Value, PrivateAccounting.Constants.LakelandMiPuebloProductRestaurant, date, GetTotal(cashPayments), "Restaurant sales using cash in Clover Register"); } if (cardTotal > 0) { CreateSalesReceipt( qboClient, restaurantCustomer.Id, restaurantCustomer.DefaultTaxCodeRef.Value, PrivateAccounting.Constants.LakelandMiPuebloProductRestaurant, date, cardTotal, "Restaurant sales using credit card in Clover Register"); } } date = date.AddDays(1); } while (date.Month == originalMonth); }
public PaymentApplicator(QuickBooksOnlineClient client) { Client = client; }
public void Run(APIGatewayProxyRequest request, APIGatewayProxyResponse response, FinanceUser user) { var dbClientCore = new AmazonDynamoDBClient(); var logger = new ConsoleLogger(); var spotClient = new DatabaseClient <Spot>(dbClientCore, logger); var spots = spotClient.ScanAll(new ScanRequest(new Spot().GetTable())) .OrderBy(x => x.Section?.Name) .ThenBy(x => x.Name) .ToList(); var qboClient = new QuickBooksOnlineClient(PrivateAccounting.Constants.LakelandMiPuebloRealmId, new DatabaseClient <QuickBooksOnlineConnection>(dbClientCore, logger), logger); var allActiveCustomers = qboClient .QueryAll <Customer>("select * from customer") .ToDictionary(x => x.Id); var allActiveVendors = new ActiveVendorSearch() .GetActiveVendors(allActiveCustomers, new DatabaseClient <Vendor>(dbClientCore, logger)) .ToList(); var spotReservationCheck = new SpotReservationCheck( spotClient, new DatabaseClient <SpotReservation>(dbClientCore, logger), allActiveVendors, allActiveCustomers ); var rentalDate = request.QueryStringParameters.ContainsKey("rentalDate") ? request.QueryStringParameters["rentalDate"] : string.Empty; var validation = ReceiptValidation.GetRentalDateValidation(rentalDate); if (validation.Any()) { response.StatusCode = 400; response.Body = new JObject { { "error", JArray.FromObject(validation) } }.ToString(); return; } validation = new List <string>(); DateTime.TryParseExact(rentalDate, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsedRentalDate); if (parsedRentalDate - DateTime.UtcNow > TimeSpan.FromDays(91)) // Endpoint is cached. The date constraint prevents denial of service causing the cache to be hit and database hits to plateau quickly. { validation.Add("rentalDate can't be greater than 90 days in the future"); } if (parsedRentalDate - DateTime.UtcNow < TimeSpan.FromDays(-1)) { validation.Add("rentalDate can't be in the past"); } if (validation.Any()) { response.StatusCode = 400; response.Body = new JObject { { "error", JArray.FromObject(validation) } }.ToString(); return; } var jsonResponse = new List <PublicSpot>(); foreach (var spot in spots) { var reservation = spotReservationCheck.GetReservation(spot.Id, rentalDate); int?reservedByQuickBooksOnlineId = 0; if (reservation?.Item1 != null) { reservedByQuickBooksOnlineId = reservation.Item1.QuickBooksOnlineId; } if (reservation?.Item2 != null) { reservedByQuickBooksOnlineId = reservation.Item2.QuickBooksOnlineId; } var publicSpot = new PublicSpot { Id = spot.Id, Name = spot.Name, Section = spot.Section, Right = spot.Right, Bottom = spot.Bottom }; if (reservedByQuickBooksOnlineId.HasValue && allActiveCustomers.TryGetValue(reservedByQuickBooksOnlineId, out var customer)) { publicSpot.ReservedBy = customer.DisplayName; } jsonResponse.Add(publicSpot); } response.Body = JsonConvert.SerializeObject(jsonResponse); }