feature exchange rate update automation

Automated downloading exchange rates from HMRC and updating the database. Added function call to the console and form applications.

Also added a form to show the console output in form application.
This commit is contained in:
Bobbie Hodgetts
2025-06-09 20:56:26 +01:00
committed by GitHub
parent 8e7cd00b74
commit ea0a52b2a0
25 changed files with 1095 additions and 179 deletions

View File

@@ -1,23 +1,200 @@
using System;
using FikaAmazonAPI.ConstructFeed.Messages;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace bnhtrade.Core.Logic.Account
{
public class Currency
{
Log.LogEvent log = new Log.LogEvent();
public decimal CurrencyConvertToGbp(string currencyCode, decimal amount, DateTime conversionDate)
{
return new Data.Database.Account.Currency().CurrencyConvertToGbp(currencyCode, amount, conversionDate);
if (currencyCode == "GBP" || amount == 0M)
{
return amount;
}
if (currencyCode.Length != 3)
{
throw new Exception("Invalid currency code '" + currencyCode + "'");
}
var db = new Data.Database.Account.Currency();
var exchageRate = db.ReadExchangeRate(currencyCode, conversionDate);
if (exchageRate != null)
{
return amount / Convert.ToDecimal(exchageRate);
}
else
{
throw new Exception("Currency code '" + currencyCode + "' or date " + conversionDate.ToShortDateString() + " " + conversionDate.ToLongTimeString() + "' does not exist in the Exchange Rate table");
}
}
public int CurrencyExchangeRateInsert(int exchangeRateSource, string currencyCode,
decimal currencyUnitsPerGbp, DateTime periodStart, DateTime periodEnd, bool checkOverride = false)
private DateTime GetHmrcMaxPeriodAvaible()
{
return new Data.Database.Account.Currency().CurrencyExchangeRateInsert(exchangeRateSource, currencyCode,
currencyUnitsPerGbp, periodStart, periodEnd, checkOverride);
// HMRC monthly sxchange rates are published on the penultimate Thursday of the month before
// For some leeway we'll use the penultimate Friday
// find penultimate Friday for current month
var ukTimeNow = new Logic.Utilities.DateTime().ConvertUtcToUk(DateTime.UtcNow);
var monthDayCount = DateTime.DaysInMonth(ukTimeNow.Year, ukTimeNow.Month);
var thisMonthPenultimateFriday = DateTime.SpecifyKind(new DateTime(ukTimeNow.Year, ukTimeNow.Month, monthDayCount), DateTimeKind.Unspecified);
int count = 0;
int fridayCount = 0;
while (count != 15)
{
if (thisMonthPenultimateFriday.DayOfWeek == DayOfWeek.Friday)
{
fridayCount++;
if (fridayCount == 2)
{
break;
}
}
thisMonthPenultimateFriday = thisMonthPenultimateFriday.AddDays(-1);
count++;
}
if (count == 15)
{
throw new Exception("Something went wrong here ErrorID:ef7f5d8f-0f7b-4014-aa65-421ecd5d7367");
}
var mostRecentPeriodAvaible = DateTime.SpecifyKind(new DateTime(ukTimeNow.Year, ukTimeNow.Month, 1), DateTimeKind.Unspecified); ;
if (ukTimeNow >= thisMonthPenultimateFriday)
{
mostRecentPeriodAvaible = mostRecentPeriodAvaible.AddMonths(1);
}
return mostRecentPeriodAvaible;
}
public void UpdateHmrcExchageRates()
{
log.LogInformation("Starting update database HMRC exchange rates");
int exchangeRateSourceId = 1; // id for hmrc
// retrive most recent data from db
var db = new Data.Database.Account.Currency();
var dbLatestRates = db.ReadExchangeRateLatest();
// sanity check, make sure there are no duplicates
int count = 0;
foreach (var exchageRate in dbLatestRates)
{
count = 0;
var currency = exchageRate.CurrencyCode;
foreach (var subExchageRate in dbLatestRates)
{
if (exchageRate.CurrencyCode == subExchageRate.CurrencyCode)
{
count = 1;
}
}
if (count > 1)
{
throw new FormatException("Datebase returned duplicate information");
}
}
// test for no data (first time running)
var hmrcMonthToRetrive = new DateTime();
if (dbLatestRates.Any() == false)
{
hmrcMonthToRetrive = Data.Database.Constants.GetBusinessStartUk();
}
// set first/earliest month to retrive from hmrc website
foreach (var exchageRate in dbLatestRates)
{
var dbEndDateTime = exchageRate.DateTimeEndUk;
if (hmrcMonthToRetrive == default(DateTime))
{
hmrcMonthToRetrive = dbEndDateTime;
}
else
{
if (dbEndDateTime < hmrcMonthToRetrive)
{
hmrcMonthToRetrive = dbEndDateTime;
}
}
}
// check - more coding required to retrive periods before 2021-01-01
if (hmrcMonthToRetrive < DateTime.SpecifyKind(new DateTime(2021, 1, 1), DateTimeKind.Unspecified))
{
throw new Exception("This function does not currently retirve exchange rates from HMRC for dates before 2021-01-01");
}
// check if retrival from hmrc is required
var hmrcMaxMonthAvaible = GetHmrcMaxPeriodAvaible();
if (hmrcMonthToRetrive.Year == hmrcMaxMonthAvaible.Year && hmrcMonthToRetrive.Month > hmrcMaxMonthAvaible.Month)
{
// nothing to retrive
log.LogInformation("Exchange rates curretly up to date, exiting.");
return;
}
// get info from hmrc and insert data in db
while (hmrcMonthToRetrive <= hmrcMaxMonthAvaible)
{
count = 0;
var url = new string(
"https://www.trade-tariff.service.gov.uk/api/v2/exchange_rates/files/monthly_xml_"
+ hmrcMonthToRetrive.Year.ToString()
+ "-"
+ hmrcMonthToRetrive.Month.ToString()
+ ".xml"
);
var xd = new XDocument();
xd = XDocument.Load(url);
foreach (var exchageRate in dbLatestRates)
{
if (exchageRate.DateTimeStartUtc < hmrcMonthToRetrive)
{
//retrive exchange rate from xml
XElement node = xd.Root.Elements("exchangeRate").Where(e => e.Element("currencyCode").Value == exchageRate.CurrencyCode.ToString()).FirstOrDefault();
decimal rate = decimal.Parse(node.Element("rateNew").Value);
rate = decimal.Round(rate, 4);
// insert into db
new Data.Database.Account.Currency().InsertExchangeRate(
exchangeRateSourceId
, exchageRate.CurrencyCode
, rate
, new Utilities.DateTime().ConvertUkToUtc(hmrcMonthToRetrive)
, new Utilities.DateTime().ConvertUkToUtc(hmrcMonthToRetrive.AddMonths(1))
);
count++;
}
}
log.LogInformation(
count + " new exchange rate(s) added to database for " + hmrcMonthToRetrive.ToString("MMMM") + " " + hmrcMonthToRetrive.Year.ToString()
);
hmrcMonthToRetrive = hmrcMonthToRetrive.AddMonths(1);
}
log.LogInformation("Updating database currency exchange rates complete.");
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Utilities
{
public class DateTime
{
public System.DateTime ConvertUkToUtc(System.DateTime ukDateTime)
{
if (ukDateTime.Kind == DateTimeKind.Local && TimeZoneInfo.Local.Id != "GMT Standard Time")
{
throw new System.ArgumentException("DateTime kind set to local, local is not set to 'GMT Standard Time'");
}
else if (ukDateTime.Kind == DateTimeKind.Utc)
{
throw new System.ArgumentException("DateTime kind is UTC");
}
TimeZoneInfo ukZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
System.DateTime ukTime = TimeZoneInfo.ConvertTimeToUtc(ukDateTime, ukZone);
return ukTime; // is returned as DateTimeKind 'unspecified'
}
public System.DateTime ConvertUtcToUk(System.DateTime utcDateTime)
{
if (utcDateTime.Kind != DateTimeKind.Utc)
{
throw new System.ArgumentException("DateTime kind is not UTC");
}
TimeZoneInfo ukZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
System.DateTime ukTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, ukZone);
return ukTime; // is returned as DateTimeKind 'unspecified'
}
public System.DateTime ParseIsoDateTimeString(string reportDateTime)
{
string isoDateTime =
reportDateTime.Substring(6, 4) + "-" +
reportDateTime.Substring(3, 2) + "-" +
reportDateTime.Substring(0, 2) + "T" +
reportDateTime.Substring(11, 2) + ":" +
reportDateTime.Substring(14, 2) + ":" +
reportDateTime.Substring(17, 2) + "Z";
return System.DateTime.Parse(isoDateTime);
}
}
}

View File

@@ -8,9 +8,9 @@ namespace bnhtrade.Core.Logic.Utilities
{
public class DateTimeCheck : Validate.Validate
{
public bool IsUtc(DateTime dateTimeToCheck)
public bool IsUtc(System.DateTime dateTimeToCheck)
{
if (dateTimeToCheck == default(DateTime))
if (dateTimeToCheck == default(System.DateTime))
{
ValidationResultAdd( "DateTime value set to default.");
return false;
@@ -22,9 +22,9 @@ namespace bnhtrade.Core.Logic.Utilities
}
return true;
}
public bool IsLocal(DateTime dateTimeToCheck)
public bool IsLocal(System.DateTime dateTimeToCheck)
{
if (dateTimeToCheck == default(DateTime))
if (dateTimeToCheck == default(System.DateTime))
{
ValidationResultAdd("DateTime value set to default.");
return false;

View File

@@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Utilities
{
public class DateTimeParse
{
public DateTime ParseMwsReportDateTime(string reportDateTime)
{
string isoDateTime =
reportDateTime.Substring(6, 4) + "-" +
reportDateTime.Substring(3, 2) + "-" +
reportDateTime.Substring(0, 2) + "T" +
reportDateTime.Substring(11, 2) + ":" +
reportDateTime.Substring(14, 2) + ":" +
reportDateTime.Substring(17, 2) + "Z";
return DateTime.Parse(isoDateTime);
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Utilities
{
public class ListFunction
{
public ListFunction() { }
/// <summary>
/// Outputs a unique list from an input list
/// </summary>
/// <param name="inputList"></param>
/// <param name="includeWhiteSpace"></param>
/// <returns>Unique list</returns>
public List<string> UniqueList(List<string> inputList, bool removeNullorWhitespace = true)
{
List<string> outputList = new List<string>();
foreach (string input in inputList)
{
if (string.IsNullOrWhiteSpace(input) && removeNullorWhitespace)
{
break;
}
bool stringExists = false;
foreach (string output in outputList)
{
if (output == input)
{
stringExists = true;
break;
}
}
if (stringExists == false)
{
outputList.Add(input);
}
}
return outputList;
}
public List<string> UniqueList(List<Enum> inputList, bool removeNullorWhitespace = true)
{
List<string> stringList = new List<string>();
foreach (Enum input in inputList)
{
stringList.Add(input.ToString());
}
return UniqueList(stringList, removeNullorWhitespace);
}
}
}

View File

@@ -21,6 +21,7 @@ namespace bnhtrade.Core.Logic.Utilities
var export = new bnhtrade.Core.Logic.Export.AmazonSettlement();
bool stockUpdate = false;
bool exchangeRate = false;
bool accountProcess = false;
while (true)
@@ -28,6 +29,7 @@ namespace bnhtrade.Core.Logic.Utilities
try
{
if (stockUpdate == false) { stockUpdate = true; new bnhtrade.Core.Logic.Import.Amazon().SyncAllWithDatabase(); ; }
if (exchangeRate == false) { exchangeRate = true; new Logic.Account.Currency().UpdateHmrcExchageRates(); }
if (accountProcess == false) { accountProcess = true; export.ToInvoice(); }
// if (stockProcess == false) { stockProcess = true; stock.ProcessFbaStockImportData(); }