mirror of
https://github.com/stokebob/bnhtrade.git
synced 2026-03-19 06:27:15 +00:00
Added invoice export function and started implementation of unitofwork pattern (#43)
* complete read invoices from db * wip * wip * wip * wip * wip * wip * wip * wip * updated nuget package spapi * WIP * wip, now test * wip, jut need to fix tax inclusive line amounts not supported * wip * wip, before I f everything up * no, it complies now, this is the one before I f everything up * wip * wip * wip, logic ready for testing * wip it builds!!!! * wip tested, working, need to complete the gui section * wip * wip * wip - created export invoice data delete, time for testing * wip testing phase * wip - delete function fully tested and working * wip on to sorting out the issue with settlement invoices not tallying * wip * wip * wip * wip * wip before I complete change the ReadInvoiceLineItem sections * that appears to have worked, on with the main quest * no it's doesn't work, saving before i remove the confusing cache system (just use a dictionary!!) * wipping picadilli * wip * wip * implemented uow on inovice export, now for testing * wip * wip all tested do invoice currency convertion fearure * wip * pretty much done so long as xero accepts the exported invoices * Complete!
This commit is contained in:
@@ -163,7 +163,7 @@ namespace bnhtrade.Core.Data.Amazon.Feeds
|
||||
GetFeedDetails(feedID);
|
||||
}
|
||||
|
||||
public void SubmitFeedPRICING(double PRICE, string SKU)
|
||||
public void SubmitFeedPRICING(decimal price, string SKU)
|
||||
{
|
||||
|
||||
ConstructFeedService createDocument = new ConstructFeedService(amazonConnection.GetCurrentSellerID, "1.02");
|
||||
@@ -175,7 +175,7 @@ namespace bnhtrade.Core.Data.Amazon.Feeds
|
||||
StandardPrice = new StandardPrice()
|
||||
{
|
||||
currency = amazonConnection.GetCurrentMarketplace.CurrencyCode.ToString(),
|
||||
Value = (PRICE).ToString("0.00")
|
||||
Value = price
|
||||
}
|
||||
});
|
||||
createDocument.AddPriceMessage(list);
|
||||
@@ -201,14 +201,14 @@ namespace bnhtrade.Core.Data.Amazon.Feeds
|
||||
StandardPrice = new StandardPrice
|
||||
{
|
||||
currency = currencyCode,
|
||||
Value = price.ToString("0.00")
|
||||
Value = price
|
||||
},
|
||||
Sale = new Sale
|
||||
{
|
||||
SalePrice = new StandardPrice
|
||||
{
|
||||
currency = currencyCode,
|
||||
Value = salePrice.ToString("0.00")
|
||||
Value = salePrice
|
||||
},
|
||||
StartDate = startDate.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.fffK"),
|
||||
EndDate = endDate.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.fffK")
|
||||
@@ -224,7 +224,7 @@ namespace bnhtrade.Core.Data.Amazon.Feeds
|
||||
}
|
||||
|
||||
|
||||
public void SubmitFeedSale(double PRICE, string SKU)
|
||||
public void SubmitFeedSale(decimal price, string SKU)
|
||||
{
|
||||
|
||||
ConstructFeedService createDocument = new ConstructFeedService("A3J37AJU4O9RHK", "1.02");
|
||||
@@ -236,7 +236,7 @@ namespace bnhtrade.Core.Data.Amazon.Feeds
|
||||
StandardPrice = new StandardPrice()
|
||||
{
|
||||
currency = amazonConnection.GetCurrentMarketplace.CurrencyCode.ToString(),
|
||||
Value = (PRICE).ToString("0.00")
|
||||
Value = price
|
||||
},
|
||||
Sale = new Sale()
|
||||
{
|
||||
@@ -245,7 +245,7 @@ namespace bnhtrade.Core.Data.Amazon.Feeds
|
||||
SalePrice = new StandardPrice()
|
||||
{
|
||||
currency = amazonConnection.GetCurrentMarketplace.CurrencyCode.ToString(),
|
||||
Value = (PRICE - 10).ToString("0.00")
|
||||
Value = price
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace bnhtrade.Core.Data.Amazon.Report
|
||||
reportIdList.Add(report.ReportId);
|
||||
}
|
||||
|
||||
UI.Console.WriteLine("{0} Settlement reports avaible on Amazon SP-API");
|
||||
UI.Console.WriteLine(reportIdList.Count().ToString() + " Settlement reports avaible on Amazon SP-API");
|
||||
return reportIdList;
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace bnhtrade.Core.Data.Database.Account
|
||||
// currency conversion
|
||||
if (currencyCode != "GBP")
|
||||
{
|
||||
amount = new Logic.Account.Currency().CurrencyConvertToGbp(currencyCode, amount, entryDate);
|
||||
amount = new Logic.Account.CurrencyService().CurrencyConvertToGbp(currencyCode, amount, entryDate);
|
||||
}
|
||||
|
||||
// ensure decimal is rounded
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections;
|
||||
using FikaAmazonAPI.AmazonSpApiSDK.Models.FulfillmentOutbound;
|
||||
using System.Data;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Account
|
||||
{
|
||||
internal class Currency : Connection
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns excahnge rate, in decimal format, for a given currency and datetime
|
||||
/// </summary>
|
||||
/// <param name="currencyCode">currency code</param>
|
||||
/// <param name="date">dat and time</param>
|
||||
/// <returns></returns>
|
||||
public decimal? ReadExchangeRate(string currencyCode, DateTime date)
|
||||
{
|
||||
using (SqlConnection sqlConn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
sqlConn.Open();
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(@"
|
||||
SELECT CurrencyUnitsPerGBP
|
||||
FROM tblAccountExchangeRate
|
||||
WHERE CurrencyCode=@currencyCode AND StartDate<=@conversionDate AND EndDate>@conversionDate
|
||||
", sqlConn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@currencyCode", currencyCode);
|
||||
cmd.Parameters.AddWithValue("@conversionDate", date);
|
||||
|
||||
object result = cmd.ExecuteScalar();
|
||||
if (result != null)
|
||||
{
|
||||
return Convert.ToDecimal(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Model.Account.CurrencyExchangeRate> ReadExchangeRate(List<Model.Account.CurrencyCode> currencyCodeList = null, DateTime date = default(DateTime))
|
||||
{
|
||||
throw new NotImplementedException("Complete, but untested");
|
||||
|
||||
var returnList = new List<Model.Account.CurrencyExchangeRate>();
|
||||
|
||||
string sql = @"
|
||||
SELECT AccountEchangeRateID, ExchangeRateSource, CurrencyCode, CurrencyUnitsPerGBP, StartDate, EndDate
|
||||
FROM tblAccountExchangeRate
|
||||
WHERE 1=1 ";
|
||||
|
||||
if (date != default(DateTime))
|
||||
{
|
||||
sql = sql + " AND (@dateTime >= StartDate AND @dateTime < endDate) ";
|
||||
}
|
||||
|
||||
var sqlWhere = new Data.Database.SqlWhereBuilder();
|
||||
|
||||
// create string list
|
||||
List<string> stringList = new List<string>();
|
||||
if (currencyCodeList != null)
|
||||
{
|
||||
stringList = currencyCodeList.ConvertAll(f => f.ToString());
|
||||
if (stringList.Any())
|
||||
{
|
||||
sqlWhere.In("CurrencyCode", stringList, "AND");
|
||||
}
|
||||
}
|
||||
|
||||
sql = sql + sqlWhere.SqlWhereString;
|
||||
|
||||
using (SqlConnection sqlConn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
sqlConn.Open();
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(sql))
|
||||
{
|
||||
if (date != default(DateTime))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@dateTime", date);
|
||||
}
|
||||
if (stringList.Any())
|
||||
{
|
||||
sqlWhere.AddParametersToSqlCommand(cmd);
|
||||
}
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
int id = reader.GetInt32(0);
|
||||
int exchangeRateSource = reader.GetInt32(1);
|
||||
string currencyCodeString = reader.GetString(2);
|
||||
decimal currencyUnitsPerGBP = reader.GetDecimal(3);
|
||||
DateTime startDate = DateTime.SpecifyKind( reader.GetDateTime(4), DateTimeKind.Utc);
|
||||
DateTime endDate = DateTime.SpecifyKind(reader.GetDateTime(5), DateTimeKind.Utc);
|
||||
|
||||
|
||||
// convert string to enum
|
||||
if (Enum.TryParse(currencyCodeString, out Model.Account.CurrencyCode currencyCode) == false)
|
||||
{
|
||||
throw new Exception("Failed converting database string to enum");
|
||||
}
|
||||
|
||||
var item = new Model.Account.CurrencyExchangeRate(
|
||||
currencyCode
|
||||
, exchangeRateSource
|
||||
, startDate
|
||||
, endDate
|
||||
);
|
||||
|
||||
returnList.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
||||
public List<Model.Account.CurrencyExchangeRate> ReadExchangeRateLatest(List<Model.Account.CurrencyCode> currencyCodeList = null)
|
||||
{
|
||||
var returnList = new List<Model.Account.CurrencyExchangeRate>();
|
||||
|
||||
string sql = @"
|
||||
SELECT t1.AccountExchangeRateID, t1.ExchangeRateSource, t1.CurrencyCode, t1.CurrencyUnitsPerGBP, t1.StartDate, t1.EndDate,
|
||||
t2.maxdate
|
||||
FROM tblAccountExchangeRate AS t1
|
||||
INNER JOIN
|
||||
(SELECT max(StartDate) AS maxdate,
|
||||
CurrencyCode
|
||||
FROM tblAccountExchangeRate
|
||||
GROUP BY CurrencyCode
|
||||
";
|
||||
|
||||
// add any filters
|
||||
var sqlWhere = new Data.Database.SqlWhereBuilder();
|
||||
var codeStringList = new List<string>();
|
||||
if (currencyCodeList != null && currencyCodeList.Any() == true)
|
||||
{
|
||||
// convert to string list
|
||||
foreach ( var currencyCode in currencyCodeList)
|
||||
{
|
||||
codeStringList.Add(currencyCode.ToString());
|
||||
}
|
||||
|
||||
// add to where statement
|
||||
sqlWhere.In("CurrencyCode", codeStringList, "HAVING");
|
||||
sql = sql + sqlWhere.SqlWhereString;
|
||||
}
|
||||
|
||||
sql = sql + @" ) AS t2
|
||||
ON t1.CurrencyCode = t2.CurrencyCode
|
||||
AND t1.StartDate = t2.maxdate
|
||||
ORDER BY t1.CurrencyCode;";
|
||||
|
||||
//query db
|
||||
using (SqlConnection sqlConn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
sqlConn.Open();
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(sql, sqlConn))
|
||||
{
|
||||
if (codeStringList.Any())
|
||||
{
|
||||
sqlWhere.AddParametersToSqlCommand(cmd);
|
||||
}
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
int id = reader.GetInt32(0);
|
||||
int exchangeRateSource = reader.GetInt32(1);
|
||||
string currencyCodeString = reader.GetString(2);
|
||||
decimal currencyUnitsPerGBP = reader.GetDecimal(3);
|
||||
DateTime startDate = DateTime.SpecifyKind(reader.GetDateTime(4), DateTimeKind.Utc);
|
||||
DateTime endDate = DateTime.SpecifyKind(reader.GetDateTime(5), DateTimeKind.Utc);
|
||||
|
||||
|
||||
// convert string to enum
|
||||
if (Enum.TryParse(currencyCodeString, out Model.Account.CurrencyCode currencyCode) == false)
|
||||
{
|
||||
throw new Exception("Failed converting database string to enum");
|
||||
}
|
||||
|
||||
var item = new Model.Account.CurrencyExchangeRate(
|
||||
currencyCode
|
||||
, exchangeRateSource
|
||||
, startDate
|
||||
, endDate
|
||||
);
|
||||
|
||||
returnList.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
||||
public int InsertExchangeRate(int exchangeRateSource, Model.Account.CurrencyCode currencyCode,
|
||||
decimal currencyUnitsPerGbp, DateTime periodStartUtc, DateTime periodEnd, bool checkOverride = false)
|
||||
{
|
||||
// checks
|
||||
if (periodStartUtc.Kind != DateTimeKind.Utc || periodEnd.Kind != DateTimeKind.Utc)
|
||||
{
|
||||
throw new FormatException("Currency date time kind must be UTC");
|
||||
}
|
||||
|
||||
currencyUnitsPerGbp = decimal.Round(currencyUnitsPerGbp, 4);
|
||||
|
||||
// CHECKS
|
||||
if (periodEnd <= periodStartUtc)
|
||||
{
|
||||
throw new Exception("Invalid date period.");
|
||||
}
|
||||
|
||||
if (checkOverride == false && (periodEnd - periodStartUtc).Days > 31)
|
||||
{
|
||||
throw new Exception("Date period is greater than 31 days.");
|
||||
}
|
||||
|
||||
// make the insert
|
||||
DateTime? periodEndLast = null;
|
||||
using (SqlConnection sqlConn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
sqlConn.Open();
|
||||
|
||||
int recordId = 0;
|
||||
using (SqlCommand cmd = new SqlCommand(@"
|
||||
INSERT INTO tblAccountExchangeRate (ExchangeRateSource, CurrencyCode, CurrencyUnitsPerGBP, StartDate, EndDate)
|
||||
OUTPUT INSERTED.AccountExchangeRateID
|
||||
VALUES (@exchangeRateSource, @currencyCode, @currencyUnitsPerGbp, @periodStart, @periodEnd);
|
||||
", sqlConn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@exchangeRateSource", exchangeRateSource);
|
||||
cmd.Parameters.AddWithValue("@currencyCode", currencyCode.ToString());
|
||||
cmd.Parameters.AddWithValue("@currencyUnitsPerGbp", currencyUnitsPerGbp);
|
||||
cmd.Parameters.AddWithValue("@periodStart", periodStartUtc);
|
||||
cmd.Parameters.AddWithValue("@periodEnd", periodEnd);
|
||||
|
||||
recordId = (int)cmd.ExecuteScalar();
|
||||
|
||||
if (recordId < 1)
|
||||
{
|
||||
throw new Exception("Error inserting record, did not retrive new record ID.");
|
||||
}
|
||||
}
|
||||
|
||||
return recordId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -8,63 +8,61 @@ using System.Transactions;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Account
|
||||
{
|
||||
public class ReadInvoiceLineItem : Connection
|
||||
internal class ReadInvoiceLineItem : Connection
|
||||
{
|
||||
public ReadInvoiceLineItem()
|
||||
{
|
||||
Innit();
|
||||
}
|
||||
|
||||
public Dictionary<string, int> AccountCodeList { get; private set; }
|
||||
|
||||
public Dictionary<string, string> TaxCodeList { get; private set; }
|
||||
|
||||
private void Innit()
|
||||
/// <summary>
|
||||
/// Reads all invoice line items from the database.
|
||||
/// </summary>
|
||||
/// <returns>dictionary where key=id, value=object</returns>
|
||||
internal Dictionary<int, Model.Account.InvoiceLineItem> All()
|
||||
{
|
||||
AccountCodeList = new Dictionary<string, int>();
|
||||
TaxCodeList = new Dictionary<string, string>();
|
||||
return Execute();
|
||||
}
|
||||
|
||||
public Model.Account.InvoiceLineItem ByItemCode(string itemCode)
|
||||
/// <summary>
|
||||
/// Read list of invoice line items by item code.
|
||||
/// </summary>
|
||||
/// <param name="itemCodes">List of item coeds to query db against</param>
|
||||
/// <returns>dictionary where key=id, value=object</returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
internal Dictionary<int, Model.Account.InvoiceLineItem> ByItemCode(List<string> itemCodes)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(itemCode)) { return null; }
|
||||
|
||||
var result = ByItemCode(new List<string>{ itemCode });
|
||||
|
||||
if (!result.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return result[itemCode];
|
||||
if (itemCodes == null || !itemCodes.Any())
|
||||
{
|
||||
throw new ArgumentException("Item codes list cannot be null or empty.");
|
||||
}
|
||||
|
||||
var sqlwhere = new SqlWhereBuilder();
|
||||
sqlwhere.In("ItemCode", itemCodes, "AND");
|
||||
return Execute(sqlwhere);
|
||||
}
|
||||
|
||||
public Dictionary<string, Model.Account.InvoiceLineItem> ByItemCode(List<string> itemCodeList)
|
||||
private Dictionary<int, Model.Account.InvoiceLineItem> Execute(SqlWhereBuilder sqlwhere = null)
|
||||
{
|
||||
Innit();
|
||||
var resultList = new Dictionary<string, Model.Account.InvoiceLineItem>();
|
||||
|
||||
if (itemCodeList == null || !itemCodeList.Any())
|
||||
{ return resultList; }
|
||||
var resultList = new Dictionary<int, Model.Account.InvoiceLineItem>();
|
||||
var accountCodeIdList = new Dictionary<int, uint>(); // key=LineItemID, value=AccountChartOfID
|
||||
var taxCodeIdList = new Dictionary<int, int>(); // key=LineItemID, value=AccountTaxCodeID
|
||||
|
||||
string sql = @"
|
||||
SELECT tblAccountInvoiceLineItem.ItemCode
|
||||
,tblAccountInvoiceLineItem.ItemName
|
||||
,tblAccountInvoiceLineItem.ItemDescription
|
||||
,tblAccountInvoiceLineItem.IsNewReviewRequired
|
||||
,tblAccountInvoiceLineItem.InvoiceLineEntryEnable
|
||||
,tblAccountChartOf.AccountCode
|
||||
,tblAccountTaxCode.TaxCode
|
||||
FROM tblAccountInvoiceLineItem
|
||||
LEFT OUTER JOIN tblAccountTaxCode ON tblAccountInvoiceLineItem.AccountTaxCodeID_Default = tblAccountTaxCode.AccountTaxCodeID
|
||||
LEFT OUTER JOIN tblAccountChartOf ON tblAccountInvoiceLineItem.AccountChartOfID_Default = tblAccountChartOf.AccountChartOfID
|
||||
WHERE ";
|
||||
SELECT [AccountInvoiceLineItemID]
|
||||
,[ItemName]
|
||||
,[ItemCode]
|
||||
,[ItemDescription]
|
||||
,[IsNewReviewRequired]
|
||||
,[InvoiceLineEntryEnable]
|
||||
,[AccountChartOfID_Default]
|
||||
,[AccountTaxCodeID_Default]
|
||||
FROM [e2A].[dbo].[tblAccountInvoiceLineItem]
|
||||
WHERE 1=1 ";
|
||||
|
||||
var whereBuilder = new SqlWhereBuilder();
|
||||
whereBuilder.In("tblAccountInvoiceLineItem.ItemCode", itemCodeList);
|
||||
sql += whereBuilder.SqlWhereString;
|
||||
if (sqlwhere != null)
|
||||
{
|
||||
sql += sqlwhere.SqlWhereString;
|
||||
}
|
||||
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
@@ -72,41 +70,53 @@ namespace bnhtrade.Core.Data.Database.Account
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(sql, conn))
|
||||
{
|
||||
foreach (var param in whereBuilder.ParameterList)
|
||||
if (sqlwhere != null)
|
||||
{
|
||||
cmd.Parameters.AddWithValue(param.Key, param.Value);
|
||||
sqlwhere.AddParametersToSqlCommand(cmd);
|
||||
}
|
||||
|
||||
using (SqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.HasRows)
|
||||
while (reader.Read())
|
||||
{
|
||||
while (reader.Read())
|
||||
var lineItem = new Model.Account.InvoiceLineItem();
|
||||
|
||||
int lineItemId = reader.GetInt32(0);
|
||||
lineItem.Name = reader.GetString(1);
|
||||
lineItem.ItemCode = reader.GetString(2);
|
||||
if (!reader.IsDBNull(3)) { lineItem.Description = reader.GetString(3); }
|
||||
lineItem.IsNewReviewRequired = reader.GetBoolean(4);
|
||||
lineItem.InvoiceLineEntryEnabled = reader.GetBoolean(5);
|
||||
|
||||
if (!reader.IsDBNull(6))
|
||||
{
|
||||
var result = new Model.Account.InvoiceLineItem();
|
||||
|
||||
result.ItemCode = reader.GetString(0);
|
||||
result.Name = reader.GetString(1);
|
||||
if (!reader.IsDBNull(2)) { result.Description = reader.GetString(2); }
|
||||
result.IsNewReviewRequired = reader.GetBoolean(3);
|
||||
result.InvoiceLineEntryEnabled = reader.GetBoolean(4);
|
||||
|
||||
if (!reader.IsDBNull(5))
|
||||
{
|
||||
AccountCodeList.Add(result.ItemCode, reader.GetInt32(5));
|
||||
}
|
||||
if (!reader.IsDBNull(6))
|
||||
{
|
||||
TaxCodeList.Add(result.ItemCode, reader.GetString(6));
|
||||
}
|
||||
|
||||
resultList.Add(result.ItemCode, result);
|
||||
accountCodeIdList.Add(lineItemId, (uint)reader.GetInt32(6));
|
||||
}
|
||||
if (!reader.IsDBNull(7))
|
||||
{
|
||||
taxCodeIdList.Add(lineItemId, reader.GetInt32(7));
|
||||
}
|
||||
}
|
||||
|
||||
return resultList;
|
||||
resultList.Add(lineItemId, lineItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get account codes and add to result list
|
||||
var accountCodeDictionary = new Data.Database.Account.ReadAccountCode().ByAccountId(accountCodeIdList.Values.ToList());
|
||||
foreach (var accountCode in accountCodeIdList)
|
||||
{
|
||||
resultList[accountCode.Key].DefaultAccountCode = accountCodeDictionary[accountCode.Value];
|
||||
}
|
||||
|
||||
// get tax codes
|
||||
var taxCodeDictionary = new Data.Database.Account.ReadTaxCode().GetByTaxCodeId(taxCodeIdList.Values.ToList());
|
||||
foreach (var taxCode in taxCodeIdList)
|
||||
{
|
||||
resultList[taxCode.Key].DefaultTaxCode = taxCodeDictionary[taxCode.Value];
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -8,11 +8,11 @@ using static System.ComponentModel.Design.ObjectSelectorEditor;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Account
|
||||
{
|
||||
public class ReadPurchaseInvoice : Connection
|
||||
internal class ReadPurchaseInvoice : Connection
|
||||
{
|
||||
private bnhtrade.Core.Data.Database.SqlWhereBuilder sqlBuilder;
|
||||
|
||||
public List<int> PurchaseInvoiceIdList { get; set; }
|
||||
public IEnumerable<int> PurchaseInvoiceIdList { get; set; }
|
||||
|
||||
public ReadPurchaseInvoice()
|
||||
{
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Account
|
||||
{
|
||||
public class ReadTaxCode : Connection
|
||||
internal class ReadTaxCode : Connection
|
||||
{
|
||||
private Data.Database.SqlWhereBuilder whereBuilder = new SqlWhereBuilder();
|
||||
|
||||
@@ -15,14 +15,15 @@ namespace bnhtrade.Core.Data.Database.Account
|
||||
{
|
||||
}
|
||||
|
||||
private List<Model.Account.TaxCodeInfo> Execute(string sqlWhere, Dictionary<string, object> parameters)
|
||||
private Dictionary<int, Model.Account.TaxCodeInfo> Execute(string sqlWhere, Dictionary<string, object> parameters)
|
||||
{
|
||||
var resultList = new List<Model.Account.TaxCodeInfo>();
|
||||
var resultList = new Dictionary<int, Model.Account.TaxCodeInfo>();
|
||||
|
||||
//build sql query
|
||||
string sqlString = @"
|
||||
SELECT
|
||||
TaxCode
|
||||
AccountTaxCodeID
|
||||
,TaxCode
|
||||
,TaxCodeName
|
||||
,TaxCodeDescription
|
||||
,TaxRatePercent
|
||||
@@ -52,19 +53,20 @@ namespace bnhtrade.Core.Data.Database.Account
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
string taxCodeId = reader.GetString(0);
|
||||
string name = reader.GetString(1);
|
||||
int taxCodeId = reader.GetByte(0);
|
||||
string taxCode = reader.GetString(1);
|
||||
string name = reader.GetString(2);
|
||||
string description = null;
|
||||
if (!reader.IsDBNull(2)) { description = reader.GetString(2); }
|
||||
decimal rate = reader.GetDecimal(3);
|
||||
bool isMargin = reader.GetBoolean(4);
|
||||
bool isValidOnExpense = reader.GetBoolean(5);
|
||||
bool isValidOnIncome = reader.GetBoolean(6);
|
||||
bool isActive = reader.GetBoolean(7);
|
||||
string taxType = reader.GetString(8);
|
||||
if (!reader.IsDBNull(3)) { description = reader.GetString(3); }
|
||||
decimal rate = reader.GetDecimal(4);
|
||||
bool isMargin = reader.GetBoolean(5);
|
||||
bool isValidOnExpense = reader.GetBoolean(6);
|
||||
bool isValidOnIncome = reader.GetBoolean(7);
|
||||
bool isActive = reader.GetBoolean(8);
|
||||
string taxType = reader.GetString(9);
|
||||
|
||||
var result = new Model.Account.TaxCodeInfo(
|
||||
taxCodeId,
|
||||
taxCode,
|
||||
name,
|
||||
description,
|
||||
rate,
|
||||
@@ -74,7 +76,7 @@ namespace bnhtrade.Core.Data.Database.Account
|
||||
taxType,
|
||||
isActive);
|
||||
|
||||
resultList.Add(result);
|
||||
resultList.Add(taxCodeId, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,12 +99,13 @@ namespace bnhtrade.Core.Data.Database.Account
|
||||
whereBuilder.Init();
|
||||
whereBuilder.In("TaxCode", taxcodeList, "WHERE");
|
||||
|
||||
return Execute(whereBuilder.SqlWhereString, whereBuilder.ParameterList);
|
||||
var dic = Execute(whereBuilder.SqlWhereString, whereBuilder.ParameterList);
|
||||
return dic.Values.ToList();
|
||||
}
|
||||
|
||||
public List<Model.Account.TaxCodeInfo> GetByTaxCodeId(List<int> taxcodeIdList)
|
||||
public Dictionary<int, Model.Account.TaxCodeInfo> GetByTaxCodeId(List<int> taxcodeIdList)
|
||||
{
|
||||
var resultList = new List<Model.Account.TaxCodeInfo>();
|
||||
var resultList = new Dictionary<int, Model.Account.TaxCodeInfo>();
|
||||
|
||||
if (taxcodeIdList == null || !taxcodeIdList.Any())
|
||||
{
|
||||
@@ -117,7 +120,12 @@ namespace bnhtrade.Core.Data.Database.Account
|
||||
return Execute(whereBuilder.SqlWhereString, whereBuilder.ParameterList);
|
||||
}
|
||||
|
||||
public List<Model.Account.TaxCodeInfo> GetAllActive()
|
||||
|
||||
/// <summary>
|
||||
/// Gets all active Tax Code objects
|
||||
/// </summary>
|
||||
/// <returns>Dictionary where database record ID is the key</returns>
|
||||
public Dictionary<int, Model.Account.TaxCodeInfo> GetAllActive()
|
||||
{
|
||||
string sqlWhere = @"
|
||||
WHERE IsActive=@isActive;";
|
||||
|
||||
@@ -11,11 +11,11 @@ namespace bnhtrade.Core.Data.Database
|
||||
public class Connection
|
||||
{
|
||||
//protected readonly string SqlConnectionString;
|
||||
private Model.Credentials.bnhtradeDB dbCredentials;
|
||||
private Model.Credentials.bnhtradeDB _dbCredentials;
|
||||
|
||||
protected string SqlConnectionString
|
||||
{
|
||||
get { return dbCredentials.ConnectionString; }
|
||||
get { return _dbCredentials.ConnectionString; }
|
||||
}
|
||||
|
||||
public Connection()
|
||||
@@ -44,7 +44,7 @@ namespace bnhtrade.Core.Data.Database
|
||||
}
|
||||
|
||||
var dbCredentials = new bnhtrade.Core.Model.Credentials.bnhtradeDB(dataSource, userId, pass);
|
||||
this.dbCredentials = dbCredentials;
|
||||
this._dbCredentials = dbCredentials;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -76,7 +76,7 @@ namespace bnhtrade.Core.Data.Database
|
||||
}
|
||||
|
||||
var dbCredentials = new bnhtrade.Core.Model.Credentials.bnhtradeDB(dataSource, userId, pass);
|
||||
this.dbCredentials = dbCredentials;
|
||||
this._dbCredentials = dbCredentials;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
@@ -91,7 +91,7 @@ namespace bnhtrade.Core.Data.Database
|
||||
{
|
||||
throw new Exception("DB credentials object is null");
|
||||
}
|
||||
this.dbCredentials = dbCredentials;
|
||||
this._dbCredentials = dbCredentials;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Consistency
|
||||
{
|
||||
public class ImportAmazonSettlement : Connection
|
||||
{
|
||||
public ImportAmazonSettlement()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public List<string> ErrorMessage { get; private set; }
|
||||
|
||||
public bool ErrorMessageIsSet
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ErrorMessage == null || !ErrorMessage.Any()) { return false; }
|
||||
else { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
public bool PeriodDateGaps()
|
||||
{
|
||||
ErrorMessage = null;
|
||||
|
||||
using (var sqlConn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
var log = new Logic.Log.LogEvent();
|
||||
sqlConn.Open();
|
||||
|
||||
// get list of marketplace names
|
||||
var marketplaces = new List<string>();
|
||||
using (SqlCommand cmd = new SqlCommand(@"
|
||||
SELECT DISTINCT tblImportAmazonSettlementReport.ImportAmazonSettlementReportID
|
||||
,tblImportAmazonSettlementReport.[marketplace-name]
|
||||
,tblImportAmazonSettlementReport.[settlement-start-date]
|
||||
,tblImportAmazonSettlementReport.[settlement-end-date]
|
||||
FROM tblImportAmazonSettlementReport
|
||||
ORDER BY tblImportAmazonSettlementReport.[marketplace-name]
|
||||
,tblImportAmazonSettlementReport.[settlement-start-date]
|
||||
,tblImportAmazonSettlementReport.[settlement-end-date];
|
||||
", sqlConn))
|
||||
{
|
||||
using(var reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (!reader.HasRows)
|
||||
{
|
||||
ErrorMessage.Add("No data found. Is the table empty?");
|
||||
return false;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
string marketplace = "some-random-string-7posadlnvl2oaweoh3amol5o5irv8nl2aoqefl";
|
||||
DateTime endDate = DateTime.SpecifyKind(default(DateTime), DateTimeKind.Utc);
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.IsDBNull(1))
|
||||
{
|
||||
log.LogError(
|
||||
"Action required: Enter market place name for settlelment report id " + reader.GetInt32(0) + "."
|
||||
, "Unable to process settlement data from one settlement report '" + reader.GetInt32(0) +
|
||||
"'. Report header table requires a market place name, which is not supplied in original " +
|
||||
"report from Amazon. This is useually inferred from settlement lines. " +
|
||||
"However, in this case, it was not not possible. Manual edit/entry for database table required."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
string newMarketPlace = reader.GetString(1);
|
||||
DateTime startDate = DateTime.SpecifyKind( reader.GetDateTime(2), DateTimeKind.Utc);
|
||||
|
||||
if (marketplace != newMarketPlace)
|
||||
{
|
||||
marketplace = newMarketPlace;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (i > 0 && startDate != endDate)
|
||||
{
|
||||
log.LogError("Inconsistancy in DB data found, break in settlement period dates.",
|
||||
marketplace + " at ID=" + reader.GetInt32(0) + " start-date is not the same as the previous period end-date."
|
||||
+ Environment.NewLine + "If it's a missing settlement report and it's over 90 days ago, you may need to go to"
|
||||
+ " 'Seller Central' > 'Payments' > 'All Statements' and manually 'Request Report' for each missing settlement.");
|
||||
return false;
|
||||
}
|
||||
|
||||
endDate = DateTime.SpecifyKind(reader.GetDateTime(3), DateTimeKind.Utc);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -8,9 +8,9 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Export
|
||||
{
|
||||
public class CreateAmazonFeedSubmission : Connection
|
||||
internal class AmazonFeedSubmissionInsert : Connection
|
||||
{
|
||||
public CreateAmazonFeedSubmission ()
|
||||
public AmazonFeedSubmissionInsert ()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -8,9 +8,9 @@ using Dapper;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Export
|
||||
{
|
||||
public class ReadAmazonFeedSubmission : Connection
|
||||
internal class AmazonFeedSubmissionRead : Connection
|
||||
{
|
||||
public ReadAmazonFeedSubmission()
|
||||
public AmazonFeedSubmissionRead()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Export
|
||||
{
|
||||
public class UpdateAmazonFeedSubmission : Connection
|
||||
internal class AmazonFeedSubmissionUpdate : Connection
|
||||
{
|
||||
public UpdateAmazonFeedSubmission()
|
||||
public AmazonFeedSubmissionUpdate()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Export
|
||||
{
|
||||
public class CreateSalesInvoice : Connection
|
||||
{
|
||||
public CreateSalesInvoice()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Execute(List<Model.Account.SalesInvoice> invoiceList)
|
||||
{
|
||||
using (TransactionScope scope = new TransactionScope())
|
||||
{
|
||||
using (SqlConnection sqlConn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
sqlConn.Open();
|
||||
|
||||
// make the inserts
|
||||
for (int i = 0; i < invoiceList.Count(); i++)
|
||||
{
|
||||
int invoiceId = 0;
|
||||
using (SqlCommand cmd = new SqlCommand(@"
|
||||
INSERT INTO tblExportAccountInvoice (
|
||||
ExportAccountInvoiceTypeID
|
||||
,Contact
|
||||
,InvoiceDate
|
||||
,InvoiceDueDate
|
||||
,InvoiceNumber
|
||||
,Reference
|
||||
,CurrencyCode
|
||||
,InvoiceAmount
|
||||
,IsComplete
|
||||
)
|
||||
OUTPUT INSERTED.ExportAccountInvoiceID
|
||||
VALUES (
|
||||
@invoiceTypeId
|
||||
,@contact
|
||||
,@invoiceDate
|
||||
,@invoiceDueDate
|
||||
,@invoiceNumber
|
||||
,@reference
|
||||
,@currencyCode
|
||||
,@invoiceAmount
|
||||
,@markComplete
|
||||
);
|
||||
", sqlConn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@invoiceTypeId", 2);
|
||||
cmd.Parameters.AddWithValue("@contact", invoiceList[i].ContactName);
|
||||
cmd.Parameters.AddWithValue("@invoiceDate", invoiceList[i].InvoiceDate);
|
||||
cmd.Parameters.AddWithValue("@invoiceDueDate", invoiceList[i].InvoiceDueDate);
|
||||
cmd.Parameters.AddWithValue("@reference", invoiceList[i].InvoiceReference);
|
||||
cmd.Parameters.AddWithValue("@currencyCode", invoiceList[i].InvoiceCurrencyCode);
|
||||
cmd.Parameters.AddWithValue("@markComplete", false);
|
||||
cmd.Parameters.AddWithValue("@invoiceNumber", invoiceList[i].InvoiceNumber);
|
||||
cmd.Parameters.AddWithValue("@invoiceAmount", invoiceList[i].InvoiceTotalAmount);
|
||||
|
||||
invoiceId = (int)cmd.ExecuteScalar();
|
||||
}
|
||||
|
||||
for (int j = 0; j < invoiceList[i].InvoiceLineList.Count(); j++)
|
||||
{
|
||||
// insert record
|
||||
using (SqlCommand cmd = new SqlCommand(@"
|
||||
INSERT INTO tblExportAccountInvoiceLine (
|
||||
ExportAccountInvoiceID
|
||||
,AccountInvoiceLineItemID
|
||||
,NetAmount
|
||||
,AccountChartOfID
|
||||
,TaxAmount
|
||||
,AccountTaxCodeID
|
||||
)
|
||||
OUTPUT INSERTED.ExportAccountInvoiceLineID
|
||||
VALUES (
|
||||
@invoiceId
|
||||
,(
|
||||
SELECT AccountInvoiceLineItemID
|
||||
FROM tblAccountInvoiceLineItem
|
||||
WHERE ItemCode = @itemCode
|
||||
)
|
||||
,@netAmount
|
||||
,(
|
||||
SELECT AccountChartOfID
|
||||
FROM tblAccountChartOf
|
||||
WHERE AccountCode = @accountCode
|
||||
)
|
||||
,@taxAmount
|
||||
,(
|
||||
SELECT AccountTaxCodeID
|
||||
FROM tblAccountTaxCode
|
||||
WHERE TaxCode = @taxCode
|
||||
)
|
||||
);
|
||||
", sqlConn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@invoiceID", invoiceId);
|
||||
cmd.Parameters.AddWithValue("@itemCode", invoiceList[i].InvoiceLineList[j].ItemCode);
|
||||
cmd.Parameters.AddWithValue("@netAmount", invoiceList[i].InvoiceLineList[j].UnitAmount);
|
||||
cmd.Parameters.AddWithValue("@accountCode", (int)invoiceList[i].InvoiceLineList[j].AccountCode.AccountCode);
|
||||
cmd.Parameters.AddWithValue("@taxAmount", invoiceList[i].InvoiceLineList[j].TaxAmount);
|
||||
cmd.Parameters.AddWithValue("@taxCode", invoiceList[i].InvoiceLineList[j].TaxCode.TaxCode);
|
||||
|
||||
int lineId = (int)cmd.ExecuteScalar();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Import
|
||||
{
|
||||
public class AmazonSettlementHeaderRead : Connection
|
||||
{
|
||||
private Dictionary<string, int> dicTablePkBySettlementId = new Dictionary<string, int>();
|
||||
private int? returnTop = null;
|
||||
private List<string> settlementIdList;
|
||||
private List<string> spapiReportId;
|
||||
|
||||
protected bool FilterOutIsProcessed { get; set; }
|
||||
|
||||
public bool DescendingOrder { get; set; }
|
||||
|
||||
public int ReturnTop
|
||||
{
|
||||
get { return (int)returnTop; }
|
||||
set
|
||||
{
|
||||
if (value > 0)
|
||||
{ returnTop = value; }
|
||||
else
|
||||
{ returnTop = null; }
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReturnTopIsSet
|
||||
{
|
||||
get { return returnTop != null; }
|
||||
}
|
||||
|
||||
private List<string> SettlementIdList
|
||||
{
|
||||
get { return settlementIdList; }
|
||||
set
|
||||
{
|
||||
if (value.Any())
|
||||
{ settlementIdList = value; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private List<string> SpapiReportIdList
|
||||
{
|
||||
get { return spapiReportId; }
|
||||
set
|
||||
{
|
||||
if (value.Any())
|
||||
{ spapiReportId = value; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private bool SettlementIdListIsSet
|
||||
{
|
||||
get { return SettlementIdList != null; }
|
||||
}
|
||||
|
||||
private bool SpapiReportIdIsSet
|
||||
{
|
||||
get { return SpapiReportIdList != null; }
|
||||
}
|
||||
|
||||
public AmazonSettlementHeaderRead()
|
||||
{
|
||||
Innit();
|
||||
}
|
||||
|
||||
private void Innit()
|
||||
{
|
||||
DescendingOrder = false;
|
||||
FilterOutIsProcessed = false;
|
||||
ReturnTop = 0;
|
||||
settlementIdList = null;
|
||||
spapiReportId = null;
|
||||
}
|
||||
|
||||
public List<Model.Import.AmazonSettlementHeader> AllUnprocessed()
|
||||
{
|
||||
Innit();
|
||||
FilterOutIsProcessed = true;
|
||||
return ReadHeaderList();
|
||||
}
|
||||
|
||||
public Model.Import.AmazonSettlementHeader BySettlementId(string settlementId)
|
||||
{
|
||||
Innit();
|
||||
|
||||
// create settlement list
|
||||
var idList = new List<string>();
|
||||
idList.Add(settlementId);
|
||||
var settlementList = BySettlementId(idList);
|
||||
|
||||
// return answer
|
||||
if (settlementList == null || !settlementList.Any())
|
||||
{ return null; }
|
||||
else
|
||||
{ return settlementList.First(); }
|
||||
}
|
||||
|
||||
public List<Model.Import.AmazonSettlementHeader> BySettlementId(List<string> settlementIdList)
|
||||
{
|
||||
Innit();
|
||||
|
||||
if (settlementIdList == null || !settlementIdList.Any())
|
||||
{ return new List<Model.Import.AmazonSettlementHeader>(); }
|
||||
|
||||
SettlementIdList = settlementIdList;
|
||||
|
||||
return ReadHeaderList();
|
||||
}
|
||||
|
||||
public List<Model.Import.AmazonSettlementHeader> BySpapiReportId(List<string> spapiReportIdList)
|
||||
{
|
||||
Innit();
|
||||
|
||||
if (spapiReportIdList == null || !spapiReportIdList.Any())
|
||||
{ return new List<Model.Import.AmazonSettlementHeader>(); }
|
||||
|
||||
SpapiReportIdList = spapiReportIdList;
|
||||
|
||||
return ReadHeaderList();
|
||||
}
|
||||
|
||||
private List<Model.Import.AmazonSettlementHeader> ReadHeaderList()
|
||||
{
|
||||
var returnHeaderList = new List<Model.Import.AmazonSettlementHeader>();
|
||||
|
||||
// build the sql string
|
||||
string sqlString = "SELECT ";
|
||||
|
||||
if (ReturnTopIsSet)
|
||||
{
|
||||
sqlString = sqlString + "TOP " + ReturnTop + " ";
|
||||
}
|
||||
|
||||
sqlString = sqlString + @"
|
||||
ImportAmazonSettlementReportID
|
||||
,[marketplace-name]
|
||||
,[settlement-id]
|
||||
,[settlement-start-date]
|
||||
,[settlement-end-date]
|
||||
,[deposit-date]
|
||||
,[total-amount]
|
||||
,currency
|
||||
,IsProcessed
|
||||
,SpapiReportId
|
||||
FROM tblImportAmazonSettlementReport
|
||||
WHERE 1 = 1";
|
||||
|
||||
if (FilterOutIsProcessed)
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
AND IsProcessed = 0";
|
||||
}
|
||||
|
||||
// build dictionary of parameter and values for settlementid
|
||||
var dicSettlementIdByParameterString = new Dictionary<string, string>();
|
||||
if (SettlementIdListIsSet)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (string item in SettlementIdList)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(item))
|
||||
{
|
||||
count = count + 1;
|
||||
string parameterString = "@settlementId" + count;
|
||||
dicSettlementIdByParameterString.Add(parameterString, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dicSettlementIdByParameterString.Any())
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var item in dicSettlementIdByParameterString)
|
||||
{
|
||||
count = count + 1;
|
||||
if (count == 1)
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
AND ( [settlement-id] = " + item.Key;
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
OR [settlement-id] = " + item.Key;
|
||||
}
|
||||
}
|
||||
sqlString = sqlString + " )";
|
||||
}
|
||||
|
||||
// build dictionary of parameter and values for SP-API Report Id
|
||||
var dicSpapiReportIdByParameterString = new Dictionary<string, string>();
|
||||
if (SpapiReportIdIsSet)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (string item in SpapiReportIdList)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(item))
|
||||
{
|
||||
count = count + 1;
|
||||
string parameterString = "@SpapiReportId" + count;
|
||||
dicSpapiReportIdByParameterString.Add(parameterString, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dicSpapiReportIdByParameterString.Any())
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var item in dicSpapiReportIdByParameterString)
|
||||
{
|
||||
count = count + 1;
|
||||
if (count == 1)
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
AND ( SpapiReportId = " + item.Key;
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
OR SpapiReportId = " + item.Key;
|
||||
}
|
||||
}
|
||||
sqlString = sqlString + " )";
|
||||
}
|
||||
|
||||
|
||||
sqlString = sqlString + @"
|
||||
ORDER BY [settlement-start-date] ";
|
||||
if (DescendingOrder) { sqlString = sqlString + " DESC"; }
|
||||
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(sqlString, conn))
|
||||
{
|
||||
if (dicSettlementIdByParameterString.Any())
|
||||
{
|
||||
foreach (var item in dicSettlementIdByParameterString)
|
||||
{
|
||||
cmd.Parameters.AddWithValue(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (dicSpapiReportIdByParameterString.Any())
|
||||
{
|
||||
foreach (var item in dicSpapiReportIdByParameterString)
|
||||
{
|
||||
cmd.Parameters.AddWithValue(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var header = new Model.Import.AmazonSettlementHeader();
|
||||
|
||||
int tablePk = reader.GetInt32(0);
|
||||
if (!reader.IsDBNull(1)) { header.MarketPlaceName = reader.GetString(1); }
|
||||
header.SettlementId = reader.GetString(2);
|
||||
header.StartDate = DateTime.SpecifyKind(reader.GetDateTime(3), DateTimeKind.Utc);
|
||||
header.EndDate = DateTime.SpecifyKind(reader.GetDateTime(4), DateTimeKind.Utc);
|
||||
header.DepositDate = DateTime.SpecifyKind(reader.GetDateTime(5), DateTimeKind.Utc);
|
||||
header.TotalAmount = reader.GetDecimal(6);
|
||||
header.CurrencyCode = reader.GetString(7);
|
||||
header.IsProcessed = reader.GetBoolean(8);
|
||||
if (!reader.IsDBNull(9)) { header.SpapiReportId = reader.GetString(9); }
|
||||
|
||||
// update dictionary
|
||||
if (!dicTablePkBySettlementId.ContainsKey(header.SettlementId))
|
||||
{
|
||||
dicTablePkBySettlementId.Add(header.SettlementId, tablePk);
|
||||
}
|
||||
|
||||
// add header to list
|
||||
returnHeaderList.Add(header);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnHeaderList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Import
|
||||
{
|
||||
public class AmazonSettlementInsert : Connection
|
||||
{
|
||||
Logic.Log.LogEvent log = new Logic.Log.LogEvent();
|
||||
|
||||
public AmazonSettlementInsert ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="reportId">The unique Amazon SP-API report id (not settlement id)</param>
|
||||
/// <returns></returns>
|
||||
public bool ByFlatFile(string filePath, string reportId)
|
||||
{
|
||||
using (TransactionScope scope = new TransactionScope())
|
||||
{
|
||||
using (SqlConnection sqlConn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
sqlConn.Open();
|
||||
|
||||
int settlementReportId = 0;
|
||||
string settlementRef = "";
|
||||
bool marketPlaceUpdated = false;
|
||||
decimal settlementAmount = 0m;
|
||||
int lineNumber = 2;
|
||||
int lineSkip = 0;
|
||||
|
||||
using (var reader = new StreamReader(filePath))
|
||||
{
|
||||
//read file one line at a time and insert data into table if required
|
||||
|
||||
// read header and retrive information
|
||||
string headerRow = reader.ReadLine();
|
||||
string[] headers = headerRow.Split('\t');
|
||||
|
||||
int columnCount = headers.Length;
|
||||
|
||||
int indexSettlementId = Array.IndexOf(headers, "settlement-id");
|
||||
int indexSettlementStartDate = Array.IndexOf(headers, "settlement-start-date");
|
||||
int indexSettlementEndDate = Array.IndexOf(headers, "settlement-end-date");
|
||||
int indexDepositDate = Array.IndexOf(headers, "deposit-date");
|
||||
int indexTotalAmount = Array.IndexOf(headers, "total-amount");
|
||||
int indexCurrency = Array.IndexOf(headers, "currency");
|
||||
int indexTransactionType = Array.IndexOf(headers, "transaction-type");
|
||||
int indexOrderId = Array.IndexOf(headers, "order-id");
|
||||
int indexMerchantOrderId = Array.IndexOf(headers, "merchant-order-id");
|
||||
int indexAdjustmentId = Array.IndexOf(headers, "adjustment-id");
|
||||
int indexShipmentId = Array.IndexOf(headers, "shipment-id");
|
||||
int indexMarketplaceName = Array.IndexOf(headers, "marketplace-name");
|
||||
int indexAmountType = Array.IndexOf(headers, "amount-type");
|
||||
int indexAmountDescription = Array.IndexOf(headers, "amount-description");
|
||||
int indexAmount = Array.IndexOf(headers, "amount");
|
||||
int indexFulfillmentId = Array.IndexOf(headers, "fulfillment-id");
|
||||
// int indexPostedDate = Array.IndexOf(headers, "posted-date");
|
||||
int indexPostedDateTime = Array.IndexOf(headers, "posted-date-time");
|
||||
int indexOrderItemCode = Array.IndexOf(headers, "order-item-code");
|
||||
int indexMerchantOrderItemId = Array.IndexOf(headers, "merchant-order-item-id");
|
||||
int indexMerchantAdjustmentItemId = Array.IndexOf(headers, "merchant-adjustment-item-id");
|
||||
int indexSku = Array.IndexOf(headers, "sku");
|
||||
int indexQuantityPurchased = Array.IndexOf(headers, "quantity-purchased");
|
||||
int indexPromotionId = Array.IndexOf(headers, "promotion-id");
|
||||
|
||||
string currency = "";
|
||||
|
||||
string fileRow;
|
||||
while ((fileRow = reader.ReadLine()) != null)
|
||||
{
|
||||
Console.Write("\rParsing record: " + lineNumber);
|
||||
//split line into array
|
||||
string[] items = fileRow.Split('\t');
|
||||
if (items.Length != columnCount)
|
||||
{
|
||||
// skip line
|
||||
lineSkip = lineSkip + 1;
|
||||
log.LogWarning(
|
||||
"Line #" + lineNumber + " skipped due to no enough element in row.",
|
||||
filePath
|
||||
);
|
||||
}
|
||||
else if (lineNumber == 2)
|
||||
{
|
||||
// check if settlement has already been imported
|
||||
using (SqlCommand sqlCommand = new SqlCommand(
|
||||
"SELECT COUNT(*) FROM tblImportAmazonSettlementReport WHERE [settlement-id]=@settlementId;"
|
||||
, sqlConn))
|
||||
{
|
||||
sqlCommand.Parameters.AddWithValue("@settlementId", items[indexSettlementId]);
|
||||
int recordCount = (int)sqlCommand.ExecuteScalar();
|
||||
if (recordCount > 0)
|
||||
{
|
||||
UpdateSpapiReportId(items[indexSettlementId], reportId);
|
||||
log.LogInformation("Settlement report already imported, skipping...");
|
||||
scope.Complete();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//set currencyId
|
||||
//currencyId = GeneralQueries.GetCurrencyId(items[5]);
|
||||
|
||||
//set currency
|
||||
currency = items[indexCurrency];
|
||||
settlementAmount = decimal.Parse(items[indexTotalAmount].Replace(",", "."));
|
||||
|
||||
// insert
|
||||
using (SqlCommand sqlCommand = new SqlCommand(@"
|
||||
INSERT INTO tblImportAmazonSettlementReport (
|
||||
[settlement-id]
|
||||
,[settlement-start-date]
|
||||
,[settlement-end-date]
|
||||
,[deposit-date]
|
||||
,[total-amount]
|
||||
,[currency]
|
||||
,SpapiReportId
|
||||
)
|
||||
OUTPUT INSERTED.ImportAmazonSettlementReportID
|
||||
VALUES (
|
||||
@settlementId
|
||||
,@settlementStartDate
|
||||
,@settlementEndDate
|
||||
,@depositDate
|
||||
,@settlementotalAmounttId
|
||||
,@currency
|
||||
,@reportId
|
||||
);
|
||||
", sqlConn))
|
||||
{
|
||||
// add parameters
|
||||
if (indexSettlementId == -1 || items[indexSettlementId].Length == 0) { sqlCommand.Parameters.AddWithValue("@settlementId", DBNull.Value); }
|
||||
else
|
||||
{
|
||||
settlementRef = items[indexSettlementId];
|
||||
sqlCommand.Parameters.AddWithValue("@settlementId", settlementRef);
|
||||
}
|
||||
|
||||
var parseDateTime = new Core.Logic.Utilities.DateTime();
|
||||
|
||||
if (indexSettlementStartDate == -1 || items[indexSettlementStartDate].Length == 0) { sqlCommand.Parameters.AddWithValue("@settlementStartDate", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@settlementStartDate", parseDateTime.ParseIsoDateTimeString(items[indexSettlementStartDate])); }
|
||||
|
||||
if (indexSettlementEndDate == -1 || items[indexSettlementEndDate].Length == 0) { sqlCommand.Parameters.AddWithValue("@settlementEndDate", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@settlementEndDate", parseDateTime.ParseIsoDateTimeString(items[indexSettlementEndDate])); }
|
||||
|
||||
if (indexDepositDate == -1 || items[indexDepositDate].Length == 0) { sqlCommand.Parameters.AddWithValue("@depositDate", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@depositDate", parseDateTime.ParseIsoDateTimeString(items[indexDepositDate])); }
|
||||
|
||||
if (indexTotalAmount == -1 || items[indexTotalAmount].Length == 0) { sqlCommand.Parameters.AddWithValue("@totalAmount", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@settlementotalAmounttId", settlementAmount); }
|
||||
|
||||
if (string.IsNullOrWhiteSpace(reportId)) { sqlCommand.Parameters.AddWithValue("@reportId", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@reportId", reportId); }
|
||||
|
||||
sqlCommand.Parameters.AddWithValue("@currency", currency);
|
||||
|
||||
//if (currencyId == -1) { sqlCommand.Parameters.AddWithValue("@currencyId", DBNull.Value); }
|
||||
//else { sqlCommand.Parameters.AddWithValue("@currencyId", currencyId); }
|
||||
|
||||
//execute and retrive id
|
||||
settlementReportId = (int)sqlCommand.ExecuteScalar();
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//update market place name in main table, if required
|
||||
if (marketPlaceUpdated == false && settlementReportId > 0 && items[indexMarketplaceName].Length > 1)
|
||||
{
|
||||
using (SqlCommand sqlCommand = new SqlCommand(@"
|
||||
UPDATE tblImportAmazonSettlementReport
|
||||
SET [marketplace-name]=@MarketplaceName
|
||||
WHERE ImportAmazonSettlementReportID=@ImportAmazonSettlementReportID
|
||||
", sqlConn))
|
||||
{
|
||||
sqlCommand.Parameters.AddWithValue("@MarketplaceName", items[indexMarketplaceName]);
|
||||
sqlCommand.Parameters.AddWithValue("@ImportAmazonSettlementReportID", settlementReportId);
|
||||
|
||||
sqlCommand.ExecuteNonQuery();
|
||||
marketPlaceUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
//insert report items
|
||||
using (SqlCommand sqlCommand = new SqlCommand(
|
||||
"INSERT INTO tblImportAmazonSettlementReportLine ( " +
|
||||
"ImportAmazonSettlementReportID, [transaction-type], [order-id], [merchant-order-id], [adjustment-id], [shipment-id], [marketplace-name], " +
|
||||
"[amount-type], [amount-description], [currency], [amount], [fulfillment-id], [posted-date-time], [order-item-code], " +
|
||||
"[merchant-order-item-id], [merchant-adjustment-item-id], [sku], [quantity-purchased], [promotion-id] ) " +
|
||||
"VALUES ( " +
|
||||
"@ImportAmazonSettlementReportID, @TransactionType, @orderRef, @merchantOrderRef, @AdjustmentRef, @ShipmentRef, @MarketplaceName, " +
|
||||
"@AmountType, @AmountDescription, @currency, @Amount, @FulfillmentRef, @PostedDateTimeUTC, @OrderItemCode, " +
|
||||
"@MerchantOrderItemRef, @MerchantAdjustmentItemRef, @SkuNumber, @QuantityPurchased, @PromotionRef );"
|
||||
, sqlConn))
|
||||
{
|
||||
// add parameters
|
||||
sqlCommand.Parameters.AddWithValue("@ImportAmazonSettlementReportID", settlementReportId);
|
||||
|
||||
sqlCommand.Parameters.AddWithValue("@currency", currency);
|
||||
|
||||
if (indexTransactionType == -1 || items[indexTransactionType].Length == 0) { sqlCommand.Parameters.AddWithValue("@TransactionType", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@TransactionType", items[indexTransactionType]); }
|
||||
|
||||
if (indexOrderId == -1 || items[indexOrderId].Length == 0) { sqlCommand.Parameters.AddWithValue("@orderRef", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@orderRef", items[indexOrderId]); }
|
||||
|
||||
if (indexMerchantOrderId == -1 || items[indexMerchantOrderId].Length == 0) { sqlCommand.Parameters.AddWithValue("@merchantOrderRef", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@merchantOrderRef", items[indexMerchantOrderId]); }
|
||||
|
||||
if (indexAdjustmentId == -1 || items[indexAdjustmentId].Length == 0) { sqlCommand.Parameters.AddWithValue("@AdjustmentRef", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@AdjustmentRef", items[indexAdjustmentId]); }
|
||||
|
||||
if (indexShipmentId == -1 || items[indexShipmentId].Length == 0) { sqlCommand.Parameters.AddWithValue("@ShipmentRef", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@ShipmentRef", items[indexShipmentId]); }
|
||||
|
||||
if (indexMarketplaceName == -1 || items[indexMarketplaceName].Length == 0) { sqlCommand.Parameters.AddWithValue("@MarketplaceName", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@MarketplaceName", items[indexMarketplaceName]); }
|
||||
|
||||
if (indexAmountType == -1 || items[indexAmountType].Length == 0) { sqlCommand.Parameters.AddWithValue("@AmountType", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@AmountType", items[indexAmountType]); }
|
||||
|
||||
if (indexAmountDescription == -1 || items[indexAmountDescription].Length == 0) { sqlCommand.Parameters.AddWithValue("@AmountDescription", DBNull.Value); }
|
||||
else
|
||||
{
|
||||
string amountDescription = items[indexAmountDescription];
|
||||
if (amountDescription.Length > 100) { amountDescription = amountDescription.Substring(0, 100); }
|
||||
sqlCommand.Parameters.AddWithValue("@AmountDescription", amountDescription);
|
||||
}
|
||||
|
||||
if (indexAmount == -1 || items[indexAmount].Length == 0) { sqlCommand.Parameters.AddWithValue("@Amount", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@Amount", decimal.Parse(items[indexAmount].Replace(",", "."))); }
|
||||
|
||||
if (indexFulfillmentId == -1 || items[indexFulfillmentId].Length == 0) { sqlCommand.Parameters.AddWithValue("@FulfillmentRef", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@FulfillmentRef", items[indexFulfillmentId]); }
|
||||
|
||||
if (indexPostedDateTime == -1 || items[indexPostedDateTime].Length == 0) { sqlCommand.Parameters.AddWithValue("@PostedDateTimeUTC", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@PostedDateTimeUTC", new Logic.Utilities.DateTime().ParseIsoDateTimeString(items[indexPostedDateTime])); }
|
||||
|
||||
if (indexOrderItemCode == -1 || items[indexOrderItemCode].Length == 0) { sqlCommand.Parameters.AddWithValue("@OrderItemCode", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@OrderItemCode", long.Parse(items[indexOrderItemCode])); }
|
||||
|
||||
if (indexMerchantOrderItemId == -1 || items[indexMerchantOrderItemId].Length == 0) { sqlCommand.Parameters.AddWithValue("@MerchantOrderItemRef", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@MerchantOrderItemRef", long.Parse(items[indexMerchantOrderItemId])); }
|
||||
|
||||
if (indexMerchantAdjustmentItemId == -1 || items[indexMerchantAdjustmentItemId].Length == 0) { sqlCommand.Parameters.AddWithValue("@MerchantAdjustmentItemRef", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@MerchantAdjustmentItemRef", items[indexMerchantAdjustmentItemId]); }
|
||||
|
||||
if (indexSku == -1 || items[indexSku].Length == 0) { sqlCommand.Parameters.AddWithValue("@SkuNumber", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@SkuNumber", items[indexSku]); }
|
||||
|
||||
if (indexQuantityPurchased == -1 || items[indexQuantityPurchased].Length == 0) { sqlCommand.Parameters.AddWithValue("@QuantityPurchased", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@QuantityPurchased", int.Parse(items[indexQuantityPurchased])); }
|
||||
|
||||
if (indexPromotionId == -1 || items[indexPromotionId].Length == 0) { sqlCommand.Parameters.AddWithValue("@PromotionRef", DBNull.Value); }
|
||||
else { sqlCommand.Parameters.AddWithValue("@PromotionRef", items[indexPromotionId]); }
|
||||
|
||||
sqlCommand.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
lineNumber = lineNumber + 1;
|
||||
}
|
||||
}
|
||||
|
||||
//final check - settlement amount matches sum of inserted settlement lines
|
||||
using (SqlCommand sqlCommand = new SqlCommand(@"
|
||||
SELECT Sum(tblImportAmazonSettlementReportLine.amount) AS SumOfAmount
|
||||
FROM tblImportAmazonSettlementReportLine
|
||||
WHERE ImportAmazonSettlementReportID=@ImportAmazonSettlementReportID;
|
||||
", sqlConn))
|
||||
{
|
||||
decimal sumOfAmount = -1.12345m;
|
||||
|
||||
sqlCommand.Parameters.AddWithValue("@ImportAmazonSettlementReportID", settlementReportId);
|
||||
sumOfAmount = (decimal)sqlCommand.ExecuteScalar();
|
||||
|
||||
if (sumOfAmount != settlementAmount)
|
||||
{
|
||||
log.LogError("Error importing settlement id'" + settlementRef + "'. Sum of inserted settlement lines (" + sumOfAmount +
|
||||
") does not match settlement amount (" + settlementAmount + ").");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
scope.Complete();
|
||||
|
||||
Console.Write("\r");
|
||||
log.LogInformation((lineNumber - (2 + lineSkip)) + " total settlement items inserted");
|
||||
if (lineSkip > 0)
|
||||
{
|
||||
log.LogError(lineSkip + " total line(s) where skipped due to insufficent number of cells on row");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdateSpapiReportId (string settlementId, string spapiReportId)
|
||||
{
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(@"
|
||||
UPDATE
|
||||
tblImportAmazonSettlementReport
|
||||
SET
|
||||
SpapiReportId = @spapiReportId
|
||||
WHERE
|
||||
[settlement-id] = @settlementId
|
||||
", conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@spapiReportId", spapiReportId);
|
||||
cmd.Parameters.AddWithValue("@settlementId", settlementId);
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,462 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Import
|
||||
{
|
||||
public class AmazonSettlementRead : Connection
|
||||
{
|
||||
private Data.Database.SqlWhereBuilder whereBuilder = new SqlWhereBuilder();
|
||||
|
||||
public List<Model.Import.AmazonSettlement> BySettlementIdList(List<string> settlementIdList)
|
||||
{
|
||||
var settlementList = new List<Model.Import.AmazonSettlement>();
|
||||
|
||||
// build sql statement
|
||||
|
||||
string sql = @"
|
||||
SELECT tblImportAmazonSettlementReport.[settlement-id]
|
||||
,tblImportAmazonSettlementReportLine.[transaction-type]
|
||||
,tblImportAmazonSettlementReportLine.[order-id]
|
||||
,tblImportAmazonSettlementReportLine.[merchant-order-id]
|
||||
,tblImportAmazonSettlementReportLine.[adjustment-id]
|
||||
,tblImportAmazonSettlementReportLine.[shipment-id]
|
||||
,tblImportAmazonSettlementReportLine.[marketplace-name]
|
||||
,tblImportAmazonSettlementReportLine.[amount-type]
|
||||
,tblImportAmazonSettlementReportLine.[amount-description]
|
||||
,tblImportAmazonSettlementReportLine.amount
|
||||
,tblImportAmazonSettlementReportLine.currency
|
||||
,tblImportAmazonSettlementReportLine.[fulfillment-id]
|
||||
,tblImportAmazonSettlementReportLine.[posted-date-time]
|
||||
,tblImportAmazonSettlementReportLine.[order-item-code]
|
||||
,tblImportAmazonSettlementReportLine.[merchant-order-item-id]
|
||||
,tblImportAmazonSettlementReportLine.[merchant-adjustment-item-id]
|
||||
,tblImportAmazonSettlementReportLine.sku
|
||||
,tblImportAmazonSettlementReportLine.[quantity-purchased]
|
||||
,tblImportAmazonSettlementReportLine.[promotion-id]
|
||||
,tblImportAmazonSettlementReportLine.IsProcessed
|
||||
,tblImportAmazonSettlementReportLine.ExportAccountInvoiceLineID
|
||||
FROM tblImportAmazonSettlementReport
|
||||
INNER JOIN tblImportAmazonSettlementReportLine ON tblImportAmazonSettlementReport.ImportAmazonSettlementReportID = tblImportAmazonSettlementReportLine.ImportAmazonSettlementReportID
|
||||
WHERE ";
|
||||
|
||||
whereBuilder.Init();
|
||||
whereBuilder.In("tblImportAmazonSettlementReport.[settlement-id]", settlementIdList);
|
||||
|
||||
sql += whereBuilder.SqlWhereString;
|
||||
|
||||
sql += @"
|
||||
ORDER BY tblImportAmazonSettlementReport.[settlement-id]
|
||||
,tblImportAmazonSettlementReportLine.[posted-date-time] ";
|
||||
|
||||
// set variables
|
||||
bool firstRecord = true;
|
||||
string settlementId = "";
|
||||
var lineList = new List<Model.Import.AmazonSettlement.SettlementLine>();
|
||||
var LineListDic = new Dictionary<string, List<Model.Import.AmazonSettlement.SettlementLine>>();
|
||||
var headerList = new List<Model.Import.AmazonSettlementHeader>();
|
||||
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(sql, conn))
|
||||
{
|
||||
whereBuilder.AddParametersToSqlCommand(cmd);
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (!reader.HasRows)
|
||||
{
|
||||
return settlementList;
|
||||
}
|
||||
|
||||
// get the header list
|
||||
headerList = new Data.Database.Import.AmazonSettlementHeaderRead().BySettlementId(settlementIdList);
|
||||
|
||||
// loop through table to build dictionary
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.GetString(0) != settlementId)
|
||||
{
|
||||
if (firstRecord)
|
||||
{
|
||||
firstRecord = false;
|
||||
settlementId = reader.GetString(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
LineListDic.Add(settlementId, lineList);
|
||||
settlementId = reader.GetString(0);
|
||||
lineList = new List<Model.Import.AmazonSettlement.SettlementLine>();
|
||||
}
|
||||
}
|
||||
|
||||
var line = new Model.Import.AmazonSettlement.SettlementLine();
|
||||
|
||||
line.TransactionType = reader.GetString(1);
|
||||
if (!reader.IsDBNull(2)) { line.OrderId = reader.GetString(2); }
|
||||
if (!reader.IsDBNull(3)) { line.MerchantOrderId = reader.GetString(3); }
|
||||
if (!reader.IsDBNull(4)) { line.AdjustmentId = reader.GetString(4); }
|
||||
if (!reader.IsDBNull(5)) { line.ShipmentId = reader.GetString(5); }
|
||||
if (!reader.IsDBNull(6)) { line.MarketPlaceName = reader.GetString(6); }
|
||||
line.AmountType = reader.GetString(7);
|
||||
line.AmountDescription = reader.GetString(8);
|
||||
line.Amount = reader.GetDecimal(9);
|
||||
line.CurrenyCode = reader.GetString(10);
|
||||
if (!reader.IsDBNull(11)) { line.FulfillmentId = reader.GetString(11); }
|
||||
line.PostDateTime = DateTime.SpecifyKind(reader.GetDateTime(12), DateTimeKind.Utc);
|
||||
if (!reader.IsDBNull(13)) { line.OrderItemCode = reader.GetString(13); }
|
||||
if (!reader.IsDBNull(14)) { line.MerchantOrderItemId = reader.GetString(14); }
|
||||
if (!reader.IsDBNull(15)) { line.MerchantAdjustmentItemId = reader.GetString(15); }
|
||||
if (!reader.IsDBNull(16)) { line.Sku = reader.GetString(16); }
|
||||
if (!reader.IsDBNull(17)) { line.QuantityPurchased = reader.GetInt32(17); }
|
||||
if (!reader.IsDBNull(18)) { line.PromotionId = reader.GetString(18); }
|
||||
line.IsProcessed = reader.GetBoolean(19);
|
||||
if (!reader.IsDBNull(20)) { int exportAccountInvoiceLineId = reader.GetInt32(20); }
|
||||
|
||||
lineList.Add(line);
|
||||
}
|
||||
LineListDic.Add(settlementId, lineList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//create return list
|
||||
foreach(var item in headerList)
|
||||
{
|
||||
var settlementReport = new Model.Import.AmazonSettlement(item);
|
||||
settlementReport.SettlementLineList = LineListDic[item.SettlementId];
|
||||
settlementList.Add(settlementReport);
|
||||
}
|
||||
|
||||
return settlementList;
|
||||
}
|
||||
|
||||
public void ByHeaderList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void BySettlementId()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Dictionary<string, int> dicTablePkBySettlementId = new Dictionary<string, int>();
|
||||
private int? returnTop = null;
|
||||
private List<string> settlementIdList;
|
||||
|
||||
private bool FilterOutIsProcessed { get; set; }
|
||||
|
||||
public bool DescendingOrder { get; set; }
|
||||
|
||||
public int ReturnTop
|
||||
{
|
||||
get { return (int)returnTop; }
|
||||
set
|
||||
{
|
||||
if (value > 0)
|
||||
{ returnTop = value; }
|
||||
else
|
||||
{ returnTop = null; }
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReturnTopIsSet
|
||||
{
|
||||
get { return returnTop != null; }
|
||||
}
|
||||
|
||||
private List<string> SettlementIdList
|
||||
{
|
||||
get { return settlementIdList; }
|
||||
set
|
||||
{
|
||||
if (value.Any())
|
||||
{ settlementIdList = value; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private bool SettlementIdListIsSet
|
||||
{
|
||||
get { return SettlementIdList != null; }
|
||||
}
|
||||
|
||||
public AmazonSettlementRead()
|
||||
{
|
||||
Innit();
|
||||
}
|
||||
|
||||
private void Innit()
|
||||
{
|
||||
DescendingOrder = false;
|
||||
FilterOutIsProcessed = false;
|
||||
ReturnTop = 0;
|
||||
settlementIdList = null;
|
||||
}
|
||||
|
||||
public List<Model.Import.AmazonSettlement> AllUnprocessed()
|
||||
{
|
||||
Innit();
|
||||
FilterOutIsProcessed = true;
|
||||
return ExecuteDbQuery();
|
||||
}
|
||||
|
||||
public Model.Import.AmazonSettlement BySettlementId(string settlementId)
|
||||
{
|
||||
Innit();
|
||||
|
||||
// create settlement list
|
||||
var idList = new List<string>();
|
||||
idList.Add(settlementId);
|
||||
var settlementList = BySettlementId(idList);
|
||||
|
||||
// return answer
|
||||
if (settlementList == null || !settlementList.Any())
|
||||
{ return null; }
|
||||
else
|
||||
{ return settlementList.First(); }
|
||||
}
|
||||
|
||||
public List<Model.Import.AmazonSettlement> BySettlementId(List<string> settlementIdList)
|
||||
{
|
||||
Innit();
|
||||
|
||||
if (settlementIdList == null || !settlementIdList.Any())
|
||||
{ return null; }
|
||||
|
||||
SettlementIdList = settlementIdList;
|
||||
|
||||
return ExecuteDbQuery();
|
||||
}
|
||||
|
||||
private List<Model.Import.AmazonSettlement> ExecuteDbQuery()
|
||||
{
|
||||
// get header info
|
||||
var settlementList = ReadHeaderList();
|
||||
if (settlementList == null || !settlementList.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// add lines to header
|
||||
foreach (var item in settlementList)
|
||||
{
|
||||
if (!dicTablePkBySettlementId.ContainsKey(item.SettlementId))
|
||||
{
|
||||
throw new Exception("This shouldnt' happen!");
|
||||
}
|
||||
int tablePk = dicTablePkBySettlementId[item.SettlementId];
|
||||
|
||||
item.SettlementLineList = ReadLineList(tablePk);
|
||||
}
|
||||
|
||||
return settlementList;
|
||||
}
|
||||
|
||||
private List<Model.Import.AmazonSettlement> ReadHeaderList()
|
||||
{
|
||||
// build the sql string
|
||||
string sqlString = "SELECT ";
|
||||
|
||||
if (ReturnTopIsSet)
|
||||
{
|
||||
sqlString = sqlString + "TOP " + ReturnTop + " ";
|
||||
}
|
||||
|
||||
sqlString = sqlString + @"
|
||||
ImportAmazonSettlementReportID
|
||||
,[marketplace-name]
|
||||
,[settlement-id]
|
||||
,[settlement-start-date]
|
||||
,[settlement-end-date]
|
||||
,[deposit-date]
|
||||
,[total-amount]
|
||||
,currency
|
||||
,IsProcessed
|
||||
FROM tblImportAmazonSettlementReport
|
||||
WHERE 1 = 1";
|
||||
|
||||
if (FilterOutIsProcessed)
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
AND IsProcessed = 0";
|
||||
}
|
||||
|
||||
// build dictionary of parameter and values
|
||||
var dicSettlementIdByParameterString = new Dictionary<string, string>();
|
||||
if (SettlementIdListIsSet)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (string item in SettlementIdList)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(item))
|
||||
{
|
||||
count = count + 1;
|
||||
string parameterString = "@settlementId" + count;
|
||||
dicSettlementIdByParameterString.Add(parameterString, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dicSettlementIdByParameterString.Any())
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var item in dicSettlementIdByParameterString)
|
||||
{
|
||||
count = count + 1;
|
||||
if (count == 1)
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
AND ( [settlement-id] = " + item.Key;
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
OR [settlement-id] = " + item.Key;
|
||||
}
|
||||
}
|
||||
sqlString = sqlString + " )";
|
||||
}
|
||||
|
||||
sqlString = sqlString + @"
|
||||
ORDER BY [settlement-start-date] ";
|
||||
if (DescendingOrder) { sqlString = sqlString + " DESC"; }
|
||||
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(sqlString, conn))
|
||||
{
|
||||
if (dicSettlementIdByParameterString.Any())
|
||||
{
|
||||
foreach (var item in dicSettlementIdByParameterString)
|
||||
{
|
||||
cmd.Parameters.AddWithValue(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (!reader.HasRows)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var headerList = new List<Model.Import.AmazonSettlement>();
|
||||
while (reader.Read())
|
||||
{
|
||||
var header = new Model.Import.AmazonSettlement();
|
||||
|
||||
int tablePk = reader.GetInt32(0);
|
||||
if (!reader.IsDBNull(1)) { header.MarketPlaceName = reader.GetString(1); }
|
||||
header.SettlementId = reader.GetString(2);
|
||||
header.StartDate = DateTime.SpecifyKind(reader.GetDateTime(3), DateTimeKind.Utc);
|
||||
header.EndDate = DateTime.SpecifyKind(reader.GetDateTime(4), DateTimeKind.Utc);
|
||||
header.DepositDate = DateTime.SpecifyKind(reader.GetDateTime(5), DateTimeKind.Utc);
|
||||
header.TotalAmount = reader.GetDecimal(6);
|
||||
header.CurrencyCode = reader.GetString(7);
|
||||
header.IsProcessed = reader.GetBoolean(8);
|
||||
|
||||
// update dictionary
|
||||
if (!dicTablePkBySettlementId.ContainsKey(header.SettlementId))
|
||||
{
|
||||
dicTablePkBySettlementId.Add(header.SettlementId, tablePk);
|
||||
}
|
||||
|
||||
// add header to list
|
||||
headerList.Add(header);
|
||||
}
|
||||
|
||||
return headerList;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Model.Import.AmazonSettlement.SettlementLine> ReadLineList(int settlementPk)
|
||||
{
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(@"
|
||||
SELECT ImportAmazonSettlementReportLineID
|
||||
,[transaction-type]
|
||||
,[order-id]
|
||||
,[merchant-order-id]
|
||||
,[adjustment-id]
|
||||
,[shipment-id]
|
||||
,[marketplace-name]
|
||||
,[amount-type]
|
||||
,[amount-description]
|
||||
,amount
|
||||
,currency
|
||||
,[fulfillment-id]
|
||||
,[posted-date-time]
|
||||
,[order-item-code]
|
||||
,[merchant-order-item-id]
|
||||
,[merchant-adjustment-item-id]
|
||||
,sku
|
||||
,[quantity-purchased]
|
||||
,[promotion-id]
|
||||
,IsProcessed
|
||||
,ExportAccountInvoiceLineID
|
||||
FROM tblImportAmazonSettlementReportLine
|
||||
WHERE ImportAmazonSettlementReportID = @settlementPk
|
||||
ORDER BY [posted-date-time]
|
||||
", conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@settlementPk", settlementPk);
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (!reader.HasRows)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var lineList = new List<Model.Import.AmazonSettlement.SettlementLine>();
|
||||
while (reader.Read())
|
||||
{
|
||||
var line = new Model.Import.AmazonSettlement.SettlementLine();
|
||||
|
||||
int tablePk = reader.GetInt32(0);
|
||||
line.TransactionType = reader.GetString(1);
|
||||
if (!reader.IsDBNull(2)) { line.OrderId = reader.GetString(2); }
|
||||
if (!reader.IsDBNull(3)) { line.MerchantOrderId = reader.GetString(3); }
|
||||
if (!reader.IsDBNull(4)) { line.AdjustmentId = reader.GetString(4); }
|
||||
if (!reader.IsDBNull(5)) { line.ShipmentId = reader.GetString(5); }
|
||||
if (!reader.IsDBNull(6)) { line.MarketPlaceName = reader.GetString(6); }
|
||||
line.AmountType = reader.GetString(7);
|
||||
line.AmountDescription = reader.GetString(8);
|
||||
line.Amount = reader.GetDecimal(9);
|
||||
line.CurrenyCode = reader.GetString(10);
|
||||
if (!reader.IsDBNull(11)) { line.FulfillmentId = reader.GetString(11); }
|
||||
line.PostDateTime = DateTime.SpecifyKind(reader.GetDateTime(12), DateTimeKind.Utc);
|
||||
if (!reader.IsDBNull(13)) { line.OrderItemCode = reader.GetString(13); }
|
||||
if (!reader.IsDBNull(14)) { line.MerchantOrderItemId = reader.GetString(14); }
|
||||
if (!reader.IsDBNull(15)) { line.MerchantAdjustmentItemId = reader.GetString(15); }
|
||||
if (!reader.IsDBNull(16)) { line.Sku = reader.GetString(16); }
|
||||
if (!reader.IsDBNull(17)) { line.QuantityPurchased = reader.GetInt32(17); }
|
||||
if (!reader.IsDBNull(18)) { line.PromotionId = reader.GetString(18); }
|
||||
line.IsProcessed = reader.GetBoolean(19);
|
||||
if (!reader.IsDBNull(20)) { int exportAccountInvoiceLineId = reader.GetInt32(20); }
|
||||
|
||||
lineList.Add(line);
|
||||
}
|
||||
return lineList;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Import
|
||||
{
|
||||
public class AmazonSettlementUpdate : Connection
|
||||
{
|
||||
public AmazonSettlementUpdate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void SetIsProcessedTrue(List<string> settlementIdList)
|
||||
{
|
||||
if (settlementIdList == null || !settlementIdList.Any())
|
||||
{
|
||||
throw new Exception("Settlement ID list is empty.");
|
||||
}
|
||||
|
||||
string sqlString = @"
|
||||
UPDATE tblImportAmazonSettlementReport
|
||||
SET IsProcessed = 1
|
||||
WHERE (1=0)";
|
||||
|
||||
for (int i = 0; i < settlementIdList.Count(); i++)
|
||||
{
|
||||
sqlString += @"
|
||||
OR ([settlement-id] = @settlementId" + i + ")";
|
||||
}
|
||||
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(sqlString, conn))
|
||||
{
|
||||
for (int i = 0; i < settlementIdList.Count(); i++)
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@settlementId" + i, settlementIdList[i]);
|
||||
}
|
||||
|
||||
if (cmd.ExecuteNonQuery() == 0)
|
||||
{
|
||||
throw new Exception("Something went wrong updating settlement status.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Programmability
|
||||
{
|
||||
public class Sequence : Connection
|
||||
{
|
||||
public Sequence ()
|
||||
{
|
||||
|
||||
}
|
||||
public int GetNext(string sequenceName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sequenceName))
|
||||
{
|
||||
throw new Exception("Sequence name is null or whitespace.");
|
||||
}
|
||||
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(@"
|
||||
SELECT NEXT VALUE FOR " + sequenceName
|
||||
, conn))
|
||||
{
|
||||
//cmd.Parameters.AddWithValue("@sequenceName", sequenceName);
|
||||
// it wouldn't let me use parameters
|
||||
|
||||
object obj = cmd.ExecuteScalar();
|
||||
|
||||
try
|
||||
{
|
||||
//string whaaaat = (string)obj;
|
||||
return Convert.ToInt32(obj);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("Error returning next value in sequence: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
using bnhtrade.Core.Data.Database.Repository.Interface;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Repository.Implementation
|
||||
{
|
||||
internal class CurrencyRepository : _Base, ICurrencyRepository
|
||||
{
|
||||
public CurrencyRepository(IDbConnection connection, IDbTransaction transaction) : base(connection, transaction)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns excahnge rate, in decimal format, for a given currency and datetime
|
||||
/// </summary>
|
||||
/// <param name="currencyCode">currency code</param>
|
||||
/// <param name="date">dat and time</param>
|
||||
/// <returns></returns>
|
||||
public decimal? ReadExchangeRate(Model.Account.CurrencyCode currencyCode, DateTime date)
|
||||
{
|
||||
string sql = @"
|
||||
SELECT CurrencyUnitsPerGBP
|
||||
FROM tblAccountExchangeRate
|
||||
WHERE CurrencyCode=@currencyCode AND StartDate<=@conversionDate AND EndDate>@conversionDate
|
||||
";
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
cmd.Parameters.AddWithValue("@currencyCode", currencyCode.ToString());
|
||||
cmd.Parameters.AddWithValue("@conversionDate", date);
|
||||
|
||||
object result = cmd.ExecuteScalar();
|
||||
if (result != null)
|
||||
{
|
||||
return Convert.ToDecimal(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Model.Account.CurrencyExchangeRate> ReadExchangeRate(List<Model.Account.CurrencyCode> currencyCodeList = null, DateTime date = default(DateTime))
|
||||
{
|
||||
//throw new NotImplementedException("Complete, but untested");
|
||||
|
||||
var returnList = new List<Model.Account.CurrencyExchangeRate>();
|
||||
|
||||
string sql = @"
|
||||
SELECT AccountExchangeRateID, ExchangeRateSource, CurrencyCode, CurrencyUnitsPerGBP, StartDate, EndDate
|
||||
FROM tblAccountExchangeRate
|
||||
WHERE 1=1 ";
|
||||
|
||||
if (date != default(DateTime))
|
||||
{
|
||||
sql = sql + " AND (@dateTime >= StartDate AND @dateTime < endDate) ";
|
||||
}
|
||||
|
||||
var sqlWhere = new Data.Database.SqlWhereBuilder();
|
||||
|
||||
// create string list
|
||||
List<string> stringList = new List<string>();
|
||||
if (currencyCodeList != null)
|
||||
{
|
||||
stringList = currencyCodeList.ConvertAll(f => f.ToString());
|
||||
if (stringList.Any())
|
||||
{
|
||||
sqlWhere.In("CurrencyCode", stringList, "AND");
|
||||
}
|
||||
}
|
||||
|
||||
sql = sql + sqlWhere.SqlWhereString;
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
if (date != default(DateTime))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@dateTime", date);
|
||||
}
|
||||
if (stringList.Any())
|
||||
{
|
||||
sqlWhere.AddParametersToSqlCommand(cmd);
|
||||
}
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
int id = reader.GetInt32(0);
|
||||
int exchangeRateSource = reader.GetInt32(1);
|
||||
string currencyCodeString = reader.GetString(2);
|
||||
decimal currencyUnitsPerGBP = reader.GetDecimal(3);
|
||||
DateTime startDate = DateTime.SpecifyKind(reader.GetDateTime(4), DateTimeKind.Utc);
|
||||
DateTime endDate = DateTime.SpecifyKind(reader.GetDateTime(5), DateTimeKind.Utc);
|
||||
|
||||
|
||||
// convert string to enum
|
||||
if (Enum.TryParse(currencyCodeString, out Model.Account.CurrencyCode currencyCode) == false)
|
||||
{
|
||||
throw new Exception("Failed converting database string to enum");
|
||||
}
|
||||
|
||||
var item = new Model.Account.CurrencyExchangeRate(
|
||||
currencyCode
|
||||
, exchangeRateSource
|
||||
, currencyUnitsPerGBP
|
||||
, startDate
|
||||
, endDate
|
||||
);
|
||||
|
||||
returnList.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
||||
public List<Model.Account.CurrencyExchangeRate> ReadExchangeRateLatest(List<Model.Account.CurrencyCode> currencyCodeList = null)
|
||||
{
|
||||
var returnList = new List<Model.Account.CurrencyExchangeRate>();
|
||||
|
||||
string sql = @"
|
||||
SELECT t1.AccountExchangeRateID, t1.ExchangeRateSource, t1.CurrencyCode, t1.CurrencyUnitsPerGBP, t1.StartDate, t1.EndDate,
|
||||
t2.maxdate
|
||||
FROM tblAccountExchangeRate AS t1
|
||||
INNER JOIN
|
||||
(SELECT max(StartDate) AS maxdate,
|
||||
CurrencyCode
|
||||
FROM tblAccountExchangeRate
|
||||
GROUP BY CurrencyCode
|
||||
";
|
||||
|
||||
// add any filters
|
||||
var sqlWhere = new Data.Database.SqlWhereBuilder();
|
||||
var codeStringList = new List<string>();
|
||||
if (currencyCodeList != null && currencyCodeList.Any() == true)
|
||||
{
|
||||
// convert to string list
|
||||
foreach (var currencyCode in currencyCodeList)
|
||||
{
|
||||
codeStringList.Add(currencyCode.ToString());
|
||||
}
|
||||
|
||||
// add to where statement
|
||||
sqlWhere.In("CurrencyCode", codeStringList, "HAVING");
|
||||
sql = sql + sqlWhere.SqlWhereString;
|
||||
}
|
||||
|
||||
sql = sql + @" ) AS t2
|
||||
ON t1.CurrencyCode = t2.CurrencyCode
|
||||
AND t1.StartDate = t2.maxdate
|
||||
ORDER BY t1.CurrencyCode;";
|
||||
|
||||
//query db
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
if (codeStringList.Any())
|
||||
{
|
||||
sqlWhere.AddParametersToSqlCommand(cmd);
|
||||
}
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
int id = reader.GetInt32(0);
|
||||
int exchangeRateSource = reader.GetInt32(1);
|
||||
string currencyCodeString = reader.GetString(2);
|
||||
decimal currencyUnitsPerGBP = reader.GetDecimal(3);
|
||||
DateTime startDate = DateTime.SpecifyKind(reader.GetDateTime(4), DateTimeKind.Utc);
|
||||
DateTime endDate = DateTime.SpecifyKind(reader.GetDateTime(5), DateTimeKind.Utc);
|
||||
|
||||
|
||||
// convert string to enum
|
||||
if (Enum.TryParse(currencyCodeString, out Model.Account.CurrencyCode currencyCode) == false)
|
||||
{
|
||||
throw new Exception("Failed converting database string to enum");
|
||||
}
|
||||
|
||||
var item = new Model.Account.CurrencyExchangeRate(
|
||||
currencyCode
|
||||
, exchangeRateSource
|
||||
, currencyUnitsPerGBP
|
||||
, startDate
|
||||
, endDate
|
||||
);
|
||||
|
||||
returnList.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
||||
public int InsertExchangeRate(int exchangeRateSource, Model.Account.CurrencyCode currencyCode,
|
||||
decimal currencyUnitsPerGbp, DateTime periodStartUtc, DateTime periodEnd, bool checkOverride = false)
|
||||
{
|
||||
// checks
|
||||
if (periodStartUtc.Kind != DateTimeKind.Utc || periodEnd.Kind != DateTimeKind.Utc)
|
||||
{
|
||||
throw new FormatException("Currency date time kind must be UTC");
|
||||
}
|
||||
|
||||
currencyUnitsPerGbp = decimal.Round(currencyUnitsPerGbp, 4);
|
||||
|
||||
// CHECKS
|
||||
if (periodEnd <= periodStartUtc)
|
||||
{
|
||||
throw new Exception("Invalid date period.");
|
||||
}
|
||||
|
||||
if (checkOverride == false && (periodEnd - periodStartUtc).Days > 31)
|
||||
{
|
||||
throw new Exception("Date period is greater than 31 days.");
|
||||
}
|
||||
|
||||
string sql = @"
|
||||
INSERT INTO tblAccountExchangeRate (ExchangeRateSource, CurrencyCode, CurrencyUnitsPerGBP, StartDate, EndDate)
|
||||
OUTPUT INSERTED.AccountExchangeRateID
|
||||
VALUES (@exchangeRateSource, @currencyCode, @currencyUnitsPerGbp, @periodStart, @periodEnd);
|
||||
";
|
||||
|
||||
// make the insert
|
||||
DateTime? periodEndLast = null;
|
||||
int recordId = 0;
|
||||
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
cmd.Parameters.AddWithValue("@exchangeRateSource", exchangeRateSource);
|
||||
cmd.Parameters.AddWithValue("@currencyCode", currencyCode.ToString());
|
||||
cmd.Parameters.AddWithValue("@currencyUnitsPerGbp", currencyUnitsPerGbp);
|
||||
cmd.Parameters.AddWithValue("@periodStart", periodStartUtc);
|
||||
cmd.Parameters.AddWithValue("@periodEnd", periodEnd);
|
||||
|
||||
recordId = (int)cmd.ExecuteScalar();
|
||||
|
||||
if (recordId < 1)
|
||||
{
|
||||
throw new Exception("Error inserting record, did not retrive new record ID.");
|
||||
}
|
||||
}
|
||||
|
||||
return recordId;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
using bnhtrade.Core.Data.Database.Repository.Interface;
|
||||
using bnhtrade.Core.Logic.Validate;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Repository.Implementation
|
||||
{
|
||||
internal class ExportInvoiceRepository : _Base, IExportInvoiceRepository
|
||||
{
|
||||
public ExportInvoiceRepository(IDbConnection connection, IDbTransaction transaction) : base(connection, transaction)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// invoice Insert methods
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Don't call this directly, use the logic layer instead (for validation and other checks).
|
||||
/// </summary>
|
||||
/// <param name="invoiceList">list of sales invoices to insert</param>
|
||||
/// <returns>dectionary, key= invoice id, value= new invoice </returns>
|
||||
public Dictionary<int, Model.Account.SalesInvoice> InsertSalesInvoices(IEnumerable<Model.Account.SalesInvoice> invoiceList)
|
||||
{
|
||||
var result = InsertBaseExecute(invoiceList);
|
||||
var returnList = new Dictionary<int, Model.Account.SalesInvoice>();
|
||||
foreach (var item in result)
|
||||
{
|
||||
returnList.Add(item.Key, (Model.Account.SalesInvoice)item.Value);
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
||||
private Dictionary<int, Model.Account.Invoice> InsertBaseExecute(IEnumerable<Model.Account.Invoice> invoiceList)
|
||||
{
|
||||
if (invoiceList == null)
|
||||
{
|
||||
throw new ArgumentException("Invoice list is null");
|
||||
}
|
||||
|
||||
var returnList = new Dictionary<int, Model.Account.Invoice>();
|
||||
int invoiceCount = invoiceList.Count();
|
||||
|
||||
// make the inserts
|
||||
foreach (var invoice in invoiceList)
|
||||
{
|
||||
int? invoiceId = null;
|
||||
string sql = @"
|
||||
INSERT INTO tblExportAccountInvoice (
|
||||
ExportAccountInvoiceTypeID
|
||||
,Contact
|
||||
,InvoiceDate
|
||||
,InvoiceDueDate
|
||||
,InvoiceNumber
|
||||
,Reference
|
||||
,CurrencyCode
|
||||
,LineUnitAmountIsTaxExclusive
|
||||
,InvoiceAmount
|
||||
,IsComplete
|
||||
)
|
||||
OUTPUT INSERTED.ExportAccountInvoiceID
|
||||
VALUES (
|
||||
@invoiceTypeId
|
||||
,@contact
|
||||
,@invoiceDate
|
||||
,@invoiceDueDate
|
||||
,@invoiceNumber
|
||||
,@reference
|
||||
,@currencyCode
|
||||
,@lineUnitAmountIsTaxExclusive
|
||||
,@invoiceAmount
|
||||
,@markComplete
|
||||
);";
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
cmd.Parameters.AddWithValue("@invoiceTypeId", (int)invoice.InvoiceType);
|
||||
cmd.Parameters.AddWithValue("@contact", invoice.ContactName);
|
||||
cmd.Parameters.AddWithValue("@invoiceDate", invoice.InvoiceDate);
|
||||
cmd.Parameters.AddWithValue("@invoiceDueDate", invoice.InvoiceDueDate);
|
||||
cmd.Parameters.AddWithValue("@reference", invoice.InvoiceReference);
|
||||
cmd.Parameters.AddWithValue("@currencyCode", invoice.InvoiceCurrencyCode.ToString());
|
||||
cmd.Parameters.AddWithValue("@lineUnitAmountIsTaxExclusive", invoice.InvoiceLineUnitAmountIsTaxExclusive);
|
||||
cmd.Parameters.AddWithValue("@markComplete", false);
|
||||
cmd.Parameters.AddWithValue("@invoiceNumber", invoice.InvoiceNumber);
|
||||
cmd.Parameters.AddWithValue("@invoiceAmount", invoice.InvoiceTotalAmount);
|
||||
|
||||
invoiceId = (int)cmd.ExecuteScalar();
|
||||
}
|
||||
|
||||
foreach (var line in invoice.InvoiceLineList)
|
||||
{
|
||||
string lineSql = @"
|
||||
INSERT INTO tblExportAccountInvoiceLine (
|
||||
ExportAccountInvoiceID
|
||||
,AccountInvoiceLineItemID
|
||||
,NetAmount
|
||||
,AccountChartOfID
|
||||
,TaxAmount
|
||||
,AccountTaxCodeID
|
||||
)
|
||||
OUTPUT INSERTED.ExportAccountInvoiceLineID
|
||||
VALUES (
|
||||
@invoiceId
|
||||
,(
|
||||
SELECT AccountInvoiceLineItemID
|
||||
FROM tblAccountInvoiceLineItem
|
||||
WHERE ItemCode = @itemCode
|
||||
)
|
||||
,@netAmount
|
||||
,(
|
||||
SELECT AccountChartOfID
|
||||
FROM tblAccountChartOf
|
||||
WHERE AccountCode = @accountCode
|
||||
)
|
||||
,@taxAmount
|
||||
,(
|
||||
SELECT AccountTaxCodeID
|
||||
FROM tblAccountTaxCode
|
||||
WHERE TaxCode = @taxCode
|
||||
)
|
||||
);";
|
||||
|
||||
// insert record
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand) // Use _connection.CreateCommand()
|
||||
{
|
||||
cmd.CommandText = lineSql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
cmd.Parameters.AddWithValue("@invoiceID", invoiceId);
|
||||
cmd.Parameters.AddWithValue("@itemCode", line.ItemCode);
|
||||
cmd.Parameters.AddWithValue("@netAmount", line.UnitAmount);
|
||||
cmd.Parameters.AddWithValue("@accountCode", (int)line.Account.AccountCode);
|
||||
cmd.Parameters.AddWithValue("@taxAmount", line.TaxAmountTotal);
|
||||
cmd.Parameters.AddWithValue("@taxCode", line.TaxCode.TaxCode);
|
||||
|
||||
int lineId = (int)cmd.ExecuteScalar();
|
||||
}
|
||||
}
|
||||
returnList.Add(invoiceId.Value, invoice);
|
||||
}
|
||||
|
||||
if (returnList.Count != invoiceCount)
|
||||
{
|
||||
throw new Exception("Not all invoices were inserted into the export table. Expected: " + invoiceCount + ", Actual: " + returnList.Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
return returnList;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// invoice read methods
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Returns list of invoice numbers that have not yet been exported (i.e. IsComplete=True)
|
||||
/// </summary>
|
||||
/// <param name="invoiceType">Sales or Purchase</param>
|
||||
/// <returns>Dictionary where key=ExportAccountInvoiceID, value=InvoiceNumber</returns>
|
||||
public Dictionary<int, string> GetNewInvoiceNumbers(Model.Account.InvoiceType invoiceType)
|
||||
{
|
||||
var returnList = new Dictionary<int, string>();
|
||||
|
||||
string sql = @"
|
||||
SELECT tblExportAccountInvoice.ExportAccountInvoiceID, [InvoiceNumber]
|
||||
FROM tblExportAccountInvoice
|
||||
WHERE (tblExportAccountInvoice.IsComplete = 0)
|
||||
AND (tblExportAccountInvoice.ExportAccountInvoiceTypeID = @invoiceType);";
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
cmd.Parameters.AddWithValue("@invoiceType", (int)invoiceType);
|
||||
|
||||
using (SqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
returnList.Add(reader.GetInt32(0), reader.GetString(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get list of invoices by invoice id
|
||||
/// </summary>
|
||||
/// <param name="idList">list of invoice ids</param>
|
||||
/// <returns>dictionary key=id, value=invoice-model</returns>
|
||||
public Dictionary<int, Model.Account.SalesInvoice> GetSalesInvoiceById(IEnumerable<int> idList)
|
||||
{
|
||||
var returnList = new Dictionary<int, Model.Account.SalesInvoice>();
|
||||
|
||||
if (idList.Any() == false)
|
||||
{
|
||||
return returnList;
|
||||
}
|
||||
|
||||
// get the account and tax code objects
|
||||
var taxcode = new Data.Database.Account.ReadTaxCode().GetAllActive();
|
||||
var account = new Data.Database.Account.ReadAccountCode().All();
|
||||
|
||||
// build sql string
|
||||
string sql = @"
|
||||
SELECT tblExportAccountInvoice.ExportAccountInvoiceID
|
||||
, tblExportAccountInvoice.ExportAccountInvoiceTypeID
|
||||
, tblExportAccountInvoice.Contact
|
||||
, tblExportAccountInvoice.InvoiceNumber
|
||||
, tblExportAccountInvoice.InvoiceDate
|
||||
, tblExportAccountInvoice.InvoiceDueDate
|
||||
, tblExportAccountInvoice.Reference
|
||||
, tblExportAccountInvoice.CurrencyCode
|
||||
, tblExportAccountInvoice.InvoiceAmount
|
||||
, tblExportAccountInvoice.IsComplete
|
||||
, tblExportAccountInvoiceLine.ExportAccountInvoiceLineID
|
||||
, tblAccountInvoiceLineItem.ItemName
|
||||
, tblExportAccountInvoiceLine.AccountChartOfID
|
||||
, tblExportAccountInvoiceLine.NetAmount
|
||||
, tblExportAccountInvoiceLine.AccountTaxCodeID
|
||||
, tblExportAccountInvoiceLine.TaxAmount
|
||||
, tblExportAccountInvoice.LineUnitAmountIsTaxExclusive
|
||||
FROM tblExportAccountInvoice
|
||||
INNER JOIN
|
||||
tblExportAccountInvoiceLine
|
||||
ON tblExportAccountInvoice.ExportAccountInvoiceID = tblExportAccountInvoiceLine.ExportAccountInvoiceID
|
||||
INNER JOIN
|
||||
tblAccountInvoiceLineItem
|
||||
ON tblExportAccountInvoiceLine.AccountInvoiceLineItemID = tblAccountInvoiceLineItem.AccountInvoiceLineItemID
|
||||
";
|
||||
|
||||
var sqlWhere = new Data.Database.SqlWhereBuilder();
|
||||
sqlWhere.In("tblExportAccountInvoice.ExportAccountInvoiceID", idList, "WHERE");
|
||||
|
||||
sql = sql + sqlWhere.SqlWhereString + " ORDER BY tblExportAccountInvoice.ExportAccountInvoiceID, tblExportAccountInvoiceLine.ExportAccountInvoiceLineID; ";
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
sqlWhere.AddParametersToSqlCommand(cmd);
|
||||
|
||||
using (SqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.HasRows == false)
|
||||
{
|
||||
return returnList;
|
||||
}
|
||||
|
||||
int? invoiceId = null;
|
||||
int? previousInvoiceId = null;
|
||||
Model.Account.SalesInvoice invoice = null;
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
invoiceId = reader.GetInt32(0);
|
||||
|
||||
// test for first/next invoice
|
||||
if (invoice == null || previousInvoiceId.Value != invoiceId.Value)
|
||||
{
|
||||
// if next invoice, add previous to return list
|
||||
if (previousInvoiceId.HasValue)
|
||||
{
|
||||
// invoice complete, add to return list
|
||||
returnList.Add(previousInvoiceId.Value, invoice);
|
||||
}
|
||||
|
||||
// add header info to new invoice
|
||||
invoice = new Model.Account.SalesInvoice(reader.GetBoolean(16));
|
||||
int invoiceType = reader.GetInt32(1);
|
||||
invoice.ContactName = reader.GetString(2);
|
||||
invoice.InvoiceNumber = reader.GetString(3);
|
||||
invoice.InvoiceDate = reader.GetDateTime(4);
|
||||
invoice.InvoiceDueDate = reader.GetDateTime(5);
|
||||
if (!reader.IsDBNull(6)) { invoice.InvoiceReference = reader.GetString(6); }
|
||||
invoice.InvoiceCurrencyCode = Enum.Parse<Model.Account.CurrencyCode>(reader.GetString(7));
|
||||
if (!reader.IsDBNull(8)) { invoice.InvoiceTotalAmount = reader.GetDecimal(8); }
|
||||
bool isComplete = reader.GetBoolean(9);
|
||||
|
||||
// set credit note
|
||||
if (invoice.InvoiceTotalAmount < 0)
|
||||
{
|
||||
invoice.IsCreditNote = true;
|
||||
}
|
||||
}
|
||||
|
||||
// add line info
|
||||
var invoiceLine = new Model.Account.SalesInvoice.InvoiceLine(invoice);
|
||||
int lineId = reader.GetInt32(10);
|
||||
invoiceLine.ItemCode = reader.GetString(11);
|
||||
invoiceLine.Account = account[Convert.ToUInt32(reader.GetInt32(12))];
|
||||
invoiceLine.UnitAmount = reader.GetDecimal(13);
|
||||
invoiceLine.Quantity = 1;
|
||||
invoiceLine.TaxCode = taxcode[reader.GetByte(14)];
|
||||
invoiceLine.SetTaxAdjustmentByTaxTotal(reader.GetDecimal(15));
|
||||
|
||||
invoice.InvoiceLineList.Add(invoiceLine);
|
||||
previousInvoiceId = invoiceId.Value;
|
||||
}
|
||||
|
||||
// complete final invoice and add to list
|
||||
returnList.Add(invoiceId.Value, invoice);
|
||||
}
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
||||
//
|
||||
// invoice update methods
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Update the 'IsComplete' field by invoice id
|
||||
/// </summary>
|
||||
/// <param name="updateDictionary">key=ExportAccountInvoiceTypeID, value=IsComplete</param>
|
||||
/// <returns>Number of row effected</returns>
|
||||
public int SetInvoiceIsCompleteValue(Dictionary<int, bool> updateDictionary)
|
||||
{
|
||||
int returnCount = 0;
|
||||
|
||||
foreach (var item in updateDictionary)
|
||||
{
|
||||
string sql = @"
|
||||
UPDATE
|
||||
tblExportAccountInvoice
|
||||
SET
|
||||
IsComplete = @isComplete
|
||||
WHERE
|
||||
ExportAccountInvoiceID = @id
|
||||
";
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
cmd.Parameters.AddWithValue("@id", item.Key);
|
||||
cmd.Parameters.AddWithValue("@isComplete", item.Value);
|
||||
|
||||
returnCount = returnCount + cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
return returnCount;
|
||||
}
|
||||
|
||||
//
|
||||
// Invoice Delete Methods
|
||||
//
|
||||
|
||||
public int DeleteInvoiceLine(int invoiceLineId)
|
||||
{
|
||||
// only being able to delete one line at a time is a design/safety decision
|
||||
// otherwise, delete whole invoice
|
||||
if (invoiceLineId <= 0)
|
||||
{
|
||||
throw new ArgumentException("InvoiceLineId must be greater than zero.");
|
||||
}
|
||||
Core.Data.Database.SqlWhereBuilder sqlWhereBuilder = new Core.Data.Database.SqlWhereBuilder();
|
||||
sqlWhereBuilder.In("ExportAccountInvoiceLineID", new List<int> { invoiceLineId });
|
||||
return DeleteInvoiceExecuteInvoiceLine(sqlWhereBuilder);
|
||||
}
|
||||
|
||||
public int DeleteInvoice(IEnumerable<int> invoiceIdList)
|
||||
{
|
||||
if (invoiceIdList == null || invoiceIdList.Count() == 0)
|
||||
{
|
||||
throw new ArgumentException("InvoiceIdList is empty, nothing to delete.");
|
||||
}
|
||||
|
||||
int invoiceToDelete = invoiceIdList.Distinct().Count();
|
||||
int invoiceEffected = 0;
|
||||
|
||||
// delete lines first
|
||||
Core.Data.Database.SqlWhereBuilder sqlWhereBuilder = new Core.Data.Database.SqlWhereBuilder();
|
||||
sqlWhereBuilder.In("ExportAccountInvoiceID", invoiceIdList);
|
||||
int lineCount = DeleteInvoiceExecuteInvoiceLine(sqlWhereBuilder);
|
||||
|
||||
// then delete invoice
|
||||
sqlWhereBuilder = new Core.Data.Database.SqlWhereBuilder();
|
||||
sqlWhereBuilder.In("ExportAccountInvoiceID", invoiceIdList);
|
||||
invoiceEffected = DeleteInvoiceExecuteInvoice(sqlWhereBuilder);
|
||||
|
||||
if (invoiceEffected == invoiceToDelete)
|
||||
{
|
||||
return invoiceEffected;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Error: "
|
||||
+ invoiceToDelete + " number invoices requested for deletion, "
|
||||
+ invoiceEffected + " number invoices effected. Changes rolled back.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method Only intended for use with ExecuteInvoice() or ExecuteInvoiceLine()
|
||||
/// </summary>
|
||||
/// <param name="sql">the full sql statement</param>
|
||||
/// <param name="sqlWhereBuilder">instance of the sql where builder helper</param>
|
||||
/// <returns>Number of row effected (deleted)</returns>
|
||||
private int DeleteInvoiceExecuteHelper(string sql, Core.Data.Database.SqlWhereBuilder sqlWhereBuilder)
|
||||
{
|
||||
// Only intended for ExecuteInvoice() or ExecuteInvoiceLine()
|
||||
|
||||
// important checks
|
||||
if (sqlWhereBuilder.IsSetSqlWhereString == false)
|
||||
{
|
||||
throw new ArgumentException("SqlWhereBuilder is not set, set the SqlWhereBuilder before calling this method.");
|
||||
}
|
||||
if (sqlWhereBuilder.ParameterListCount == 0)
|
||||
{
|
||||
throw new Exception("Parameter list count is zero, this could delete all records in table, operation cancelled");
|
||||
}
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction; // Assign the shared transaction
|
||||
sqlWhereBuilder.AddParametersToSqlCommand(cmd);
|
||||
|
||||
return cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
private int DeleteInvoiceExecuteInvoice(Core.Data.Database.SqlWhereBuilder sqlWhereBuilder)
|
||||
{
|
||||
string sql = "DELETE FROM tblExportAccountInvoice WHERE " + sqlWhereBuilder.SqlWhereString;
|
||||
return DeleteInvoiceExecuteHelper(sql, sqlWhereBuilder);
|
||||
}
|
||||
|
||||
private int DeleteInvoiceExecuteInvoiceLine(Core.Data.Database.SqlWhereBuilder sqlWhereBuilder)
|
||||
{
|
||||
string sql = "DELETE FROM tblExportAccountInvoiceLine WHERE " + sqlWhereBuilder.SqlWhereString;
|
||||
return DeleteInvoiceExecuteHelper(sql, sqlWhereBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,713 @@
|
||||
using bnhtrade.Core.Data.Database._BoilerPlate;
|
||||
using bnhtrade.Core.Data.Database.Repository.Interface;
|
||||
using bnhtrade.Core.Model.Amazon;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.VisualBasic.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Repository.Implementation
|
||||
{
|
||||
internal class ImportAmazonRepository : _Base, IImportAmazonRepository
|
||||
{
|
||||
Logic.Log.LogEvent _log = new Logic.Log.LogEvent();
|
||||
|
||||
public ImportAmazonRepository(IDbConnection connection, IDbTransaction transaction) : base(connection, transaction)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// Read Amazon Settlement Header Information
|
||||
//
|
||||
|
||||
public List<Model.Import.AmazonSettlementHeader> ReadAmazonSettlementHeaderInfoBySettlementId(List<string> settlementIdList)
|
||||
{
|
||||
if (settlementIdList == null || !settlementIdList.Any())
|
||||
{ return new List<Model.Import.AmazonSettlementHeader>(); }
|
||||
|
||||
return ReadAmazonSettlementHeaders(settlementIdList);
|
||||
}
|
||||
|
||||
public List<Model.Import.AmazonSettlementHeader> ReadAmazonSettlementHeaderInfoBySpapiReportId(List<string> spapiReportIdList)
|
||||
{
|
||||
if (spapiReportIdList == null || !spapiReportIdList.Any())
|
||||
{ return new List<Model.Import.AmazonSettlementHeader>(); }
|
||||
|
||||
return ReadAmazonSettlementHeaders(null, spapiReportIdList);
|
||||
}
|
||||
|
||||
private List<Model.Import.AmazonSettlementHeader> ReadAmazonSettlementHeaders(
|
||||
List<string> settlementIdList = null, List<string> spapiReportIdList = null, bool filterOutIsProcessed = false,
|
||||
bool DescendingOrder = false, int? returnTop = null)
|
||||
{
|
||||
var returnHeaderList = new List<Model.Import.AmazonSettlementHeader>();
|
||||
Dictionary<string, int> dicTablePkBySettlementId = new Dictionary<string, int>();
|
||||
|
||||
|
||||
// build the sql string
|
||||
string sqlString = "SELECT ";
|
||||
|
||||
if (returnTop != null)
|
||||
{
|
||||
sqlString = sqlString + "TOP " + returnTop.Value + " ";
|
||||
}
|
||||
|
||||
sqlString = sqlString + @"
|
||||
ImportAmazonSettlementReportID
|
||||
,[marketplace-name]
|
||||
,[settlement-id]
|
||||
,[settlement-start-date]
|
||||
,[settlement-end-date]
|
||||
,[deposit-date]
|
||||
,[total-amount]
|
||||
,currency
|
||||
,IsProcessed
|
||||
,SpapiReportId
|
||||
FROM tblImportAmazonSettlementReport
|
||||
WHERE 1 = 1";
|
||||
|
||||
if (filterOutIsProcessed)
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
AND IsProcessed = 0";
|
||||
}
|
||||
|
||||
// build dictionary of parameter and values for settlementid
|
||||
var dicSettlementIdByParameterString = new Dictionary<string, string>();
|
||||
if (settlementIdList != null)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (string item in settlementIdList)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(item))
|
||||
{
|
||||
count = count + 1;
|
||||
string parameterString = "@settlementId" + count;
|
||||
dicSettlementIdByParameterString.Add(parameterString, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dicSettlementIdByParameterString.Any())
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var item in dicSettlementIdByParameterString)
|
||||
{
|
||||
count = count + 1;
|
||||
if (count == 1)
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
AND ( [settlement-id] = " + item.Key;
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
OR [settlement-id] = " + item.Key;
|
||||
}
|
||||
}
|
||||
sqlString = sqlString + " )";
|
||||
}
|
||||
|
||||
// build dictionary of parameter and values for SP-API Report Id
|
||||
var dicSpapiReportIdByParameterString = new Dictionary<string, string>();
|
||||
if (spapiReportIdList != null && spapiReportIdList.Any())
|
||||
{
|
||||
int count = 0;
|
||||
foreach (string item in spapiReportIdList)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(item))
|
||||
{
|
||||
count = count + 1;
|
||||
string parameterString = "@SpapiReportId" + count;
|
||||
dicSpapiReportIdByParameterString.Add(parameterString, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dicSpapiReportIdByParameterString.Any())
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var item in dicSpapiReportIdByParameterString)
|
||||
{
|
||||
count = count + 1;
|
||||
if (count == 1)
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
AND ( SpapiReportId = " + item.Key;
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
OR SpapiReportId = " + item.Key;
|
||||
}
|
||||
}
|
||||
sqlString = sqlString + " )";
|
||||
}
|
||||
|
||||
|
||||
sqlString = sqlString + @"
|
||||
ORDER BY [settlement-start-date] ";
|
||||
if (DescendingOrder) { sqlString = sqlString + " DESC"; }
|
||||
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sqlString;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
if (dicSettlementIdByParameterString.Any())
|
||||
{
|
||||
foreach (var item in dicSettlementIdByParameterString)
|
||||
{
|
||||
cmd.Parameters.AddWithValue(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (dicSpapiReportIdByParameterString.Any())
|
||||
{
|
||||
foreach (var item in dicSpapiReportIdByParameterString)
|
||||
{
|
||||
cmd.Parameters.AddWithValue(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var header = new Model.Import.AmazonSettlementHeader();
|
||||
|
||||
int tablePk = reader.GetInt32(0);
|
||||
if (!reader.IsDBNull(1)) { header.MarketPlace = MarketPlaceEnumExtensions.FromMarketplaceUrl(reader.GetString(1)); }
|
||||
header.SettlementId = reader.GetString(2);
|
||||
header.StartDate = DateTime.SpecifyKind(reader.GetDateTime(3), DateTimeKind.Utc);
|
||||
header.EndDate = DateTime.SpecifyKind(reader.GetDateTime(4), DateTimeKind.Utc);
|
||||
header.DepositDate = DateTime.SpecifyKind(reader.GetDateTime(5), DateTimeKind.Utc);
|
||||
header.TotalAmount = reader.GetDecimal(6);
|
||||
header.CurrencyCode = reader.GetString(7);
|
||||
header.IsProcessed = reader.GetBoolean(8);
|
||||
if (!reader.IsDBNull(9)) { header.SpapiReportId = reader.GetString(9); }
|
||||
|
||||
// update dictionary
|
||||
if (!dicTablePkBySettlementId.ContainsKey(header.SettlementId))
|
||||
{
|
||||
dicTablePkBySettlementId.Add(header.SettlementId, tablePk);
|
||||
}
|
||||
|
||||
// add header to list
|
||||
returnHeaderList.Add(header);
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnHeaderList;
|
||||
}
|
||||
|
||||
//
|
||||
// Read Amazon Settlement Report
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Gets Amazon settlement report information from the database.
|
||||
/// </summary>
|
||||
/// <returns>Dictionary where key=id and value=settlement</returns>
|
||||
public Dictionary<int, Model.Import.AmazonSettlement> ReadAmazonSettlements(
|
||||
List<string> settlementIdList = null, List<string> marketPlaceNameList = null, bool? isProcessed = null,
|
||||
bool descendingOrder = false, int? returnTop = null )
|
||||
{
|
||||
var returnList = new Dictionary<int, Model.Import.AmazonSettlement>();
|
||||
var whereBuilder = new Data.Database.SqlWhereBuilder();
|
||||
|
||||
// build the sql string
|
||||
string sqlString = "SELECT ";
|
||||
|
||||
if (returnTop != null)
|
||||
{
|
||||
sqlString = sqlString + "TOP " + returnTop.Value + " ";
|
||||
}
|
||||
|
||||
sqlString = sqlString + @"
|
||||
tblImportAmazonSettlementReport.ImportAmazonSettlementReportID,
|
||||
tblImportAmazonSettlementReport.[marketplace-name],
|
||||
tblImportAmazonSettlementReport.[settlement-id],
|
||||
tblImportAmazonSettlementReport.[settlement-start-date],
|
||||
tblImportAmazonSettlementReport.[settlement-end-date],
|
||||
tblImportAmazonSettlementReport.[deposit-date],
|
||||
tblImportAmazonSettlementReport.[total-amount],
|
||||
tblImportAmazonSettlementReport.currency,
|
||||
tblImportAmazonSettlementReport.IsProcessed,
|
||||
tblImportAmazonSettlementReport.SpapiReportId,
|
||||
tblImportAmazonSettlementReportLine.ImportAmazonSettlementReportLineID,
|
||||
tblImportAmazonSettlementReportLine.[transaction-type],
|
||||
tblImportAmazonSettlementReportLine.[order-id],
|
||||
tblImportAmazonSettlementReportLine.[merchant-order-id],
|
||||
tblImportAmazonSettlementReportLine.[adjustment-id],
|
||||
tblImportAmazonSettlementReportLine.[shipment-id],
|
||||
tblImportAmazonSettlementReportLine.[marketplace-name] AS Expr1,
|
||||
tblImportAmazonSettlementReportLine.[amount-type],
|
||||
tblImportAmazonSettlementReportLine.[amount-description],
|
||||
tblImportAmazonSettlementReportLine.amount,
|
||||
tblImportAmazonSettlementReportLine.currency,
|
||||
tblImportAmazonSettlementReportLine.[fulfillment-id],
|
||||
tblImportAmazonSettlementReportLine.[posted-date-time],
|
||||
tblImportAmazonSettlementReportLine.[order-item-code],
|
||||
tblImportAmazonSettlementReportLine.[merchant-order-item-id],
|
||||
tblImportAmazonSettlementReportLine.[merchant-adjustment-item-id],
|
||||
tblImportAmazonSettlementReportLine.sku,
|
||||
tblImportAmazonSettlementReportLine.[quantity-purchased],
|
||||
tblImportAmazonSettlementReportLine.[promotion-id],
|
||||
tblImportAmazonSettlementReportLine.IsProcessed,
|
||||
tblImportAmazonSettlementReportLine.ExportAccountInvoiceLineID
|
||||
FROM tblImportAmazonSettlementReport
|
||||
INNER JOIN
|
||||
tblImportAmazonSettlementReportLine
|
||||
ON tblImportAmazonSettlementReport.ImportAmazonSettlementReportID = tblImportAmazonSettlementReportLine.ImportAmazonSettlementReportID
|
||||
WHERE 1 = 1 ";
|
||||
|
||||
if (isProcessed != null)
|
||||
{
|
||||
if (isProcessed.Value == true)
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
AND tblImportAmazonSettlementReport.IsProcessed = 1";
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlString = sqlString + @"
|
||||
AND tblImportAmazonSettlementReport.IsProcessed = 0";
|
||||
}
|
||||
}
|
||||
|
||||
if (settlementIdList != null && settlementIdList.Any() == true)
|
||||
{
|
||||
whereBuilder.In("tblImportAmazonSettlementReport.ImportAmazonSettlementReportID", settlementIdList, "AND");
|
||||
}
|
||||
|
||||
if (marketPlaceNameList != null && marketPlaceNameList.Any() == true)
|
||||
{
|
||||
whereBuilder.In("tblImportAmazonSettlementReport.[marketplace-name]", marketPlaceNameList, "AND");
|
||||
}
|
||||
|
||||
sqlString = sqlString + whereBuilder.SqlWhereString;
|
||||
|
||||
// add the order by clause(s)
|
||||
sqlString = sqlString + @"
|
||||
ORDER BY tblImportAmazonSettlementReport.[marketplace-name] ASC, tblImportAmazonSettlementReport.[settlement-start-date] ";
|
||||
if (descendingOrder) { sqlString = sqlString + " DESC"; }
|
||||
else { sqlString = sqlString + " ASC"; }
|
||||
sqlString = sqlString + ", tblImportAmazonSettlementReport.ImportAmazonSettlementReportID ASC, tblImportAmazonSettlementReportLine.[posted-date-time] ASC, "
|
||||
+ "tblImportAmazonSettlementReportLine.ImportAmazonSettlementReportLineID ASC;";
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sqlString;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
whereBuilder.AddParametersToSqlCommand(cmd);
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
int tableId = reader.GetInt32(0);
|
||||
|
||||
if (returnList.ContainsKey(tableId) == false)
|
||||
{
|
||||
var settlement = new Model.Import.AmazonSettlement();
|
||||
|
||||
if (!reader.IsDBNull(1)) { settlement.MarketPlace = MarketPlaceEnumExtensions.FromMarketplaceUrl(reader.GetString(1)); }
|
||||
settlement.SettlementId = reader.GetString(2);
|
||||
settlement.StartDate = DateTime.SpecifyKind(reader.GetDateTime(3), DateTimeKind.Utc);
|
||||
settlement.EndDate = DateTime.SpecifyKind(reader.GetDateTime(4), DateTimeKind.Utc);
|
||||
settlement.DepositDate = DateTime.SpecifyKind(reader.GetDateTime(5), DateTimeKind.Utc);
|
||||
settlement.TotalAmount = reader.GetDecimal(6);
|
||||
settlement.CurrencyCode = reader.GetString(7);
|
||||
settlement.IsProcessed = reader.GetBoolean(8);
|
||||
if (!reader.IsDBNull(9)) { settlement.SpapiReportId = reader.GetString(9); }
|
||||
|
||||
returnList.Add(tableId, settlement);
|
||||
}
|
||||
|
||||
// add lines to settlement
|
||||
var line = new Model.Import.AmazonSettlement.SettlementLine();
|
||||
|
||||
line.TransactionType = reader.GetString(11);
|
||||
if (!reader.IsDBNull(12)) { line.OrderId = reader.GetString(12); }
|
||||
if (!reader.IsDBNull(13)) { line.MerchantOrderId = reader.GetString(13); }
|
||||
if (!reader.IsDBNull(14)) { line.AdjustmentId = reader.GetString(14); }
|
||||
if (!reader.IsDBNull(15)) { line.ShipmentId = reader.GetString(15); }
|
||||
if (!reader.IsDBNull(16)) { line.MarketPlaceName = reader.GetString(16); }
|
||||
line.AmountType = reader.GetString(17);
|
||||
line.AmountDescription = reader.GetString(18);
|
||||
line.Amount = reader.GetDecimal(19);
|
||||
line.CurrenyCode = reader.GetString(20);
|
||||
if (!reader.IsDBNull(21)) { line.FulfillmentId = reader.GetString(21); }
|
||||
line.PostDateTime = DateTime.SpecifyKind(reader.GetDateTime(22), DateTimeKind.Utc);
|
||||
if (!reader.IsDBNull(23)) { line.OrderItemCode = reader.GetString(23); }
|
||||
if (!reader.IsDBNull(24)) { line.MerchantOrderItemId = reader.GetString(24); }
|
||||
if (!reader.IsDBNull(25)) { line.MerchantAdjustmentItemId = reader.GetString(25); }
|
||||
if (!reader.IsDBNull(26)) { line.Sku = reader.GetString(26); }
|
||||
if (!reader.IsDBNull(27)) { line.QuantityPurchased = reader.GetInt32(27); }
|
||||
if (!reader.IsDBNull(28)) { line.PromotionId = reader.GetString(28); }
|
||||
line.IsProcessed = reader.GetBoolean(29);
|
||||
if (!reader.IsDBNull(30)) { line.ExportAccountInvoiceLineId = reader.GetInt32(30); }
|
||||
|
||||
returnList[tableId].SettlementLineList.Add(line);
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Update Amazon Settlement Report
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Set the IsProcessed flag to true or false for the specified settlement Ids.
|
||||
/// </summary>
|
||||
/// <param name="settlementIdList">List of settlement id (not table id)</param>
|
||||
/// <param name="isProcessed">value to set the isProcessed to</param>
|
||||
/// <returns>Number of rows effected</returns>
|
||||
public int SetAmazonSettlementIsProcessed(List<string> settlementIdList, bool isProcessed)
|
||||
{
|
||||
if (settlementIdList == null || !settlementIdList.Any())
|
||||
{
|
||||
throw new Exception("Settlement ID list is empty.");
|
||||
}
|
||||
|
||||
string sqlString = @"
|
||||
UPDATE tblImportAmazonSettlementReport ";
|
||||
|
||||
sqlString = sqlString + @"
|
||||
SET IsProcessed = " + (isProcessed ? "1" : "0");
|
||||
|
||||
var whereBuilder = new Data.Database.SqlWhereBuilder();
|
||||
whereBuilder.In("tblImportAmazonSettlementReport.[settlement-id]", settlementIdList, "WHERE");
|
||||
|
||||
sqlString = sqlString + whereBuilder.SqlWhereString;
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sqlString;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
whereBuilder.AddParametersToSqlCommand(cmd);
|
||||
|
||||
return cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a Settlement Report flat file from Amazon and imports it into the database.
|
||||
/// </summary>
|
||||
/// <param name="filePath">path to the Amazon report flat file</param>
|
||||
/// <param name="reportId">The unique Amazon SP-API report id (not settlement id)</param>
|
||||
/// <returns></returns>
|
||||
public bool CreateAmazonSettlements(string filePath, string reportId)
|
||||
{
|
||||
int settlementReportId = 0;
|
||||
string settlementRef = "";
|
||||
bool marketPlaceUpdated = false;
|
||||
decimal settlementAmount = 0m;
|
||||
int lineNumber = 2;
|
||||
int lineSkip = 0;
|
||||
|
||||
using (var reader = new StreamReader(filePath))
|
||||
{
|
||||
//read file one line at a time and insert data into table if required
|
||||
|
||||
// read header and retrive information
|
||||
string headerRow = reader.ReadLine();
|
||||
string[] headers = headerRow.Split('\t');
|
||||
|
||||
int columnCount = headers.Length;
|
||||
|
||||
int indexSettlementId = Array.IndexOf(headers, "settlement-id");
|
||||
int indexSettlementStartDate = Array.IndexOf(headers, "settlement-start-date");
|
||||
int indexSettlementEndDate = Array.IndexOf(headers, "settlement-end-date");
|
||||
int indexDepositDate = Array.IndexOf(headers, "deposit-date");
|
||||
int indexTotalAmount = Array.IndexOf(headers, "total-amount");
|
||||
int indexCurrency = Array.IndexOf(headers, "currency");
|
||||
int indexTransactionType = Array.IndexOf(headers, "transaction-type");
|
||||
int indexOrderId = Array.IndexOf(headers, "order-id");
|
||||
int indexMerchantOrderId = Array.IndexOf(headers, "merchant-order-id");
|
||||
int indexAdjustmentId = Array.IndexOf(headers, "adjustment-id");
|
||||
int indexShipmentId = Array.IndexOf(headers, "shipment-id");
|
||||
int indexMarketplaceName = Array.IndexOf(headers, "marketplace-name");
|
||||
int indexAmountType = Array.IndexOf(headers, "amount-type");
|
||||
int indexAmountDescription = Array.IndexOf(headers, "amount-description");
|
||||
int indexAmount = Array.IndexOf(headers, "amount");
|
||||
int indexFulfillmentId = Array.IndexOf(headers, "fulfillment-id");
|
||||
// int indexPostedDate = Array.IndexOf(headers, "posted-date");
|
||||
int indexPostedDateTime = Array.IndexOf(headers, "posted-date-time");
|
||||
int indexOrderItemCode = Array.IndexOf(headers, "order-item-code");
|
||||
int indexMerchantOrderItemId = Array.IndexOf(headers, "merchant-order-item-id");
|
||||
int indexMerchantAdjustmentItemId = Array.IndexOf(headers, "merchant-adjustment-item-id");
|
||||
int indexSku = Array.IndexOf(headers, "sku");
|
||||
int indexQuantityPurchased = Array.IndexOf(headers, "quantity-purchased");
|
||||
int indexPromotionId = Array.IndexOf(headers, "promotion-id");
|
||||
|
||||
string currency = "";
|
||||
|
||||
string fileRow;
|
||||
while ((fileRow = reader.ReadLine()) != null)
|
||||
{
|
||||
Console.Write("\rParsing record: " + lineNumber);
|
||||
//split line into array
|
||||
string[] items = fileRow.Split('\t');
|
||||
if (items.Length != columnCount)
|
||||
{
|
||||
// skip line
|
||||
lineSkip = lineSkip + 1;
|
||||
_log.LogWarning(
|
||||
"Line #" + lineNumber + " skipped due to no enough element in row.",
|
||||
filePath
|
||||
);
|
||||
}
|
||||
else if (lineNumber == 2)
|
||||
{
|
||||
// check if settlement has already been imported
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = "SELECT COUNT(*) FROM tblImportAmazonSettlementReport WHERE [settlement-id]=@settlementId;";
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
cmd.Parameters.AddWithValue("@settlementId", items[indexSettlementId]);
|
||||
|
||||
int recordCount = (int)cmd.ExecuteScalar();
|
||||
if (recordCount > 0)
|
||||
{
|
||||
SetSpapiReportId(items[indexSettlementId], reportId);
|
||||
_log.LogInformation("Settlement report already imported, skipping...");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//set currencyId
|
||||
//currencyId = GeneralQueries.GetCurrencyId(items[5]);
|
||||
|
||||
//set currency
|
||||
currency = items[indexCurrency];
|
||||
settlementAmount = decimal.Parse(items[indexTotalAmount].Replace(",", "."));
|
||||
|
||||
// insert
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
cmd.CommandText = @"
|
||||
INSERT INTO tblImportAmazonSettlementReport (
|
||||
[settlement-id]
|
||||
,[settlement-start-date]
|
||||
,[settlement-end-date]
|
||||
,[deposit-date]
|
||||
,[total-amount]
|
||||
,[currency]
|
||||
,SpapiReportId
|
||||
)
|
||||
OUTPUT INSERTED.ImportAmazonSettlementReportID
|
||||
VALUES (
|
||||
@settlementId
|
||||
,@settlementStartDate
|
||||
,@settlementEndDate
|
||||
,@depositDate
|
||||
,@settlementotalAmounttId
|
||||
,@currency
|
||||
,@reportId
|
||||
);";
|
||||
|
||||
// add parameters
|
||||
if (indexSettlementId == -1 || items[indexSettlementId].Length == 0) { cmd.Parameters.AddWithValue("@settlementId", DBNull.Value); }
|
||||
else
|
||||
{
|
||||
settlementRef = items[indexSettlementId];
|
||||
cmd.Parameters.AddWithValue("@settlementId", settlementRef);
|
||||
}
|
||||
|
||||
var parseDateTime = new Core.Logic.Utilities.DateTime();
|
||||
|
||||
if (indexSettlementStartDate == -1 || items[indexSettlementStartDate].Length == 0) { cmd.Parameters.AddWithValue("@settlementStartDate", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@settlementStartDate", parseDateTime.ParseIsoDateTimeString(items[indexSettlementStartDate])); }
|
||||
|
||||
if (indexSettlementEndDate == -1 || items[indexSettlementEndDate].Length == 0) { cmd.Parameters.AddWithValue("@settlementEndDate", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@settlementEndDate", parseDateTime.ParseIsoDateTimeString(items[indexSettlementEndDate])); }
|
||||
|
||||
if (indexDepositDate == -1 || items[indexDepositDate].Length == 0) { cmd.Parameters.AddWithValue("@depositDate", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@depositDate", parseDateTime.ParseIsoDateTimeString(items[indexDepositDate])); }
|
||||
|
||||
if (indexTotalAmount == -1 || items[indexTotalAmount].Length == 0) { cmd.Parameters.AddWithValue("@totalAmount", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@settlementotalAmounttId", settlementAmount); }
|
||||
|
||||
if (string.IsNullOrWhiteSpace(reportId)) { cmd.Parameters.AddWithValue("@reportId", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@reportId", reportId); }
|
||||
|
||||
cmd.Parameters.AddWithValue("@currency", currency);
|
||||
|
||||
//if (currencyId == -1) { sqlCommand.Parameters.AddWithValue("@currencyId", DBNull.Value); }
|
||||
//else { sqlCommand.Parameters.AddWithValue("@currencyId", currencyId); }
|
||||
|
||||
//execute and retrive id
|
||||
settlementReportId = (int)cmd.ExecuteScalar();
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//update market place name in main table, if required
|
||||
if (marketPlaceUpdated == false && settlementReportId > 0 && items[indexMarketplaceName].Length > 1)
|
||||
{
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
cmd.CommandText = @"
|
||||
UPDATE tblImportAmazonSettlementReport
|
||||
SET [marketplace-name]=@MarketplaceName
|
||||
WHERE ImportAmazonSettlementReportID=@ImportAmazonSettlementReportID;";
|
||||
cmd.Parameters.AddWithValue("@MarketplaceName", items[indexMarketplaceName]);
|
||||
cmd.Parameters.AddWithValue("@ImportAmazonSettlementReportID", settlementReportId);
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
marketPlaceUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
//insert report items
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
cmd.CommandText =
|
||||
"INSERT INTO tblImportAmazonSettlementReportLine ( " +
|
||||
"ImportAmazonSettlementReportID, [transaction-type], [order-id], [merchant-order-id], [adjustment-id], [shipment-id], [marketplace-name], " +
|
||||
"[amount-type], [amount-description], [currency], [amount], [fulfillment-id], [posted-date-time], [order-item-code], " +
|
||||
"[merchant-order-item-id], [merchant-adjustment-item-id], [sku], [quantity-purchased], [promotion-id] ) " +
|
||||
"VALUES ( " +
|
||||
"@ImportAmazonSettlementReportID, @TransactionType, @orderRef, @merchantOrderRef, @AdjustmentRef, @ShipmentRef, @MarketplaceName, " +
|
||||
"@AmountType, @AmountDescription, @currency, @Amount, @FulfillmentRef, @PostedDateTimeUTC, @OrderItemCode, " +
|
||||
"@MerchantOrderItemRef, @MerchantAdjustmentItemRef, @SkuNumber, @QuantityPurchased, @PromotionRef );";
|
||||
|
||||
// add parameters
|
||||
cmd.Parameters.AddWithValue("@ImportAmazonSettlementReportID", settlementReportId);
|
||||
|
||||
cmd.Parameters.AddWithValue("@currency", currency);
|
||||
|
||||
if (indexTransactionType == -1 || items[indexTransactionType].Length == 0) { cmd.Parameters.AddWithValue("@TransactionType", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@TransactionType", items[indexTransactionType]); }
|
||||
|
||||
if (indexOrderId == -1 || items[indexOrderId].Length == 0) { cmd.Parameters.AddWithValue("@orderRef", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@orderRef", items[indexOrderId]); }
|
||||
|
||||
if (indexMerchantOrderId == -1 || items[indexMerchantOrderId].Length == 0) { cmd.Parameters.AddWithValue("@merchantOrderRef", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@merchantOrderRef", items[indexMerchantOrderId]); }
|
||||
|
||||
if (indexAdjustmentId == -1 || items[indexAdjustmentId].Length == 0) { cmd.Parameters.AddWithValue("@AdjustmentRef", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@AdjustmentRef", items[indexAdjustmentId]); }
|
||||
|
||||
if (indexShipmentId == -1 || items[indexShipmentId].Length == 0) { cmd.Parameters.AddWithValue("@ShipmentRef", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@ShipmentRef", items[indexShipmentId]); }
|
||||
|
||||
if (indexMarketplaceName == -1 || items[indexMarketplaceName].Length == 0) { cmd.Parameters.AddWithValue("@MarketplaceName", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@MarketplaceName", items[indexMarketplaceName]); }
|
||||
|
||||
if (indexAmountType == -1 || items[indexAmountType].Length == 0) { cmd.Parameters.AddWithValue("@AmountType", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@AmountType", items[indexAmountType]); }
|
||||
|
||||
if (indexAmountDescription == -1 || items[indexAmountDescription].Length == 0) { cmd.Parameters.AddWithValue("@AmountDescription", DBNull.Value); }
|
||||
else
|
||||
{
|
||||
string amountDescription = items[indexAmountDescription];
|
||||
if (amountDescription.Length > 100) { amountDescription = amountDescription.Substring(0, 100); }
|
||||
cmd.Parameters.AddWithValue("@AmountDescription", amountDescription);
|
||||
}
|
||||
|
||||
if (indexAmount == -1 || items[indexAmount].Length == 0) { cmd.Parameters.AddWithValue("@Amount", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@Amount", decimal.Parse(items[indexAmount].Replace(",", "."))); }
|
||||
|
||||
if (indexFulfillmentId == -1 || items[indexFulfillmentId].Length == 0) { cmd.Parameters.AddWithValue("@FulfillmentRef", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@FulfillmentRef", items[indexFulfillmentId]); }
|
||||
|
||||
if (indexPostedDateTime == -1 || items[indexPostedDateTime].Length == 0) { cmd.Parameters.AddWithValue("@PostedDateTimeUTC", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@PostedDateTimeUTC", new Logic.Utilities.DateTime().ParseIsoDateTimeString(items[indexPostedDateTime])); }
|
||||
|
||||
if (indexOrderItemCode == -1 || items[indexOrderItemCode].Length == 0) { cmd.Parameters.AddWithValue("@OrderItemCode", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@OrderItemCode", long.Parse(items[indexOrderItemCode])); }
|
||||
|
||||
if (indexMerchantOrderItemId == -1 || items[indexMerchantOrderItemId].Length == 0) { cmd.Parameters.AddWithValue("@MerchantOrderItemRef", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@MerchantOrderItemRef", long.Parse(items[indexMerchantOrderItemId])); }
|
||||
|
||||
if (indexMerchantAdjustmentItemId == -1 || items[indexMerchantAdjustmentItemId].Length == 0) { cmd.Parameters.AddWithValue("@MerchantAdjustmentItemRef", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@MerchantAdjustmentItemRef", items[indexMerchantAdjustmentItemId]); }
|
||||
|
||||
if (indexSku == -1 || items[indexSku].Length == 0) { cmd.Parameters.AddWithValue("@SkuNumber", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@SkuNumber", items[indexSku]); }
|
||||
|
||||
if (indexQuantityPurchased == -1 || items[indexQuantityPurchased].Length == 0) { cmd.Parameters.AddWithValue("@QuantityPurchased", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@QuantityPurchased", int.Parse(items[indexQuantityPurchased])); }
|
||||
|
||||
if (indexPromotionId == -1 || items[indexPromotionId].Length == 0) { cmd.Parameters.AddWithValue("@PromotionRef", DBNull.Value); }
|
||||
else { cmd.Parameters.AddWithValue("@PromotionRef", items[indexPromotionId]); }
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
lineNumber = lineNumber + 1;
|
||||
}
|
||||
}
|
||||
|
||||
//final check - settlement amount matches sum of inserted settlement lines
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
cmd.CommandText = @"
|
||||
SELECT Sum(tblImportAmazonSettlementReportLine.amount) AS SumOfAmount
|
||||
FROM tblImportAmazonSettlementReportLine
|
||||
WHERE ImportAmazonSettlementReportID=@ImportAmazonSettlementReportID;";
|
||||
|
||||
decimal sumOfAmount = -1.12345m;
|
||||
|
||||
cmd.Parameters.AddWithValue("@ImportAmazonSettlementReportID", settlementReportId);
|
||||
sumOfAmount = (decimal)cmd.ExecuteScalar();
|
||||
|
||||
if (sumOfAmount != settlementAmount)
|
||||
{
|
||||
_log.LogError("Error importing settlement id'" + settlementRef + "'. Sum of inserted settlement lines (" + sumOfAmount +
|
||||
") does not match settlement amount (" + settlementAmount + ").");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Console.Write("\r");
|
||||
_log.LogInformation((lineNumber - (2 + lineSkip)) + " total settlement items inserted");
|
||||
if (lineSkip > 0)
|
||||
{
|
||||
_log.LogError(lineSkip + " total line(s) where skipped due to insufficent number of cells on row");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int SetSpapiReportId(string settlementId, string spapiReportId)
|
||||
{
|
||||
string sqlString = @"
|
||||
UPDATE
|
||||
tblImportAmazonSettlementReport
|
||||
SET
|
||||
SpapiReportId = @spapiReportId
|
||||
WHERE
|
||||
[settlement-id] = @settlementId;";
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sqlString;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
cmd.Parameters.AddWithValue("@spapiReportId", spapiReportId);
|
||||
cmd.Parameters.AddWithValue("@settlementId", settlementId);
|
||||
|
||||
return cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using bnhtrade.Core.Data.Database.Repository.Interface;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Repository.Implementation
|
||||
{
|
||||
internal class SequenceGenerator : _Base, ISequenceGenerator
|
||||
{
|
||||
public SequenceGenerator (IDbConnection connection, IDbTransaction transaction) : base(connection, transaction)
|
||||
{
|
||||
}
|
||||
|
||||
public int GetNext(string sequenceName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sequenceName))
|
||||
{
|
||||
throw new Exception("Sequence name is null or whitespace.");
|
||||
}
|
||||
|
||||
string sql = $"SELECT NEXT VALUE FOR {sequenceName};";
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
object obj = cmd.ExecuteScalar();
|
||||
|
||||
try
|
||||
{
|
||||
return Convert.ToInt32(obj);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Provide more context in the exception
|
||||
throw new InvalidOperationException($"Error retrieving next value for sequence '{sequenceName}'. " +
|
||||
$"Raw value: '{obj}'. Inner exception: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Repository.Implementation
|
||||
{
|
||||
abstract class _Base
|
||||
{
|
||||
protected readonly IDbConnection _connection;
|
||||
protected readonly IDbTransaction _transaction;
|
||||
|
||||
public _Base(IDbConnection connection, IDbTransaction transaction)
|
||||
{
|
||||
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||
_transaction = transaction; // Transaction can be null if not needed for read-only ops
|
||||
// But for NEXT VALUE FOR, it implies a modification/dependency within a transaction
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
using bnhtrade.Core.Data.Database._BoilerPlate;
|
||||
using bnhtrade.Core.Data.Database.Repository.Interface;
|
||||
using FikaAmazonAPI.AmazonSpApiSDK.Models.FulfillmentOutbound;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Repository.Implementation
|
||||
{
|
||||
internal class _BoilerPlate : _Base, _IBoilerPlate
|
||||
{
|
||||
public _BoilerPlate(IDbConnection connection, IDbTransaction transaction) : base(connection, transaction)
|
||||
{
|
||||
}
|
||||
|
||||
public Dictionary<int, string> SelectByList(List<string> selectList)
|
||||
{
|
||||
var returnList = new Dictionary<int, string>();
|
||||
|
||||
// check input list for items
|
||||
if (selectList == null)
|
||||
{
|
||||
throw new ArgumentNullException("Method argument cannot be null");
|
||||
}
|
||||
|
||||
// build SQL string
|
||||
string sql = @"
|
||||
SELECT
|
||||
column01
|
||||
,column02
|
||||
FROM
|
||||
tblTable
|
||||
";
|
||||
|
||||
var sqlwhere = new Data.Database.SqlWhereBuilder();
|
||||
sqlwhere.In("column03", selectList, "WHERE");
|
||||
|
||||
sql = sql + sqlwhere.SqlWhereString;
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
sqlwhere.AddParametersToSqlCommand(cmd);
|
||||
|
||||
using (SqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
int id = reader.GetInt32(0);
|
||||
string data = reader.GetString(1);
|
||||
|
||||
returnList.Add(id, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
||||
public int Insert()
|
||||
{
|
||||
string sql = @"
|
||||
INSERT INTO tblTable (
|
||||
column01
|
||||
,column02
|
||||
,column03
|
||||
,column04
|
||||
)
|
||||
OUTPUT INSERTED.TablePk
|
||||
VALUES (
|
||||
@value01
|
||||
,@value02
|
||||
,@value03
|
||||
,@value04
|
||||
)
|
||||
";
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
cmd.Parameters.AddWithValue("@value01", "xxxxxxxx");
|
||||
cmd.Parameters.AddWithValue("@value02", "xxxxxxxx");
|
||||
cmd.Parameters.AddWithValue("@value03", "xxxxxxxx");
|
||||
cmd.Parameters.AddWithValue("@value04", "xxxxxxxx");
|
||||
|
||||
int newId = (int)cmd.ExecuteScalar();
|
||||
|
||||
return newId;
|
||||
}
|
||||
}
|
||||
|
||||
public int Update()
|
||||
{
|
||||
string sql = @"
|
||||
UPDATE
|
||||
tblTable
|
||||
SET
|
||||
column01 = @value01
|
||||
column02 = @value02
|
||||
column03 = @value03
|
||||
column04 = @value04
|
||||
WHERE
|
||||
column05 = @value05
|
||||
";
|
||||
|
||||
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.Transaction = _transaction as SqlTransaction;
|
||||
|
||||
cmd.Parameters.AddWithValue("@value01", "xxxxxxxx");
|
||||
cmd.Parameters.AddWithValue("@value02", "xxxxxxxx");
|
||||
cmd.Parameters.AddWithValue("@value03", "xxxxxxxx");
|
||||
cmd.Parameters.AddWithValue("@value04", "xxxxxxxx");
|
||||
cmd.Parameters.AddWithValue("@value04", "xxxxxxxx");
|
||||
|
||||
int recordsEffected = cmd.ExecuteNonQuery();
|
||||
|
||||
return recordsEffected;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Repository.Interface
|
||||
{
|
||||
internal interface ICurrencyRepository
|
||||
{
|
||||
public decimal? ReadExchangeRate(Model.Account.CurrencyCode currencyCode, DateTime date);
|
||||
public List<Model.Account.CurrencyExchangeRate> ReadExchangeRate(List<Model.Account.CurrencyCode> currencyCodeList = null, DateTime date = default(DateTime));
|
||||
public List<Model.Account.CurrencyExchangeRate> ReadExchangeRateLatest(List<Model.Account.CurrencyCode> currencyCodeList = null);
|
||||
public int InsertExchangeRate(int exchangeRateSource, Model.Account.CurrencyCode currencyCode,
|
||||
decimal currencyUnitsPerGbp, DateTime periodStartUtc, DateTime periodEnd, bool checkOverride = false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Repository.Interface
|
||||
{
|
||||
internal interface IExportInvoiceRepository
|
||||
{
|
||||
internal Dictionary<int, Model.Account.SalesInvoice> InsertSalesInvoices(IEnumerable<Model.Account.SalesInvoice> invoiceList);
|
||||
internal Dictionary<int, string> GetNewInvoiceNumbers(Model.Account.InvoiceType invoiceType);
|
||||
internal Dictionary<int, Model.Account.SalesInvoice> GetSalesInvoiceById(IEnumerable<int> idList);
|
||||
internal int SetInvoiceIsCompleteValue(Dictionary<int, bool> updateDictionary);
|
||||
internal int DeleteInvoiceLine(int invoiceLineId);
|
||||
internal int DeleteInvoice(IEnumerable<int> invoiceIdList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Repository.Interface
|
||||
{
|
||||
internal interface IImportAmazonRepository
|
||||
{
|
||||
public List<Model.Import.AmazonSettlementHeader> ReadAmazonSettlementHeaderInfoBySettlementId(List<string> settlementIdList);
|
||||
|
||||
public List<Model.Import.AmazonSettlementHeader> ReadAmazonSettlementHeaderInfoBySpapiReportId(List<string> spapiReportIdList);
|
||||
|
||||
public Dictionary<int, Model.Import.AmazonSettlement> ReadAmazonSettlements(
|
||||
List<string> settlementIdList = null, List<string> marketPlaceNameList = null, bool? isProcessed = null,
|
||||
bool descendingOrder = false, int? returnTop = null);
|
||||
|
||||
public int SetAmazonSettlementIsProcessed(List<string> settlementIdList, bool isProcessed);
|
||||
|
||||
public bool CreateAmazonSettlements(string filePath, string reportId);
|
||||
|
||||
public int SetSpapiReportId(string settlementId, string spapiReportId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Data;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Repository.Interface
|
||||
{
|
||||
internal interface ISequenceGenerator
|
||||
{
|
||||
int GetNext(string sequenceName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.Repository.Interface
|
||||
{
|
||||
internal interface _IBoilerPlate
|
||||
{
|
||||
internal Dictionary<int, string> SelectByList(List<string> selectList);
|
||||
internal int Insert();
|
||||
internal int Update();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using FikaAmazonAPI.AmazonSpApiSDK.Models.ProductPricing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -22,11 +22,11 @@ namespace bnhtrade.Core.Data.Database.Sku
|
||||
var dbTax = new Data.Database.Account.ReadTaxCode();
|
||||
var taxCodeList = dbTax.GetByTaxCodeId(new List<int> { accountTaxCodeId });
|
||||
|
||||
if (!taxCodeList.Any())
|
||||
if (taxCodeList.ContainsKey(accountTaxCodeId) == false)
|
||||
{
|
||||
throw new Exception("AccountTaxCodeID=" + accountTaxCodeId + " doesn't exist!");
|
||||
}
|
||||
else if (taxCodeList[0].IsValidOnIncome == false)
|
||||
else if (taxCodeList[accountTaxCodeId].IsValidOnIncome == false)
|
||||
{
|
||||
throw new Exception("AccountTaxCodeID=" + accountTaxCodeId + " is not a valid type for an SKU.");
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database
|
||||
{
|
||||
@@ -12,9 +13,12 @@ namespace bnhtrade.Core.Data.Database
|
||||
/// Step 1: Call the methods for each where clause you want to create. This can be done multiple times to create an sql where string. Pay attention
|
||||
/// to the prefixes that you'll require between each where clause, as each time a method is called the sql statement will be appended to the previous sql
|
||||
/// string.
|
||||
///
|
||||
/// Step 2: Appened the created sql string to your sql statement, NB the WHERE statemet is not included by default.
|
||||
///
|
||||
/// Step 3: Once you've created your sql command object, add the parameters to it using the method contained within this class.
|
||||
/// STep 4: exceute your sql commend.
|
||||
///
|
||||
/// Step 4: exceute your sql commend.
|
||||
/// </summary>
|
||||
public class SqlWhereBuilder
|
||||
{
|
||||
@@ -44,6 +48,21 @@ namespace bnhtrade.Core.Data.Database
|
||||
|
||||
public Dictionary<string, object> ParameterList { get; private set; }
|
||||
|
||||
public int ParameterListCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ParameterList == null || ParameterList.Any() == false)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ParameterList.Count();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialises the class
|
||||
/// </summary>
|
||||
@@ -83,7 +102,7 @@ namespace bnhtrade.Core.Data.Database
|
||||
/// <param name="columnReference">Name of the column to used to for the condition statement</param>
|
||||
/// <param name="phraseList">List of phrases to test in condition statement</param>
|
||||
/// <param name="wherePrefix">Optional prefix that gets added to the sql string result</param>
|
||||
public void LikeAnd(string columnReference, List<string> phraseList, string wherePrefix = null)
|
||||
public void LikeAnd(string columnReference, IEnumerable<string> phraseList, string wherePrefix = null)
|
||||
{
|
||||
Like(columnReference, phraseList, true, wherePrefix);
|
||||
}
|
||||
@@ -94,12 +113,12 @@ namespace bnhtrade.Core.Data.Database
|
||||
/// <param name="columnReference">Name of the column to used to for the condition statement</param>
|
||||
/// <param name="phraseList">List of phrases to test in condition statement</param>
|
||||
/// <param name="wherePrefix">Optional prefix that gets added to the sql string result</param>
|
||||
public void LikeOr(string columnReference, List<string> phraseList, string wherePrefix = null)
|
||||
public void LikeOr(string columnReference, IEnumerable<string> phraseList, string wherePrefix = null)
|
||||
{
|
||||
Like(columnReference, phraseList, false, wherePrefix);
|
||||
}
|
||||
|
||||
private void Like(string columnReference, List<string> phraseList, bool isAnd, string wherePrefix = null)
|
||||
private void Like(string columnReference, IEnumerable<string> phraseList, bool isAnd, string wherePrefix = null)
|
||||
{
|
||||
if (phraseList == null || !phraseList.Any())
|
||||
{
|
||||
@@ -107,7 +126,7 @@ namespace bnhtrade.Core.Data.Database
|
||||
}
|
||||
|
||||
// ensure no values are repeated
|
||||
var distinctList = phraseList.ToList();
|
||||
var distinctList = phraseList.Distinct().ToList();
|
||||
|
||||
// clean the list
|
||||
for (int i = 0; i < distinctList.Count; i++)
|
||||
@@ -166,7 +185,7 @@ namespace bnhtrade.Core.Data.Database
|
||||
/// <param name="columnReference">Name of the column to used to for the condition statement</param>
|
||||
/// <param name="orValueList">List of values to test in condition statement</param>
|
||||
/// <param name="wherePrefix">Optional prefix that gets added to the sql string result</param>
|
||||
public void In(string columnReference, List<string> orValueList, string wherePrefix = null)
|
||||
public void In(string columnReference, IEnumerable<object> orValueList, string wherePrefix = null)
|
||||
{
|
||||
if (orValueList == null || !orValueList.Any())
|
||||
{
|
||||
@@ -208,19 +227,19 @@ namespace bnhtrade.Core.Data.Database
|
||||
/// <param name="columnReference">Name of the column to used to for the condition statement</param>
|
||||
/// <param name="orValueList">List of values to test in condition statement</param>
|
||||
/// <param name="wherePrefix">Optional prefix that gets added to the sql string result</param>
|
||||
public void In(string columnReference, List<int> orValueList, string wherePrefix = null)
|
||||
public void In(string columnReference, IEnumerable<string> orValueList, string wherePrefix = null)
|
||||
{
|
||||
var stringList = new List<string>();
|
||||
var objectList = new List<object>();
|
||||
|
||||
if (orValueList != null || !orValueList.Any())
|
||||
if (orValueList != null && orValueList.Any())
|
||||
{
|
||||
foreach (int value in orValueList)
|
||||
foreach (string value in orValueList)
|
||||
{
|
||||
stringList.Add(value.ToString());
|
||||
objectList.Add(value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
In(columnReference, stringList, wherePrefix);
|
||||
In(columnReference, objectList, wherePrefix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -229,19 +248,55 @@ namespace bnhtrade.Core.Data.Database
|
||||
/// <param name="columnReference">Name of the column to used to for the condition statement</param>
|
||||
/// <param name="orValueList">List of values to test in condition statement</param>
|
||||
/// <param name="wherePrefix">Optional prefix that gets added to the sql string result</param>
|
||||
public void In(string columnReference, List<uint> orValueList, string wherePrefix = null)
|
||||
public void In(string columnReference, IEnumerable<int> orValueList, string wherePrefix = null)
|
||||
{
|
||||
var stringList = new List<string>();
|
||||
var objectList = new List<object>();
|
||||
|
||||
if (orValueList != null || !orValueList.Any())
|
||||
if (orValueList != null && orValueList.Any())
|
||||
{
|
||||
foreach (uint value in orValueList)
|
||||
foreach (var value in orValueList)
|
||||
{
|
||||
stringList.Add(value.ToString());
|
||||
objectList.Add(value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
In(columnReference, stringList, wherePrefix);
|
||||
In(columnReference, objectList, wherePrefix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append an 'In' statement and parameter list to the class properties
|
||||
/// </summary>
|
||||
/// <param name="columnReference">Name of the column to used to for the condition statement</param>
|
||||
/// <param name="orValueList">List of values to test in condition statement</param>
|
||||
/// <param name="wherePrefix">Optional prefix that gets added to the sql string result</param>
|
||||
public void In(string columnReference, IEnumerable<uint> orValueList, string wherePrefix = null)
|
||||
{
|
||||
var objectList = new List<object>();
|
||||
|
||||
if (orValueList != null && orValueList.Any())
|
||||
{
|
||||
foreach (var value in orValueList)
|
||||
{
|
||||
objectList.Add(value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
In(columnReference, objectList, wherePrefix);
|
||||
}
|
||||
|
||||
public void In(string columnReference, IEnumerable<DateTime> orValueList, string wherePrefix = null)
|
||||
{
|
||||
var objectList = new List<object>();
|
||||
|
||||
if (orValueList != null && orValueList.Any())
|
||||
{
|
||||
foreach(var value in orValueList)
|
||||
{
|
||||
objectList.Add(value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
In(columnReference, objectList, wherePrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
55
src/bnhtrade.Core/Data/Database/UnitOfWork/Connection.cs
Normal file
55
src/bnhtrade.Core/Data/Database/UnitOfWork/Connection.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Data.SqlClient;
|
||||
using System.Configuration;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.UnitOfWork
|
||||
{
|
||||
internal class Connection
|
||||
{
|
||||
//protected readonly string SqlConnectionString;
|
||||
private Model.Credentials.bnhtradeDB _dbCredentials;
|
||||
|
||||
protected string SqlConnectionString
|
||||
{
|
||||
get { return _dbCredentials.ConnectionString; }
|
||||
}
|
||||
|
||||
public Connection()
|
||||
{
|
||||
var config = new Config().GetConfiguration();
|
||||
|
||||
// attempt to retrive credentials from app.local.config
|
||||
try
|
||||
{
|
||||
string dataSource = config.AppSettings.Settings["DbDataSource"].Value;
|
||||
string userId = config.AppSettings.Settings["DbUserId"].Value;
|
||||
string pass = config.AppSettings.Settings["DbUserPassword"].Value;
|
||||
|
||||
// check
|
||||
if (string.IsNullOrEmpty(dataSource))
|
||||
{
|
||||
throw new ArgumentException("Could not retrive 'DbDataSource' from config file");
|
||||
}
|
||||
else if (string.IsNullOrEmpty(userId))
|
||||
{
|
||||
throw new ArgumentException("Could not retrive 'DbUserId' from config file");
|
||||
}
|
||||
else if (string.IsNullOrEmpty(pass))
|
||||
{
|
||||
throw new ArgumentException("Could not retrive 'DbUserPassword' from config file");
|
||||
}
|
||||
|
||||
var dbCredentials = new bnhtrade.Core.Model.Credentials.bnhtradeDB(dataSource, userId, pass);
|
||||
this._dbCredentials = dbCredentials;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("Unable to retirve DB credentials: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/bnhtrade.Core/Data/Database/UnitOfWork/IUnitOfWork.cs
Normal file
23
src/bnhtrade.Core/Data/Database/UnitOfWork/IUnitOfWork.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using bnhtrade.Core.Data.Database.Repository.Interface;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.UnitOfWork
|
||||
{
|
||||
internal interface IUnitOfWork : IDisposable
|
||||
{
|
||||
// Properties for repositories, add here for each repository
|
||||
public ICurrencyRepository CurrencyRepository { get; }
|
||||
IExportInvoiceRepository ExportInvoiceRepository { get; }
|
||||
IImportAmazonRepository ImportAmazonRepository { get; }
|
||||
ISequenceGenerator SequenceGenerator { get; }
|
||||
|
||||
// Methods to manage the transaction
|
||||
void Commit();
|
||||
|
||||
void Rollback();
|
||||
}
|
||||
}
|
||||
136
src/bnhtrade.Core/Data/Database/UnitOfWork/UnitOfWork.cs
Normal file
136
src/bnhtrade.Core/Data/Database/UnitOfWork/UnitOfWork.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using bnhtrade.Core.Data.Database.Repository.Implementation;
|
||||
using bnhtrade.Core.Data.Database.Repository.Interface;
|
||||
using bnhtrade.Core.Data.Database.UnitOfWork;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Data.Database.UnitOfWork
|
||||
{
|
||||
internal class UnitOfWork : Connection, IUnitOfWork
|
||||
{
|
||||
private IDbConnection _connection;
|
||||
private IDbTransaction _transaction;
|
||||
private bool _disposed;
|
||||
|
||||
// Private field for lazy loading, add here for each repository
|
||||
private ICurrencyRepository _currencyRepository;
|
||||
private IExportInvoiceRepository _exportInvoiceRepository;
|
||||
private IImportAmazonRepository _importAmazonRepository;
|
||||
private ISequenceGenerator _sequenceGenerator;
|
||||
|
||||
public UnitOfWork()
|
||||
{
|
||||
_connection = new SqlConnection(this.SqlConnectionString);
|
||||
_connection.Open();
|
||||
_transaction = _connection.BeginTransaction();
|
||||
}
|
||||
|
||||
// Properties for repositories, add here for each repository
|
||||
public ICurrencyRepository CurrencyRepository
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currencyRepository == null)
|
||||
{
|
||||
_currencyRepository = new CurrencyRepository(_connection, _transaction);
|
||||
}
|
||||
return _currencyRepository;
|
||||
}
|
||||
}
|
||||
|
||||
public IExportInvoiceRepository ExportInvoiceRepository
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_exportInvoiceRepository == null)
|
||||
{
|
||||
_exportInvoiceRepository = new ExportInvoiceRepository(_connection, _transaction);
|
||||
}
|
||||
return _exportInvoiceRepository;
|
||||
}
|
||||
}
|
||||
|
||||
public IImportAmazonRepository ImportAmazonRepository
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_importAmazonRepository == null)
|
||||
{
|
||||
_importAmazonRepository = new ImportAmazonRepository(_connection, _transaction);
|
||||
}
|
||||
return _importAmazonRepository;
|
||||
}
|
||||
}
|
||||
|
||||
public ISequenceGenerator SequenceGenerator
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_sequenceGenerator == null)
|
||||
{
|
||||
_sequenceGenerator = new SequenceGenerator(_connection, _transaction);
|
||||
}
|
||||
return _sequenceGenerator;
|
||||
}
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
{
|
||||
try
|
||||
{
|
||||
_transaction.Commit();
|
||||
}
|
||||
catch
|
||||
{
|
||||
_transaction.Rollback();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Rollback()
|
||||
{
|
||||
_transaction.Rollback();
|
||||
Dispose();
|
||||
}
|
||||
|
||||
// IDisposable implementation (ensure proper disposal of connection and transaction)
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose the transaction first
|
||||
if (_transaction != null)
|
||||
{
|
||||
_transaction.Dispose();
|
||||
_transaction = null;
|
||||
}
|
||||
// Then close and dispose the connection
|
||||
if (_connection != null)
|
||||
{
|
||||
_connection.Close();
|
||||
_connection.Dispose();
|
||||
_connection = null;
|
||||
}
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Test._BoilerPlate
|
||||
namespace bnhtrade.Core.Data.Database._BoilerPlate
|
||||
{
|
||||
class ClassFromSql
|
||||
{
|
||||
@@ -1,34 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
|
||||
namespace bnhtrade.Core.Test._BoilerPlate
|
||||
namespace bnhtrade.Core.Data.Database._BoilerPlate
|
||||
{
|
||||
public class Sql
|
||||
internal class Sql
|
||||
{
|
||||
public void Select()
|
||||
private string SqlConnectionString = "";
|
||||
|
||||
internal List<string> Select()
|
||||
{
|
||||
using (SqlConnection conn = new SqlConnection())
|
||||
var returnList = new List<string>();
|
||||
|
||||
string sql = @"
|
||||
SELECT
|
||||
column01
|
||||
,column02
|
||||
,column03
|
||||
,column04
|
||||
FROM tblTable
|
||||
WHERE
|
||||
column01 = @value01
|
||||
OR column01 = @value02
|
||||
OR column01 = @value03
|
||||
OR column01 = @value04; ";
|
||||
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
using (SqlCommand cmd = new SqlCommand(@"
|
||||
SELECT
|
||||
column01
|
||||
,column02
|
||||
,column03
|
||||
,column04
|
||||
FROM tblTable
|
||||
WHERE
|
||||
column01 = @value01
|
||||
OR column01 = @value02
|
||||
OR column01 = @value03
|
||||
OR column01 = @value04
|
||||
", conn))
|
||||
using (SqlCommand cmd = new SqlCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@value01", "xxxxxxxx");
|
||||
cmd.Parameters.AddWithValue("@value02", "xxxxxxxx");
|
||||
@@ -37,22 +42,17 @@ namespace bnhtrade.Core.Test._BoilerPlate
|
||||
|
||||
using (SqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (!reader.HasRows)
|
||||
while (reader.Read())
|
||||
{
|
||||
// do something
|
||||
}
|
||||
else
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
// do some thing with the data
|
||||
}
|
||||
returnList.Add("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
public string SelectByList(List<string> stringList)
|
||||
|
||||
internal string SelectByList(List<string> stringList)
|
||||
{
|
||||
// check input list for items
|
||||
if (stringList == null || !stringList.Any())
|
||||
@@ -98,7 +98,7 @@ namespace bnhtrade.Core.Test._BoilerPlate
|
||||
|
||||
// execute query and build result list
|
||||
var skuTaxCodeList = new List<Tuple<string, string>>();
|
||||
using (SqlConnection conn = new SqlConnection())
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
@@ -125,11 +125,12 @@ namespace bnhtrade.Core.Test._BoilerPlate
|
||||
}
|
||||
return "Complete"; // return object
|
||||
}
|
||||
public void Insert()
|
||||
|
||||
internal void Insert()
|
||||
{
|
||||
using (TransactionScope scope = new TransactionScope())
|
||||
{
|
||||
using (SqlConnection conn = new SqlConnection())
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
@@ -160,11 +161,12 @@ namespace bnhtrade.Core.Test._BoilerPlate
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
public void Update()
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
using (TransactionScope scope = new TransactionScope())
|
||||
{
|
||||
using (SqlConnection conn = new SqlConnection())
|
||||
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using bnhtrade.Core.Data.Database;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Test._BoilerPlate
|
||||
namespace bnhtrade.Core.Data.Database._BoilerPlate
|
||||
{
|
||||
internal class xxxxxxxYourClassNameHerexxxxxxxx : Connection
|
||||
{
|
||||
private bnhtrade.Core.Data.Database.SqlWhereBuilder sqlBuilder;
|
||||
private SqlWhereBuilder sqlBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Results filter
|
||||
172
src/bnhtrade.Core/Data/Xero/SalesInvoice.cs
Normal file
172
src/bnhtrade.Core/Data/Xero/SalesInvoice.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using bnhtrade.Core.Test.Amazon.SP_API;
|
||||
using CsvHelper;
|
||||
using CsvHelper.Configuration.Attributes;
|
||||
using Microsoft.VisualBasic.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
namespace bnhtrade.Core.Data.Xero
|
||||
{
|
||||
internal class SalesInvoice
|
||||
{
|
||||
private class CsvLine
|
||||
{
|
||||
[Required()]
|
||||
[Name("*ContactName")]
|
||||
public string ContactName { get; set; }
|
||||
|
||||
public string EmailAddress { get; set; }
|
||||
|
||||
public string POAddressLine1 { get; set; }
|
||||
|
||||
public string POAddressLine2 { get; set; }
|
||||
|
||||
public string POAddressLine3 { get; set; }
|
||||
|
||||
public string POAddressLine4 { get; set; }
|
||||
|
||||
public string POCity { get; set; }
|
||||
|
||||
public string PORegion { get; set; }
|
||||
|
||||
public string POPostalCode { get; set; }
|
||||
|
||||
public string POCountry { get; set; }
|
||||
|
||||
[Required()]
|
||||
[Name("*InvoiceNumber")]
|
||||
public string InvoiceNumber { get; set; }
|
||||
public string Reference { get; set; }
|
||||
|
||||
[Required()]
|
||||
[Name("*InvoiceDate")]
|
||||
public string InvoiceDate { get; set; }
|
||||
|
||||
[Required()]
|
||||
[Name("*DueDate")]
|
||||
public string DueDate { get; set; }
|
||||
|
||||
public string Total { get; set; }
|
||||
|
||||
public string InventoryItemCode { get; set; }
|
||||
|
||||
[Required()]
|
||||
[Name("*Description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[Required()]
|
||||
[Name("*Quantity")]
|
||||
public string Quantity { get; set; }
|
||||
|
||||
[Required()]
|
||||
[Name("*UnitAmount")]
|
||||
public string UnitAmount { get; set; }
|
||||
|
||||
public string Discount { get; set; }
|
||||
|
||||
[Required()]
|
||||
[Name("*AccountCode")]
|
||||
public string AccountCode { get; set; }
|
||||
|
||||
[Required()]
|
||||
[Name("*TaxType")]
|
||||
public string TaxType { get; set; }
|
||||
|
||||
public string TaxAmount { get; set; }
|
||||
|
||||
public string TrackingName1 { get; set; }
|
||||
|
||||
public string TrackingOption1 { get; set; }
|
||||
|
||||
public string TrackingName2 { get; set; }
|
||||
|
||||
public string TrackingOption2 { get; set; }
|
||||
|
||||
public string Currency { get; set; }
|
||||
|
||||
public string BrandingTheme { get; set; }
|
||||
}
|
||||
|
||||
internal void ExportToCsv(List<Core.Model.Account.SalesInvoice> salesInvoiceList, int startingInvoiceNumber, string savePath, bool unitAmountIsTaxExclusive = true)
|
||||
{
|
||||
int invoiceNumberCount = startingInvoiceNumber;
|
||||
|
||||
// convert invoice to unitAmountIsTaxExclusive = true
|
||||
foreach (var invoice in salesInvoiceList)
|
||||
{
|
||||
if (invoice.InvoiceLineUnitAmountIsTaxExclusive != unitAmountIsTaxExclusive)
|
||||
{
|
||||
if (invoice.InvoiceLineUnitAmountIsTaxExclusive == true)
|
||||
{
|
||||
invoice.ConvertToLineUnitAmountIsTaxInclusive();
|
||||
}
|
||||
else
|
||||
{
|
||||
invoice.ConvertToLineUnitAmountIsTaxExclusive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var csvLineList = new List<CsvLine>();
|
||||
foreach (var invoice in salesInvoiceList)
|
||||
{
|
||||
if (invoice.InvoiceLineUnitAmountIsTaxExclusive != unitAmountIsTaxExclusive)
|
||||
{
|
||||
throw new Exception("UnitAmountIsTaxExclusive check failed");
|
||||
}
|
||||
|
||||
string contactName = invoice.ContactName;
|
||||
string invoiceNumber = "INV-" + invoiceNumberCount.ToString("000000");
|
||||
string reference = invoice.InvoiceReference;
|
||||
string invoiceDate = invoice.InvoiceDate.GetValueOrDefault().ToString("dd MMM yyyy", CultureInfo.InvariantCulture);
|
||||
string dueDate = invoice.InvoiceDueDate.GetValueOrDefault().ToString("dd MMM yyyy", CultureInfo.InvariantCulture);
|
||||
string total = invoice.InvoiceTotalAmount.ToString();
|
||||
string currencyCode = invoice.InvoiceCurrencyCode.ToString();
|
||||
|
||||
foreach (var line in invoice.InvoiceLineList)
|
||||
{
|
||||
var csvLine = new CsvLine();
|
||||
|
||||
// header info
|
||||
csvLine.ContactName = contactName;
|
||||
csvLine.InvoiceNumber = invoiceNumber;
|
||||
csvLine.Reference = reference;
|
||||
csvLine.InvoiceDate = invoiceDate;
|
||||
csvLine.DueDate = dueDate;
|
||||
csvLine.Total = total;
|
||||
|
||||
// line info
|
||||
csvLine.Description = line.Description;
|
||||
csvLine.Quantity = line.Quantity.ToString();
|
||||
csvLine.UnitAmount = line.UnitAmount.ToString();
|
||||
csvLine.AccountCode = line.Account.AccountCode.ToString();
|
||||
csvLine.TaxType = line.TaxCode.TaxCodeName;
|
||||
csvLine.TaxAmount = line.TaxAmountTotal.ToString();
|
||||
csvLine.Currency = currencyCode;
|
||||
|
||||
csvLineList.Add(csvLine);
|
||||
}
|
||||
|
||||
invoiceNumberCount++;
|
||||
}
|
||||
|
||||
var config = new CsvHelper.Configuration.CsvConfiguration(CultureInfo.CurrentCulture);
|
||||
//config.Delimiter = "\t";
|
||||
config.Encoding = Encoding.UTF8;
|
||||
|
||||
savePath = Environment.ExpandEnvironmentVariables(savePath);
|
||||
using (var writer = new StreamWriter(savePath))
|
||||
using (var csv = new CsvWriter(writer, config))
|
||||
{
|
||||
csv.WriteRecords(csvLineList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
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)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private DateTime GetHmrcMaxPeriodAvaible()
|
||||
{
|
||||
// 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
445
src/bnhtrade.Core/Logic/Account/CurrencyService.cs
Normal file
445
src/bnhtrade.Core/Logic/Account/CurrencyService.cs
Normal file
@@ -0,0 +1,445 @@
|
||||
using bnhtrade.Core.Data.Database.UnitOfWork;
|
||||
using FikaAmazonAPI.AmazonSpApiSDK.Models.FulfillmentInbound;
|
||||
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;
|
||||
using static bnhtrade.Core.Model.Account.PurchaseInvoice;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Account
|
||||
{
|
||||
public class CurrencyService
|
||||
{
|
||||
private readonly IUnitOfWork _providedUnitOfWork = null;
|
||||
private readonly bool _ownsUnitOfWork = false;
|
||||
private List<Model.Account.CurrencyExchangeRate> _exchangeRateList = new List<Model.Account.CurrencyExchangeRate>();
|
||||
|
||||
Log.LogEvent _log = new Log.LogEvent();
|
||||
|
||||
public string ErrorMessage { get; private set; } = null;
|
||||
|
||||
public bool ErrorMessageIsSet
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrEmpty(ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public CurrencyService()
|
||||
{
|
||||
_ownsUnitOfWork = true;
|
||||
}
|
||||
|
||||
internal CurrencyService(IUnitOfWork unitOfWork)
|
||||
{
|
||||
_providedUnitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
|
||||
_ownsUnitOfWork = false;
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
ErrorMessage = null;
|
||||
}
|
||||
|
||||
public bool ConvertInvoiceToGbp(Model.Account.IInvoice invoice)
|
||||
{
|
||||
Init();
|
||||
|
||||
//checks
|
||||
if (invoice == null)
|
||||
{
|
||||
ErrorMessage = "Invoice cannot be null";
|
||||
return false;
|
||||
}
|
||||
if (invoice.InvoiceCurrencyCode == Model.Account.CurrencyCode.GBP)
|
||||
{
|
||||
return true; // no conversion needed
|
||||
}
|
||||
if (invoice.InvoiceDate == null)
|
||||
{
|
||||
ErrorMessage = "Invoice date cannot be null";
|
||||
return false;
|
||||
}
|
||||
|
||||
//validate the invoice
|
||||
var validate = new Logic.Validate.Invoice();
|
||||
// Fix for CS1503: Cast 'invoice' to 'Model.Account.Invoice' explicitly
|
||||
if (validate.IsValidExportInvoice(new List<Model.Account.Invoice> { (Model.Account.Invoice)invoice }) == false)
|
||||
{
|
||||
ErrorMessage = "Invoice failed validation. See logs for further details.";
|
||||
_log.LogError(ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if exchange rate is already in list
|
||||
Model.Account.CurrencyExchangeRate existingRate = null;
|
||||
foreach (var exchangeRate in _exchangeRateList)
|
||||
{
|
||||
if (exchangeRate.CurrencyCode == invoice.InvoiceCurrencyCode && exchangeRate.DateTimeWithinPeriodCheck(invoice.InvoiceDate.Value))
|
||||
{
|
||||
existingRate = exchangeRate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// get rates from db and add to field list if not in list already
|
||||
var rateList = GetExchangeRateObjectList(
|
||||
new List<Model.Account.CurrencyCode> { invoice.InvoiceCurrencyCode }, invoice.InvoiceDate.Value);
|
||||
|
||||
if (rateList == null || rateList.Count == 0)
|
||||
{
|
||||
ErrorMessage =
|
||||
"Exchange rate for currency code '" + invoice.InvoiceCurrencyCode + "' and date " + invoice.InvoiceDate.Value.ToShortDateString() + "' does not exist in the Exchange Rate table";
|
||||
return false;
|
||||
}
|
||||
|
||||
_exchangeRateList.AddRange(rateList);
|
||||
existingRate = rateList[0];
|
||||
|
||||
// do the conversion
|
||||
// convert header information
|
||||
decimal invoiceOriginalTotalAmount = invoice.InvoiceTotalAmount.Value;
|
||||
invoice.InvoiceTotalAmount = existingRate.ConvertToGbp(invoice.InvoiceTotalAmount.Value);
|
||||
invoice.InvoiceCurrencyCode = Model.Account.CurrencyCode.GBP;
|
||||
|
||||
// convert line items
|
||||
var lineWeighting = new List<(int, decimal)>();
|
||||
var lineCalculatedTotalByWeighting = new List<(int, decimal)>();
|
||||
decimal newLineSum = 0;
|
||||
int i = 0;
|
||||
foreach (var line in invoice.InvoiceLineList)
|
||||
{
|
||||
decimal weighting = line.LineTotalAmount / invoiceOriginalTotalAmount;
|
||||
decimal lineCalculatedTotal = invoiceOriginalTotalAmount * weighting;
|
||||
lineWeighting.Add(new(i, weighting));
|
||||
lineCalculatedTotalByWeighting.Add(new(i, lineCalculatedTotal));
|
||||
|
||||
// edit line
|
||||
if (line.TaxAmountAdjust != 0)
|
||||
{
|
||||
line.TaxAmountAdjust = existingRate.ConvertToGbp(line.TaxAmountAdjust);
|
||||
}
|
||||
line.UnitAmount = null;
|
||||
line.SetUnitAmountByLineTotal(decimal.Round(lineCalculatedTotal, 2));
|
||||
newLineSum += line.LineTotalAmount;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// there may be rounding errors
|
||||
// section untested - may have to fix some bugs in here when I've got some invoice to test on
|
||||
if (invoiceOriginalTotalAmount != newLineSum)
|
||||
{
|
||||
decimal amountRemainingToDistribute = invoiceOriginalTotalAmount - newLineSum;
|
||||
lineWeighting = lineWeighting.OrderByDescending(i => i.Item2).ToList();
|
||||
|
||||
foreach (var line in lineWeighting)
|
||||
{
|
||||
// distribute the amount remaining to the lines
|
||||
decimal amountToDistribute = amountRemainingToDistribute * line.Item2;
|
||||
if (amountToDistribute > 0 && amountToDistribute < 0.01m)
|
||||
{
|
||||
amountToDistribute = 0.01m; // minimum amount to distribute
|
||||
}
|
||||
else if (amountToDistribute < 0 && amountToDistribute > -0.01m)
|
||||
{
|
||||
amountToDistribute = -0.01m; // minimum amount to distribute
|
||||
}
|
||||
else
|
||||
{
|
||||
amountToDistribute = Math.Round(amountToDistribute, 2);
|
||||
}
|
||||
|
||||
amountRemainingToDistribute = amountRemainingToDistribute - amountToDistribute;
|
||||
invoice.InvoiceLineList[line.Item1].SetUnitAmountByLineTotal(
|
||||
invoice.InvoiceLineList[line.Item1].LineTotalAmount + amountToDistribute
|
||||
);
|
||||
|
||||
if (amountRemainingToDistribute == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (amountRemainingToDistribute > 0.01m || amountRemainingToDistribute < -0.01m)
|
||||
{
|
||||
// check if the amount remaining to distribute is too large
|
||||
// this should not happen, but if it does, you'll have to manually fix the invoice or do more coding
|
||||
ErrorMessage = "Rounding error when converting invoice to GBP. Amount remaining to distribute: " + amountRemainingToDistribute.ToString("C", CultureInfo.CurrentCulture);
|
||||
_log.LogError(ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
else if (amountRemainingToDistribute != 0 && amountRemainingToDistribute < 0.01m && amountRemainingToDistribute > -0.01m)
|
||||
{
|
||||
invoice.InvoiceLineList[lineWeighting[0].Item1].SetUnitAmountByLineTotal(
|
||||
invoice.InvoiceLineList[lineWeighting[0].Item1].LineTotalAmount + amountRemainingToDistribute
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix for CS1950: Ensure the list contains valid 'Model.Account.Invoice' objects
|
||||
if (validate.IsValidExportInvoice(new List<Model.Account.Invoice> { (Model.Account.Invoice)invoice }) == false)
|
||||
{
|
||||
ErrorMessage = "Invoice failed validation after conversion to GBP. See logs for further details.";
|
||||
_log.LogError(ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Model.Account.CurrencyExchangeRate> GetExchangeRateObjectList(List<Model.Account.CurrencyCode> currencyCodeList, DateTime conversionDate)
|
||||
{
|
||||
Init();
|
||||
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
|
||||
using (currentUow != null && _ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
return currentUow.CurrencyRepository.ReadExchangeRate(currencyCodeList, conversionDate);
|
||||
}
|
||||
}
|
||||
|
||||
public decimal CurrencyConvertToGbp(string currencyCode, decimal amount, DateTime conversionDate)
|
||||
{
|
||||
Init();
|
||||
|
||||
if (string.IsNullOrEmpty(currencyCode) || currencyCode.Length != 3)
|
||||
{
|
||||
throw new Exception("Invalid currency code '" + currencyCode + "'");
|
||||
}
|
||||
|
||||
Enum.TryParse(currencyCode, out Model.Account.CurrencyCode enumCurrencyCode);
|
||||
|
||||
return CurrencyConvertToGbp(enumCurrencyCode, amount, conversionDate);
|
||||
}
|
||||
|
||||
public decimal CurrencyConvertToGbp(Model.Account.CurrencyCode currencyCode, decimal amount, DateTime conversionDate)
|
||||
{
|
||||
Init();
|
||||
|
||||
//checks
|
||||
if (currencyCode == Model.Account.CurrencyCode.GBP || amount == 0M)
|
||||
{
|
||||
return amount;
|
||||
}
|
||||
|
||||
// read db
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
|
||||
using (currentUow != null && _ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
var exchageRate = currentUow.CurrencyRepository.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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime GetHmrcMaxPeriodAvaible()
|
||||
{
|
||||
// HMRC monthly exchange rates are published on the penultimate Thursday of the month before
|
||||
// For some leeeeeeeeway 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()
|
||||
{
|
||||
Init();
|
||||
|
||||
_log.LogInformation("Starting update database HMRC exchange rates");
|
||||
|
||||
int exchangeRateSourceId = 1; // id for hmrc
|
||||
|
||||
// retrive most recent data from db
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
|
||||
using (currentUow != null && _ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
var dbLatestRates = currentUow.CurrencyRepository.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
|
||||
currentUow.CurrencyRepository.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);
|
||||
}
|
||||
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow.Commit();
|
||||
}
|
||||
|
||||
_log.LogInformation("Updating database currency exchange rates complete.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Account
|
||||
{
|
||||
public class GetInvoiceLineItem
|
||||
{
|
||||
private Dictionary<string, Model.Account.InvoiceLineItem> cache;
|
||||
private Data.Database.Account.ReadInvoiceLineItem dbRead;
|
||||
private Logic.Log.LogEvent log = new Logic.Log.LogEvent();
|
||||
private Logic.Account.GetTaxCodeInfo getTaxCode;
|
||||
private Logic.Account.GetAccountCodeInfo getAccountCode;
|
||||
|
||||
public GetInvoiceLineItem()
|
||||
{
|
||||
CacheInnit();
|
||||
dbRead = new Data.Database.Account.ReadInvoiceLineItem();
|
||||
getAccountCode = new GetAccountCodeInfo();
|
||||
getTaxCode = new GetTaxCodeInfo();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create new 'default' line item code when a requested item code is not found.
|
||||
/// </summary>
|
||||
public bool InsertNewOnNoMatch { get; set; } = false;
|
||||
|
||||
public void CacheInnit()
|
||||
{
|
||||
cache = new Dictionary<string, Model.Account.InvoiceLineItem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefill cache in one SQL call, saves multiple SQL calls.
|
||||
/// </summary>
|
||||
/// <param name="itemCodeList">List of item codes to lookup from database</param>
|
||||
/// <param name="forceDbRead">Forces a database read (does not read cache)</param>
|
||||
public void CacheFill(List<string> itemCodeList, bool forceDbRead = false)
|
||||
{
|
||||
if (itemCodeList == null || !itemCodeList.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var itemCodeQueryList = new List<string>();
|
||||
foreach (var itemCode in itemCodeList)
|
||||
{
|
||||
if (forceDbRead)
|
||||
{
|
||||
cache.Remove(itemCode);
|
||||
itemCodeQueryList.Add(itemCode);
|
||||
}
|
||||
else if (!cache.ContainsKey(itemCode))
|
||||
{
|
||||
itemCodeQueryList.Add(itemCode);
|
||||
}
|
||||
}
|
||||
|
||||
// query database
|
||||
var resultList = dbRead.ByItemCode(itemCodeQueryList);
|
||||
|
||||
// get account & tax codes dictionaries
|
||||
var accountCodeDict = getAccountCode.ConvertToDictionary(getAccountCode.ByAccountCode(dbRead.AccountCodeList.Values.ToList()));
|
||||
var taxCodeDict = getTaxCode.ConvertToDictionary(getTaxCode.GetByTaxCode(dbRead.TaxCodeList.Values.ToList()));
|
||||
|
||||
// build final itemcode object and add to cache
|
||||
foreach (var result in resultList.Values)
|
||||
{
|
||||
if (dbRead.AccountCodeList.ContainsKey(result.ItemCode))
|
||||
{
|
||||
result.DefaultAccountCode = accountCodeDict[dbRead.AccountCodeList[result.ItemCode]];
|
||||
}
|
||||
|
||||
if (dbRead.TaxCodeList.ContainsKey(result.ItemCode))
|
||||
{
|
||||
result.DefaultTaxCode = taxCodeDict[dbRead.TaxCodeList[result.ItemCode]];
|
||||
}
|
||||
|
||||
cache.Add(result.ItemCode, result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new 'default' item code entry. The item code and title are set the same and will require updating.
|
||||
/// </summary>
|
||||
/// <param name="itemCode">Item code string</param>
|
||||
/// <returns></returns>
|
||||
public Model.Account.InvoiceLineItem CreateDefault(string itemCode)
|
||||
{
|
||||
new Data.Database.Account.CreateInvoiceLineItem().CreateDefault(itemCode);
|
||||
var item = dbRead.ByItemCode(itemCode);
|
||||
cache.Add(item.ItemCode, item);
|
||||
return item;
|
||||
}
|
||||
|
||||
public Model.Account.InvoiceLineItem ByItemCode(string itemCode, bool forceDbRead = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(itemCode))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
CacheFill(new List<string> { itemCode }, forceDbRead);
|
||||
|
||||
Model.Account.InvoiceLineItem item = null;
|
||||
if (cache.ContainsKey(itemCode))
|
||||
{
|
||||
item = cache[itemCode];
|
||||
|
||||
// check title
|
||||
if (!item.IsNewReviewRequired && item.Name == item.ItemCode)
|
||||
{
|
||||
throw new Exception
|
||||
("ItemCode found with the incomplete title. Update title and then try again.");
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
if (!InsertNewOnNoMatch)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CreateDefault(itemCode);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if ItemCode has Invoice line entry enabled
|
||||
/// </summary>
|
||||
/// <param name="itemCode">Item code</param>
|
||||
/// <param name="forceDbRead">Forces a database read (does not read cache)</param>
|
||||
/// <returns></returns>
|
||||
public bool InvoiceLineEntryEnabled(string itemCode, bool forceDbRead = false)
|
||||
{
|
||||
var item = ByItemCode(itemCode, forceDbRead);
|
||||
if (item == null)
|
||||
throw new Exception("Invalid item code");
|
||||
return item.InvoiceLineEntryEnabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if ItemCode is new (default) and a review id required
|
||||
/// </summary>
|
||||
/// <param name="itemCode">Item code</param>
|
||||
/// <param name="forceDbRead">Forces a database read (does not read cache)</param>
|
||||
/// <returns></returns>
|
||||
public bool IsNewReviewRequired(string itemCode, bool forceDbRead = false)
|
||||
{
|
||||
var item = ByItemCode(itemCode, forceDbRead);
|
||||
if (item == null)
|
||||
{
|
||||
throw new Exception("Invalid item code");
|
||||
}
|
||||
return item.IsNewReviewRequired;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,6 @@ namespace bnhtrade.Core.Logic.Account
|
||||
dbRead = new Data.Database.Account.ReadTaxCode();
|
||||
}
|
||||
|
||||
public List<Model.Account.TaxCodeInfo> GetAllActive()
|
||||
{
|
||||
return dbRead.GetAllActive();
|
||||
}
|
||||
|
||||
public List<Model.Account.TaxCodeInfo> GetByTaxCode(List<string> taxCodeList)
|
||||
{
|
||||
return dbRead.GetByTaxCode(taxCodeList);
|
||||
@@ -42,7 +37,7 @@ namespace bnhtrade.Core.Logic.Account
|
||||
/// Gets list of Tax Code Info for a given list of Sku Numbers
|
||||
/// </summary>
|
||||
/// <param name="skuNumberList">List of SKU numbers</param>
|
||||
/// <returns>Dictionary, key is SkuNumber and value is Tax Code Info</returns>
|
||||
/// <returns>Dictionary, key=SkuNumber and value=TaxCodeInfo</returns>
|
||||
public Dictionary<string, Model.Account.TaxCodeInfo> GetBySkuNumber(List<string> skuNumberList)
|
||||
{
|
||||
var returnList = new Dictionary<string, Model.Account.TaxCodeInfo>();
|
||||
|
||||
41
src/bnhtrade.Core/Logic/Account/InvoiceLineItemService.cs
Normal file
41
src/bnhtrade.Core/Logic/Account/InvoiceLineItemService.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Account
|
||||
{
|
||||
public class InvoiceLineItemService
|
||||
{
|
||||
private Logic.Log.LogEvent log = new Logic.Log.LogEvent();
|
||||
|
||||
/// <summary>
|
||||
/// Creates new 'default' item code entry. The item code and title are set, will require updating by user before use.
|
||||
/// </summary>
|
||||
/// <param name="itemCode">Item code string</param>
|
||||
/// <returns></returns>
|
||||
public Model.Account.InvoiceLineItem CreateNew(string itemCode)
|
||||
{
|
||||
new Data.Database.Account.CreateInvoiceLineItem().CreateDefault(itemCode);
|
||||
var item = new Data.Database.Account.ReadInvoiceLineItem().ByItemCode(new List<string> { itemCode });
|
||||
if (item == null || item.Count == 0)
|
||||
{
|
||||
log.LogError("InvoiceLineItemService.CreateNew", "Error creating new invoice line item in database for item code: " + itemCode);
|
||||
throw new Exception("Error creating new invoice line item in database");
|
||||
}
|
||||
return item[0];
|
||||
}
|
||||
|
||||
public Dictionary<string, Model.Account.InvoiceLineItem> GetLineItems(List<string> itemCodes)
|
||||
{
|
||||
var dbResult = new Data.Database.Account.ReadInvoiceLineItem().ByItemCode(itemCodes);
|
||||
var returnDict = new Dictionary<string, Model.Account.InvoiceLineItem>();
|
||||
foreach ( var item in dbResult)
|
||||
{
|
||||
returnDict.Add(item.Value.ItemCode, item.Value);
|
||||
}
|
||||
return returnDict;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,18 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Account
|
||||
{
|
||||
public class PurchaseInvoice : Core.Data.Database.Account.ReadPurchaseInvoice
|
||||
public class PurchaseInvoice
|
||||
{
|
||||
/// <summary>
|
||||
/// Get purchase invoices by id
|
||||
/// </summary>
|
||||
/// <param name="purchaseIdList">purchase id list</param>
|
||||
/// <returns>dictionary where key=id, value=purchaseinvoice</returns>
|
||||
public Dictionary<int, Model.Account.PurchaseInvoice> GetPurchaseInvoice(IEnumerable<int> purchaseIdList)
|
||||
{
|
||||
var dbRead = new Data.Database.Account.ReadPurchaseInvoice();
|
||||
dbRead.PurchaseInvoiceIdList = purchaseIdList;
|
||||
return dbRead.Read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,503 @@
|
||||
using Amazon.SQS.Model.Internal.MarshallTransformations;
|
||||
using bnhtrade.Core.Data.Database.UnitOfWork;
|
||||
using bnhtrade.Core.Model.Amazon;
|
||||
using bnhtrade.Core.Test.Export;
|
||||
using FikaAmazonAPI.ReportGeneration.ReportDataTable;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
using static bnhtrade.Core.Model.Import.AmazonSettlement;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Export.AccountInvoice
|
||||
{
|
||||
internal class AmazonSettlement : Data.Database.Connection
|
||||
{
|
||||
private readonly IUnitOfWork _providedUnitOfWork = null;
|
||||
private readonly bool _ownsUnitOfWork = false;
|
||||
|
||||
private Logic.Log.LogEvent _log = new Logic.Log.LogEvent();
|
||||
private List<string> _lineItemCodeList = new List<string>();
|
||||
private bool _settlementAmountIsTaxExclusive = false; // i.e. they're tax inclusive
|
||||
|
||||
public AmazonSettlement()
|
||||
{
|
||||
_ownsUnitOfWork = true;
|
||||
}
|
||||
|
||||
internal AmazonSettlement(IUnitOfWork unitOfWork)
|
||||
{
|
||||
_providedUnitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
|
||||
_ownsUnitOfWork = false;
|
||||
}
|
||||
|
||||
public string ErrorMessage { get; private set; }
|
||||
|
||||
private void Init()
|
||||
{
|
||||
ErrorMessage = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates invoices from Amazon settlement reports and saves the invoices to database Export Invoice table
|
||||
/// </summary>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public bool GenerateInvoicesForExportQueue(bool convertToGbp = true)
|
||||
{
|
||||
Init();
|
||||
_log.LogInformation("Starting processing of Amazon settlement data into export invoice table...");
|
||||
|
||||
// get list of unprocessed settlement reports to export
|
||||
var settlementList = GetListOfUnprocessedSettlementReports();
|
||||
if (settlementList == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// create list of settlement ids for later use
|
||||
var settlementIdList = new List<string>();
|
||||
foreach (var settlement in settlementList)
|
||||
{
|
||||
settlementIdList.Add(settlement.SettlementId);
|
||||
}
|
||||
|
||||
// create list of invoices from settlement reports
|
||||
var invoiceList = CreateInvoices(settlementList, convertToGbp);
|
||||
if (invoiceList == null || invoiceList.Any() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// add invoice to export queue and set settlements as processed
|
||||
Console.Write("\rWriting to database... ");
|
||||
using (UnitOfWork unitOfWork = new UnitOfWork())
|
||||
{
|
||||
try
|
||||
{
|
||||
var queueService = new Logic.Export.AccountInvoice.QueueService(unitOfWork);
|
||||
// add temp invoice numbers
|
||||
queueService.AddTempInvoiceNumber(invoiceList, true);
|
||||
|
||||
// write to the database (gets validated there)
|
||||
var queueInsertResult = queueService.Insert(invoiceList);
|
||||
if (queueService.ErrorMessageIsSet == false && queueInsertResult.Count() == invoiceList.Count())
|
||||
{
|
||||
// set settlements to isprocessed
|
||||
unitOfWork.ImportAmazonRepository.SetAmazonSettlementIsProcessed(settlementIdList, true);
|
||||
unitOfWork.Commit();
|
||||
}
|
||||
else
|
||||
{
|
||||
unitOfWork.Rollback();
|
||||
string error = queueService.ErrorMessage;
|
||||
ErrorMessage = "Cancelled processing of Amazon settlement data into export invoice table. " + error;
|
||||
_log.LogInformation("Cancelled processing of Amazon settlement data into export invoice table...");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string error = "Exeception caught while writing Amazon settlement invoices to DB. Check logs";
|
||||
ErrorMessage = "Cancelled processing of Amazon settlement data into export invoice table: " + error;
|
||||
_log.LogError(error, ex.Message);
|
||||
_log.LogInformation("Cancelled processing of Amazon settlement data into export invoice table...");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Console.Write("\r");
|
||||
_log.LogInformation("\rFinished processing of Amazon settlement data. " + invoiceList.Count() + " invoices created from " + settlementIdList.Count() + " Amazon settlement reports.");
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrives a list of unprocessed settlement reports from the database, checks for gaps in settlement periods, and validates
|
||||
/// the settlement data.
|
||||
/// </summary>
|
||||
/// <param name="newMarketplaceNameList">Import will fail on new marketplace, add here to bypass this check</param>
|
||||
/// <returns></returns>
|
||||
private List<Model.Import.AmazonSettlement> GetListOfUnprocessedSettlementReports()
|
||||
{
|
||||
List<Model.Import.AmazonSettlement> settlementList = null;
|
||||
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
|
||||
// get list of unprocssed settlement reports to export
|
||||
using (currentUow != null && _ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
settlementList = currentUow.ImportAmazonRepository.ReadAmazonSettlements(null, null, false).Values.ToList();
|
||||
|
||||
if (settlementList.Any() == false)
|
||||
{
|
||||
ErrorMessage = "No new settlement reports to process";
|
||||
return null;
|
||||
}
|
||||
|
||||
// test marketplace-name has been sucsessfully entered into settlement table --
|
||||
// as this is not supplied in the original report from Amazon and has to be inferred from settlement line data
|
||||
// this is not picked up in validate stage as null value is valid
|
||||
foreach (var settlement in settlementList)
|
||||
{
|
||||
if (settlement.MarketPlaceNameIsSet == false)
|
||||
{
|
||||
string error = "User action required: Enter market place name for amazon settlelment report id:" + settlement.SettlementId + ".";
|
||||
ErrorMessage = "Cancelled processing of Amazon settlement data into export invoice table: " + error;
|
||||
_log.LogError(
|
||||
error
|
||||
, "Unable to process settlement data from report '" + settlement.SettlementId +
|
||||
"'. Report header table requires a market place name, which is not supplied in original " +
|
||||
"report from Amazon. This is useually inferred from settlement lines. " +
|
||||
"However, in this case, it was not not possible. Manual edit/entry for database table required."
|
||||
);
|
||||
_log.LogInformation("Cancelled processing of Amazon settlement data into export invoice table...");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// check for time gaps between settlement periods
|
||||
settlementList = settlementList.OrderBy(x => x.MarketPlace).ThenBy(x => x.StartDate).ToList();
|
||||
for (var i = 0; i < settlementList.Count; i++)
|
||||
{
|
||||
// first marketplace of type in list? retrive the previously completed settlement for that marketplace to compare datetimes
|
||||
if (i == 0 || settlementList[i].MarketPlace != settlementList[i - 1].MarketPlace)
|
||||
{
|
||||
// get previously completed settlement for this marketplace
|
||||
var completedSettlement = currentUow.ImportAmazonRepository.ReadAmazonSettlements(
|
||||
null, new List<string> { settlementList[i].MarketPlace.GetMarketplaceUrl() }, true, true, 1);
|
||||
if (completedSettlement.Any())
|
||||
{
|
||||
if (completedSettlement.FirstOrDefault().Value.EndDate != settlementList[i].StartDate)
|
||||
{
|
||||
string error = (settlementList[i].StartDate - settlementList[i - 1].EndDate).Days + " day gap in "
|
||||
+ settlementList[i].MarketPlace.GetMarketplaceUrl() + " settlement data (" + settlementList[i - 1].EndDate.ToString("dd MMM yyyy")
|
||||
+ " to " + settlementList[i].StartDate.ToString("dd MMM yyyy") + "). Ensure all settlement reports have been imported.";
|
||||
ErrorMessage = error;
|
||||
_log.LogError("Cancelled processing of Amazon settlement data into invoice export queue: " + error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// first settlement for this marketplace, no previous settlement to compare against
|
||||
// continue on
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (settlementList[i - 1].EndDate != settlementList[i].StartDate)
|
||||
{
|
||||
string error = (settlementList[i].StartDate - settlementList[i - 1].EndDate).Days + " day gap in "
|
||||
+ settlementList[i].MarketPlace + " settlement data (" + settlementList[i - 1].EndDate.ToString("dd MMM yyyy")
|
||||
+ " to " + settlementList[i].StartDate.ToString("dd MMM yyyy") + "). Ensure all settlement reports have been imported.";
|
||||
ErrorMessage = error;
|
||||
_log.LogError("Cancelled processing of Amazon settlement data into invoice export queue: " + error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// validate settlelments
|
||||
var validate = new Logic.Validate.AmazonSettlement();
|
||||
foreach (var settlement in settlementList)
|
||||
{
|
||||
if (!validate.IsValid(settlement))
|
||||
{
|
||||
_log.LogError("Error procesing Amazon Settlement data for export.", validate.ValidationResultListToString());
|
||||
}
|
||||
}
|
||||
if (validate.IsValidResult == false)
|
||||
{
|
||||
string error = "Amazon settlements report returned from database failed validation. Check Logs";
|
||||
ErrorMessage = "Cancelled processing of Amazon settlement data into export invoice table: " + error;
|
||||
_log.LogInformation("Cancelled processing of Amazon settlement data into export invoice table...");
|
||||
return null;
|
||||
}
|
||||
|
||||
return settlementList;
|
||||
}
|
||||
|
||||
private string BuildLineItemCode(Dictionary<string, Model.Account.TaxCodeInfo> taxCodeBySkuNumer, string skuNumber, string transactionType, string amountType, string amountDescription)
|
||||
{
|
||||
// build the match string
|
||||
// NB special case for global accounting sale and refunds (also note Goodlwill is included) and sku's where tax is included
|
||||
string match01 = transactionType;
|
||||
string match02 = amountType;
|
||||
string match03 = amountDescription;
|
||||
string matchString = "<AmazonReport><SettlementReportLine><" + match01 + "><" + match02 + "><" + match03 + ">";
|
||||
|
||||
// add tax info if required
|
||||
if ((match01 == "Order" || match01 == "Refund")
|
||||
&& (match02 == "ItemPrice" || match02 == "Promotion" || match02 == "ItemWithheldTax"))
|
||||
{
|
||||
if (taxCodeBySkuNumer.ContainsKey(skuNumber))
|
||||
{
|
||||
matchString = matchString + "<TaxCode=" + taxCodeBySkuNumer[skuNumber].TaxCode + ">";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Sku#" + skuNumber + " tax info not found in dictionary list.");
|
||||
}
|
||||
}
|
||||
|
||||
// add to list of generated line item codes
|
||||
_lineItemCodeList.Add(matchString);
|
||||
|
||||
// return value
|
||||
return matchString;
|
||||
}
|
||||
|
||||
private List<Model.Account.SalesInvoice> CreateInvoices(List<Model.Import.AmazonSettlement> settlementList, bool convertToGbp = true)
|
||||
{
|
||||
// create list of settlement ids for later use
|
||||
var settlementIdList = new List<string>();
|
||||
foreach (var settlement in settlementList)
|
||||
{
|
||||
settlementIdList.Add(settlement.SettlementId);
|
||||
}
|
||||
|
||||
// get dictionary of sku-number to taxcodeId
|
||||
Console.Write("\rBuilding SKU list... ");
|
||||
var skuList = new List<string>();
|
||||
foreach (var settlement in settlementList)
|
||||
{
|
||||
if (settlement.SettlementLineListIsSet)
|
||||
{
|
||||
foreach (var line in settlement.SettlementLineList)
|
||||
{
|
||||
if (line.SkuIsSet
|
||||
&& !string.IsNullOrWhiteSpace(line.Sku))
|
||||
{
|
||||
skuList.Add(line.Sku);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var taxCodeBySkuNumerDict = new Logic.Account.GetTaxCodeInfo().GetBySkuNumber(skuList);
|
||||
|
||||
// loop through each settlement and build list of invoices to export
|
||||
Console.Write("\rBuilding invoices to export... ");
|
||||
var invoiceList = new List<Model.Account.SalesInvoice>();
|
||||
foreach (var settlement in settlementList)
|
||||
{
|
||||
// split settlement line list into months
|
||||
var monthList = settlement.SettlementLineList
|
||||
.GroupBy(x => new DateTime(x.PostDateTime.Year, x.PostDateTime.Month, 1, 0, 0, 0, x.PostDateTime.Kind));
|
||||
|
||||
int monthCount = 0;
|
||||
foreach (var month in monthList)
|
||||
{
|
||||
monthCount++;
|
||||
var itemCodeTotal = new Dictionary<string, decimal>();
|
||||
foreach (var line in month)
|
||||
{
|
||||
string itemCode = BuildLineItemCode(taxCodeBySkuNumerDict, line.Sku, line.TransactionType, line.AmountType, line.AmountDescription);
|
||||
if (itemCodeTotal.ContainsKey(itemCode))
|
||||
{
|
||||
itemCodeTotal[itemCode] += line.Amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemCodeTotal.Add(itemCode, line.Amount);
|
||||
}
|
||||
}
|
||||
|
||||
// create invoice, one for each month
|
||||
var invoice = new Model.Account.SalesInvoice(_settlementAmountIsTaxExclusive);
|
||||
|
||||
// create invoice lines forsy
|
||||
invoice.InvoiceLineList = new List<Model.Account.SalesInvoice.InvoiceLine>();
|
||||
decimal lineUnitAmountTotal = 0m;
|
||||
decimal lineTaxTotal = 0m;
|
||||
foreach (var item in itemCodeTotal)
|
||||
{
|
||||
var line = new Model.Account.SalesInvoice.InvoiceLine(invoice);
|
||||
line.ItemCode = item.Key;
|
||||
line.Quantity = 1;
|
||||
line.UnitAmount = item.Value;
|
||||
lineUnitAmountTotal += item.Value;
|
||||
lineTaxTotal += 0;
|
||||
invoice.InvoiceLineList.Add(line);
|
||||
}
|
||||
|
||||
invoice.ContactName = settlement.MarketPlace.GetMarketplaceUrl();
|
||||
invoice.InvoiceCurrencyCode = Enum.Parse<Model.Account.CurrencyCode>(settlement.CurrencyCode);
|
||||
if (monthList.Count() == 1 || monthList.Count() == monthCount)
|
||||
{ invoice.InvoiceDate = settlement.EndDate; }
|
||||
else
|
||||
{ invoice.InvoiceDate = new DateTime(month.Key.Year, month.Key.Month, 1, 0, 0, 0, DateTimeKind.Utc).AddMonths(1).AddDays(-1); }
|
||||
invoice.InvoiceDueDate = settlement.DepositDate;
|
||||
invoice.InvoiceReference = settlement.SettlementId;
|
||||
invoice.InvoiceTotalAmount = lineUnitAmountTotal + lineTaxTotal;
|
||||
if (invoice.InvoiceTotalAmount < 0) { invoice.IsCreditNote = true; }
|
||||
else { invoice.IsCreditNote = false; }
|
||||
|
||||
// invoice complete, add to list
|
||||
invoiceList.Add(invoice);
|
||||
}
|
||||
}
|
||||
|
||||
// sort list of invoices
|
||||
invoiceList = invoiceList.OrderBy(x => x.InvoiceReference).ThenBy(x => x.InvoiceDate).ToList();
|
||||
|
||||
// check invoice total against settlement totals
|
||||
var invoiceTotal = new Dictionary<string, decimal>();
|
||||
for (int i = 0; i < invoiceList.Count(); i++)
|
||||
{
|
||||
if (invoiceTotal.ContainsKey(invoiceList[i].InvoiceReference))
|
||||
{
|
||||
invoiceTotal[invoiceList[i].InvoiceReference] += invoiceList[i].InvoiceTotalAmount.GetValueOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
invoiceTotal.Add(invoiceList[i].InvoiceReference, invoiceList[i].InvoiceTotalAmount.GetValueOrDefault());
|
||||
}
|
||||
}
|
||||
foreach (var settlement in settlementList)
|
||||
{
|
||||
if (settlement.TotalAmount != invoiceTotal[settlement.SettlementId])
|
||||
{
|
||||
throw new Exception("invoice totals does not match settlement total.");
|
||||
}
|
||||
}
|
||||
if (settlementIdList.Count != invoiceTotal.Count())
|
||||
{
|
||||
string error = "Not all settlements have been transposed into invoices.";
|
||||
ErrorMessage = "Cancelled processing of Amazon settlement data into export invoice table: " + error;
|
||||
_log.LogError(error);
|
||||
_log.LogInformation("Cancelled processing of Amazon settlement data into export invoice table...");
|
||||
return null;
|
||||
}
|
||||
|
||||
// add invoice item code data to lines
|
||||
// also clean invoices of any disabled lines (remove lines and possibly invoices)
|
||||
var lineItemService = new Logic.Account.InvoiceLineItemService();
|
||||
var lineItemDict = lineItemService.GetLineItems(_lineItemCodeList);
|
||||
bool newTypeFound = false;
|
||||
string newTypeText = null;
|
||||
|
||||
for (int i = 0; i < invoiceList.Count(); i++)
|
||||
{
|
||||
for (int j = 0; j < invoiceList[i].InvoiceLineList.Count(); j++)
|
||||
{
|
||||
var itemCode = lineItemDict[invoiceList[i].InvoiceLineList[j].ItemCode];
|
||||
// flag new type and throw exception further on
|
||||
if (itemCode.IsNewReviewRequired)
|
||||
{
|
||||
newTypeFound = true;
|
||||
if (string.IsNullOrWhiteSpace(itemCode.ItemCode))
|
||||
{
|
||||
newTypeText = itemCode.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
newTypeText = itemCode.ItemCode;
|
||||
}
|
||||
}
|
||||
// clean invoices of any disabled lines (remove lines and possibly invoices)
|
||||
else if (itemCode.InvoiceLineEntryEnabled == false)
|
||||
{
|
||||
// remove line
|
||||
invoiceList[i].InvoiceTotalAmount =
|
||||
invoiceList[i].InvoiceTotalAmount
|
||||
- (invoiceList[i].InvoiceLineList[j].Quantity * invoiceList[i].InvoiceLineList[j].UnitAmount);
|
||||
invoiceList[i].InvoiceLineList.RemoveAt(j);
|
||||
j = j - 1;
|
||||
|
||||
// remove invoice?
|
||||
if (invoiceList[i].InvoiceLineList.Count == 0)
|
||||
{
|
||||
invoiceList.RemoveAt(i);
|
||||
if (i > 0)
|
||||
{
|
||||
i = i - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// get here add info to lines
|
||||
else
|
||||
{
|
||||
invoiceList[i].InvoiceLineList[j].Account = itemCode.DefaultAccountCode;
|
||||
invoiceList[i].InvoiceLineList[j].Description = itemCode.Name;
|
||||
invoiceList[i].InvoiceLineList[j].ItemCode = itemCode.ItemCode;
|
||||
invoiceList[i].InvoiceLineList[j].TaxCode = itemCode.DefaultTaxCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newTypeFound)
|
||||
{
|
||||
string error = "User action required: Parameters required for Invoice line item code '" + newTypeText + "'. Set in tblAccountInvoiceLineItem.";
|
||||
ErrorMessage = "Cancelled processing of Amazon settlement data into export invoice table: " + error;
|
||||
_log.LogError(error);
|
||||
_log.LogInformation("Cancelled processing of Amazon settlement data into export invoice table...");
|
||||
return null;
|
||||
}
|
||||
|
||||
// postfix invoices references that span multiple months with -n
|
||||
if (invoiceList.Count > 1)
|
||||
{
|
||||
string lastRef = invoiceList[0].InvoiceReference;
|
||||
int countRef = 1;
|
||||
for (int i = 1; i < invoiceList.Count(); i++)
|
||||
{
|
||||
if (invoiceList[i].InvoiceReference == lastRef)
|
||||
{
|
||||
if (countRef == 1)
|
||||
{
|
||||
invoiceList[i - 1].InvoiceReference = lastRef + "-" + countRef;
|
||||
}
|
||||
invoiceList[i].InvoiceReference = lastRef + "-" + (countRef += 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// shouldn't normally be more than 2 date ranges, log and move on.
|
||||
if (countRef > 2)
|
||||
{
|
||||
_log.LogError(
|
||||
countRef + " invoices where created from Amazon Settlement Id" + lastRef + "."
|
||||
, "Settlement period appears to span more 3 months or more. Whilst this is possible, it is unsual. Confirm his is correct.");
|
||||
}
|
||||
|
||||
lastRef = invoiceList[i].InvoiceReference;
|
||||
countRef = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for any non gbp invoices
|
||||
if (convertToGbp)
|
||||
{
|
||||
for (var i = 0; i < invoiceList.Count; i++)
|
||||
{
|
||||
if (invoiceList[i].InvoiceCurrencyCode != Model.Account.CurrencyCode.GBP)
|
||||
{
|
||||
// convert to gbp
|
||||
var invoice = invoiceList[i];
|
||||
var currencyService = new Logic.Account.CurrencyService();
|
||||
if (currencyService.ConvertInvoiceToGbp(invoice) == false)
|
||||
{
|
||||
ErrorMessage = "Cancelled processing of Amazon settlement data into export invoice table: Error converting invoice to GBP.";
|
||||
return null; // error message is set in currency service
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return invoiceList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Export.AccountInvoice
|
||||
{
|
||||
internal class InvoiceService
|
||||
{
|
||||
}
|
||||
}
|
||||
293
src/bnhtrade.Core/Logic/Export/AccountInvoice/QueueService.cs
Normal file
293
src/bnhtrade.Core/Logic/Export/AccountInvoice/QueueService.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
using bnhtrade.Core.Data.Database.UnitOfWork;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
using static bnhtrade.Core.Model.Account.Invoice;
|
||||
using static System.Formats.Asn1.AsnWriter;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Export.AccountInvoice
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Processes the Export Invoice table and exports to Xero
|
||||
/// </summary>
|
||||
public class QueueService
|
||||
{
|
||||
private Log.LogEvent _log = new Log.LogEvent();
|
||||
private IEnumerable<int> _exportSaleInvoiceIdList = new List<int>();
|
||||
private readonly IUnitOfWork _providedUnitOfWork = null;
|
||||
private readonly bool _ownsUnitOfWork = false;
|
||||
|
||||
public QueueService()
|
||||
{
|
||||
_ownsUnitOfWork = true;
|
||||
}
|
||||
|
||||
internal QueueService(IUnitOfWork unitOfWork)
|
||||
{
|
||||
_providedUnitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
|
||||
_ownsUnitOfWork = false;
|
||||
}
|
||||
|
||||
public string ErrorMessage { get; private set; } = null;
|
||||
|
||||
public bool ErrorMessageIsSet
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( ErrorMessage == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
ErrorMessage = null;
|
||||
}
|
||||
|
||||
public void ImportAll()
|
||||
{
|
||||
new Logic.Export.AccountInvoice.AmazonSettlement().GenerateInvoicesForExportQueue(true);
|
||||
}
|
||||
|
||||
public string GetNextTempInvoiceNumber()
|
||||
{
|
||||
Init();
|
||||
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
|
||||
string result = null;
|
||||
using (currentUow != null && _ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
result = "_tmp" + currentUow.SequenceGenerator.GetNext("ExportTempInvoiceNumber").ToString("00000000");
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow.Commit();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void AddTempInvoiceNumber(IEnumerable<Model.Account.IInvoice> invoiceList, bool overwriteExisting)
|
||||
{
|
||||
Init();
|
||||
|
||||
for (int i = 0; i < invoiceList.Count(); i++)
|
||||
{
|
||||
if (invoiceList.ElementAt(i).InvoiceNumber != null && overwriteExisting == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
invoiceList.ElementAt(i).InvoiceNumber = GetNextTempInvoiceNumber();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read invoices from datbase (with validation)
|
||||
/// </summary>
|
||||
/// <param name="invoiceIdList">list of invoice id to retrive</param>
|
||||
/// <returns>Dictionary where key=id, value=invoice-model</returns>
|
||||
/// <exception cref="Exception">Failed validation</exception>
|
||||
public Dictionary<int, Model.Account.SalesInvoice> ReadInvoiceById(IEnumerable<int> invoiceIdList)
|
||||
{
|
||||
Init();
|
||||
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
using (_ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
var returnList = currentUow.ExportInvoiceRepository.GetSalesInvoiceById(invoiceIdList);
|
||||
var validate = new Logic.Validate.Invoice();
|
||||
bool isValid = validate.IsValidExportInvoice(returnList.Values.ToList());
|
||||
|
||||
if (isValid == false)
|
||||
{
|
||||
ErrorMessage = "Reading invoices from database failed validation. See logs for further details.";
|
||||
_log.LogError("ErrorMessage", validate.ValidationResultListToString());
|
||||
throw new Exception(ErrorMessage);
|
||||
}
|
||||
|
||||
return returnList;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds sales invoice to database export table, ready for export
|
||||
/// </summary>
|
||||
/// <param name="invoiceList">List of sales invoices</param>
|
||||
/// <returns>Dictionary where key=invoice id, value=invoice model</returns>
|
||||
internal Dictionary<int, Model.Account.SalesInvoice> Insert(IEnumerable<Model.Account.SalesInvoice> invoiceList)
|
||||
{
|
||||
Init();
|
||||
|
||||
// validate the list of invoices
|
||||
var validateInvoice = new Validate.Invoice();
|
||||
validateInvoice.IsValidExportInvoice(invoiceList);
|
||||
if (validateInvoice.IsValidResult == false)
|
||||
{
|
||||
string error = "Sales invoice(s) failed validation.";
|
||||
_log.LogError(error, validateInvoice.ValidationResultListToString());
|
||||
ErrorMessage = error + " Check logs for further info";
|
||||
return null;
|
||||
}
|
||||
validateInvoice = null;
|
||||
|
||||
// save to database
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
|
||||
using (currentUow != null && _ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
var result = currentUow.ExportInvoiceRepository.InsertSalesInvoices(invoiceList);
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow.Commit();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets count of new invoices ready to export to external accounting software
|
||||
/// </summary>
|
||||
/// <returns>Count of new invoices as int</returns>
|
||||
public int Count(Model.Account.InvoiceType invoiceType)
|
||||
{
|
||||
Init();
|
||||
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
|
||||
using (currentUow != null && _ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
var result = currentUow.ExportInvoiceRepository.GetNewInvoiceNumbers(invoiceType).Count();
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow.Commit();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="firstInvoiceNumber"></param>
|
||||
public void ExportSalesInvoice(string filePath, int firstInvoiceNumber)
|
||||
{
|
||||
Init();
|
||||
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
|
||||
using (currentUow != null && _ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
var invoiceType = Model.Account.InvoiceType.Sale;
|
||||
|
||||
var idList = currentUow.ExportInvoiceRepository.GetNewInvoiceNumbers(invoiceType);
|
||||
_exportSaleInvoiceIdList = idList.Keys.ToList();
|
||||
var invoiceList = ReadInvoiceById(idList.Keys.ToList());
|
||||
|
||||
var exportToFile = new Data.Xero.SalesInvoice();
|
||||
exportToFile.ExportToCsv(invoiceList.Values.ToList(), firstInvoiceNumber, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this after ExportSalesInvoice() to mark exported invoices as complete
|
||||
/// </summary>
|
||||
/// <returns>number of invoices effected</returns>
|
||||
public int? ExportSalesInvoiceIsComplete()
|
||||
{
|
||||
Init();
|
||||
|
||||
if (_exportSaleInvoiceIdList.Any() == false)
|
||||
{
|
||||
ErrorMessage = "Nothing to set as complete, did you call the ExportSalesInvoice method first?";
|
||||
return null;
|
||||
}
|
||||
|
||||
var parameters = new Dictionary<int, bool>();
|
||||
foreach (var id in _exportSaleInvoiceIdList)
|
||||
{
|
||||
parameters.Add(id, true);
|
||||
}
|
||||
|
||||
// update database
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
|
||||
using (currentUow != null && _ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
int count = currentUow.ExportInvoiceRepository.SetInvoiceIsCompleteValue(parameters);
|
||||
if (_exportSaleInvoiceIdList.Count() == count)
|
||||
{
|
||||
currentUow.Commit();
|
||||
return count;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow.Rollback();
|
||||
ErrorMessage = "ExportSalesInvoiceIsComplete() Incorrect number of rows updated, changes rolled back.";
|
||||
_log.LogError(ErrorMessage);
|
||||
throw new Exception(ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Export
|
||||
{
|
||||
public class AmazonSettlement
|
||||
{
|
||||
private Logic.Log.LogEvent log = new Logic.Log.LogEvent();
|
||||
private List<string> lineItemCodeList = new List<string>();
|
||||
|
||||
public AmazonSettlement()
|
||||
{
|
||||
}
|
||||
|
||||
public void ToInvoice()
|
||||
{
|
||||
log.LogInformation("Starting processing of Amazon settlement data into export invoice table...");
|
||||
|
||||
// check settlement reports consistancy
|
||||
var consistencyCheck = new Data.Database.Consistency.ImportAmazonSettlement().PeriodDateGaps();
|
||||
if (consistencyCheck == false)
|
||||
{ return; }
|
||||
|
||||
// get list of unprocssed settlement reports to export
|
||||
var settlementData = new Data.Database.Import.AmazonSettlementRead();
|
||||
var settlementList = settlementData.AllUnprocessed();
|
||||
settlementData = null;
|
||||
|
||||
if (settlementList == null)
|
||||
{
|
||||
log.LogInformation("No new settlements to process, exiting import...");
|
||||
return;
|
||||
}
|
||||
|
||||
// create list of settlement ids
|
||||
var settlementIdList = new List<string>();
|
||||
for (int i = 0; i < settlementList.Count(); i++)
|
||||
{
|
||||
settlementIdList.Add(settlementList[i].SettlementId);
|
||||
}
|
||||
|
||||
// test marketplace-name has been sucsessfully entered into settlement table --
|
||||
// as this is not supplied in the original report from Amazon and has to be inferred from settlement line data
|
||||
// this is not picked up in validate stage as null value is valid
|
||||
for (int i = 0; i < settlementList.Count(); i++)
|
||||
{
|
||||
if (!settlementList[i].MarketPlaceNameIsSet)
|
||||
{
|
||||
log.LogError(
|
||||
"Action required: Enter market place name for settlelment report id " + settlementList[i].SettlementId + "."
|
||||
, "Unable to process settlement data from one settlement report '" + settlementList[i].SettlementId +
|
||||
"'. Report header table requires a market place name, which is not supplied in original " +
|
||||
"report from Amazon. This is useually inferred from settlement lines. " +
|
||||
"However, in this case, it was not not possible. Manual edit/entry for database table required."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// validate settlelments
|
||||
var validate = new Logic.Validate.AmazonSettlement();
|
||||
for (int i = 0; i < settlementList.Count(); i++)
|
||||
{
|
||||
if (!validate.IsValid(settlementList[i]))
|
||||
{
|
||||
log.LogError("Error procesing Amazon Settlement data for export.", validate.ValidationResultListToString());
|
||||
}
|
||||
}
|
||||
if (validate.IsValidResult == false) { return; }
|
||||
|
||||
// get dictionary of sku-number to taxcodeId
|
||||
Console.Write("\rBuilding SKU list... ");
|
||||
var skuList = new List<string>();
|
||||
foreach (var settlement in settlementList)
|
||||
{
|
||||
if (settlement.SettlementLineListIsSet)
|
||||
{
|
||||
foreach (var line in settlement.SettlementLineList)
|
||||
{
|
||||
if (line.SkuIsSet
|
||||
&& !string.IsNullOrWhiteSpace(line.Sku))
|
||||
{
|
||||
skuList.Add(line.Sku);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var taxCodeBySkuNumer = new Logic.Account.GetTaxCodeInfo().GetBySkuNumber(skuList);
|
||||
|
||||
// loop through each settlement and build list of invoices to export
|
||||
Console.Write("\rBuilding invoices to export... ");
|
||||
var invoiceList = new List<Model.Account.SalesInvoice>();
|
||||
for (int i = 0; i < settlementList.Count(); i++)
|
||||
{
|
||||
// split settlement line list into months
|
||||
// List<Model.Account.SalesInvoice.InvoiceLine>
|
||||
var monthList = settlementList[i].SettlementLineList
|
||||
.GroupBy(x => new DateTime(x.PostDateTime.Year, x.PostDateTime.Month, 1, 0, 0, 0, x.PostDateTime.Kind));
|
||||
//.GroupBy(x => string.Format("{0}-{1}", x.PostDateTime.Year, x.PostDateTime.Month));
|
||||
//.GroupBy(x => new { x.PostDateTime.Month, x.PostDateTime.Year });
|
||||
|
||||
int monthCount = 0;
|
||||
foreach (var month in monthList)
|
||||
{
|
||||
monthCount++;
|
||||
var itemCodeTotal = new Dictionary<string, decimal>();
|
||||
foreach (var line in month)
|
||||
{
|
||||
string itemCode = BuildLineItemCode(taxCodeBySkuNumer, line.Sku, line.TransactionType, line.AmountType, line.AmountDescription);
|
||||
if (itemCodeTotal.ContainsKey(itemCode))
|
||||
{
|
||||
itemCodeTotal[itemCode] += line.Amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemCodeTotal.Add(itemCode, line.Amount);
|
||||
}
|
||||
}
|
||||
|
||||
// create invoice, one for each month
|
||||
var invoice = new Model.Account.SalesInvoice();
|
||||
|
||||
// create invoice lines forsy
|
||||
invoice.InvoiceLineList = new List<Model.Account.SalesInvoice.InvoiceLine>();
|
||||
decimal lineNetTotal = 0m;
|
||||
decimal lineTaxTotal = 0m;
|
||||
foreach (var item in itemCodeTotal)
|
||||
{
|
||||
var line = new Model.Account.SalesInvoice.InvoiceLine(invoice.UnitAmountIsTaxExclusive);
|
||||
line.ItemCode = item.Key;
|
||||
line.Quantity = 1;
|
||||
line.UnitAmount = item.Value;
|
||||
lineNetTotal += item.Value;
|
||||
lineTaxTotal += 0;
|
||||
invoice.InvoiceLineList.Add(line);
|
||||
}
|
||||
|
||||
invoice.ContactName = settlementList[i].MarketPlaceName;
|
||||
invoice.InvoiceCurrencyCode = settlementList[i].CurrencyCode;
|
||||
if (monthList.Count() == 1 || monthList.Count() == monthCount)
|
||||
{ invoice.InvoiceDate = settlementList[i].EndDate; }
|
||||
else
|
||||
{ invoice.InvoiceDate = new DateTime(month.Key.Year, month.Key.Month, 1, 0, 0, 0, DateTimeKind.Utc).AddMonths(1).AddDays(-1); }
|
||||
invoice.InvoiceDueDate = settlementList[i].DepositDate;
|
||||
invoice.InvoiceReference = settlementList[i].SettlementId;
|
||||
invoice.InvoiceTotalAmount = lineNetTotal + lineTaxTotal;
|
||||
if (invoice.InvoiceTotalAmount < 0) { invoice.IsCreditNote = true; }
|
||||
else { invoice.IsCreditNote = false; }
|
||||
|
||||
// invoice complete, add to list
|
||||
invoiceList.Add(invoice);
|
||||
}
|
||||
}
|
||||
|
||||
// sort list of invoices
|
||||
invoiceList = invoiceList.OrderBy(x => x.InvoiceReference).ThenBy(x => x.InvoiceDate).ToList();
|
||||
|
||||
// check invoice total against settlement totals
|
||||
var invoiceTotal = new Dictionary<string, decimal>();
|
||||
for (int i = 0; i < invoiceList.Count(); i++)
|
||||
{
|
||||
if (invoiceTotal.ContainsKey(invoiceList[i].InvoiceReference))
|
||||
{
|
||||
invoiceTotal[invoiceList[i].InvoiceReference] += invoiceList[i].InvoiceTotalAmount.GetValueOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
invoiceTotal.Add(invoiceList[i].InvoiceReference, invoiceList[i].InvoiceTotalAmount.GetValueOrDefault());
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < settlementList.Count(); i++)
|
||||
{
|
||||
if (settlementList[i].TotalAmount != invoiceTotal[settlementList[i].SettlementId])
|
||||
{
|
||||
throw new Exception("invoice totals does not match settlement total.");
|
||||
}
|
||||
}
|
||||
if (settlementIdList.Count != invoiceTotal.Count())
|
||||
{
|
||||
log.LogError("Stopping Settlement export. Not all settlements have been transposed into invoices.");
|
||||
return;
|
||||
}
|
||||
|
||||
// add invoice item code data to lines
|
||||
// also clean invoices of any disabled lines (remove lines and possibly invoices)
|
||||
var getLineItemInfo = new Logic.Account.GetInvoiceLineItem();
|
||||
getLineItemInfo.InsertNewOnNoMatch = true;
|
||||
getLineItemInfo.CacheFill(lineItemCodeList);
|
||||
bool newTypeFound = false;
|
||||
string newTypeText = null;
|
||||
|
||||
for (int i = 0; i < invoiceList.Count(); i++)
|
||||
{
|
||||
for (int j = 0; j < invoiceList[i].InvoiceLineList.Count(); j++)
|
||||
{
|
||||
var itemCode = getLineItemInfo.ByItemCode(invoiceList[i].InvoiceLineList[j].ItemCode);
|
||||
// error! itemCode should never be null
|
||||
if (itemCode == null)
|
||||
{
|
||||
throw new Exception("Item code is null");
|
||||
}
|
||||
// flag new type and throw exception further on
|
||||
else if (itemCode.IsNewReviewRequired)
|
||||
{
|
||||
newTypeFound = true;
|
||||
if (string.IsNullOrWhiteSpace(itemCode.ItemCode))
|
||||
{
|
||||
newTypeText = itemCode.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
newTypeText = itemCode.ItemCode;
|
||||
}
|
||||
}
|
||||
// clean invoices of any disabled lines (remove lines and possibly invoices)
|
||||
else if (itemCode.InvoiceLineEntryEnabled == false)
|
||||
{
|
||||
// remove line
|
||||
invoiceList[i].InvoiceTotalAmount = invoiceList[i].InvoiceTotalAmount - invoiceList[i].InvoiceLineList[j].LineTotalAmount;
|
||||
invoiceList[i].InvoiceLineList.RemoveAt(j);
|
||||
j = j - 1;
|
||||
|
||||
// remove invoice?
|
||||
if (invoiceList[i].InvoiceLineList.Count == 0)
|
||||
{
|
||||
invoiceList.RemoveAt(i);
|
||||
if (i > 0)
|
||||
{
|
||||
i = i - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// get here add info to lines
|
||||
else
|
||||
{
|
||||
invoiceList[i].InvoiceLineList[j].AccountCode = itemCode.DefaultAccountCode;
|
||||
invoiceList[i].InvoiceLineList[j].Description = itemCode.Name;
|
||||
invoiceList[i].InvoiceLineList[j].ItemCode = itemCode.ItemCode;
|
||||
invoiceList[i].InvoiceLineList[j].TaxCode = itemCode.DefaultTaxCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newTypeFound)
|
||||
{
|
||||
if (newTypeFound)
|
||||
{
|
||||
throw new Exception("Parameters required for Invoice line item code '"+ newTypeText + "'. Set in tblAccountInvoiceLineItem.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// postfix invoices references that span multiple months with -n
|
||||
if (invoiceList.Count > 1)
|
||||
{
|
||||
string lastRef = invoiceList[0].InvoiceReference;
|
||||
int countRef = 1;
|
||||
for (int i = 1; i < invoiceList.Count(); i++)
|
||||
{
|
||||
if (invoiceList[i].InvoiceReference == lastRef)
|
||||
{
|
||||
if (countRef == 1)
|
||||
{
|
||||
invoiceList[i - 1].InvoiceReference = lastRef + "-" + countRef;
|
||||
}
|
||||
invoiceList[i].InvoiceReference = lastRef + "-" + (countRef += 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// shouldn't normally be more than 2 date ranges, log and move on.
|
||||
if (countRef > 2)
|
||||
{
|
||||
log.LogError(
|
||||
countRef + " invoices where created from Amazon Settlement Id" + lastRef + "."
|
||||
, "Settlement period appears to span more 3 months or more. Whilst this is possible, it is unsual. Confirm his is correct.");
|
||||
}
|
||||
|
||||
lastRef = invoiceList[i].InvoiceReference;
|
||||
countRef = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.Write("\rWriting to database... ");
|
||||
using (TransactionScope scope = new TransactionScope())
|
||||
{
|
||||
try
|
||||
{
|
||||
var saveInv = new Logic.Export.SalesInvoice();
|
||||
// add temp invoice numbers
|
||||
saveInv.AddTempInvoiceNumber(invoiceList, true);
|
||||
|
||||
// write to the database (gets validated there)
|
||||
saveInv.SaveSalesInvoice(invoiceList);
|
||||
|
||||
// set settlements to isprocessed
|
||||
new Data.Database.Import.AmazonSettlementUpdate().SetIsProcessedTrue(settlementIdList);
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
scope.Dispose();
|
||||
log.LogError("Exeception caught while writing Amazon settlement invoices to DB. Changes were rolled back."
|
||||
, ex.Message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Console.Write("\r");
|
||||
log.LogInformation("\rFinished processing of Amazon settlement data. " + invoiceList.Count() + " invoices created from " + settlementIdList.Count() + " Amazon settlement reports.");
|
||||
}
|
||||
|
||||
private string BuildLineItemCode(Dictionary<string, Model.Account.TaxCodeInfo> taxCodeBySkuNumer, string skuNumber, string transactionType, string amountType, string amountDescription)
|
||||
{
|
||||
// build the match string
|
||||
// NB special case for global accounting sale and refunds (also note Goodlwill is included) and sku's where tax is included
|
||||
string match01 = transactionType;
|
||||
string match02 = amountType;
|
||||
string match03 = amountDescription;
|
||||
string matchString = "<AmazonReport><SettlementReportLine><" + match01 + "><" + match02 + "><" + match03 + ">";
|
||||
|
||||
// add tax info if required
|
||||
if ((match01 == "Order" || match01 == "Refund")
|
||||
&& (match02 == "ItemPrice" || match02 == "Promotion" || match02 == "ItemWithheldTax"))
|
||||
{
|
||||
if (taxCodeBySkuNumer.ContainsKey(skuNumber))
|
||||
{
|
||||
matchString = matchString + "<TaxCode=" + taxCodeBySkuNumer[skuNumber].TaxCode + ">";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Sku#" + skuNumber + " tax info not found in dictionary list.");
|
||||
}
|
||||
}
|
||||
|
||||
// add to list of generated line item codes
|
||||
lineItemCodeList.Add(matchString);
|
||||
|
||||
// return value
|
||||
return matchString;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ namespace bnhtrade.Core.Logic.Export
|
||||
int queueId = 0;
|
||||
using (var scope = new TransactionScope())
|
||||
{
|
||||
queueId = new Data.Database.Export.CreateAmazonFeedSubmission().Execute(feedType, fileInfo);
|
||||
queueId = new Data.Database.Export.AmazonFeedSubmissionInsert().Execute(feedType, fileInfo);
|
||||
|
||||
// validate the result
|
||||
var validateResults = new List<ValidationResult>();
|
||||
@@ -89,7 +89,7 @@ namespace bnhtrade.Core.Logic.Export
|
||||
}
|
||||
|
||||
// set the amazon feed Id
|
||||
var dbUpdate = new Data.Database.Export.UpdateAmazonFeedSubmission();
|
||||
var dbUpdate = new Data.Database.Export.AmazonFeedSubmissionUpdate();
|
||||
dbUpdate.AddAmazonFeedId(queueId, feedSubmission.FeedSubmissionId);
|
||||
|
||||
// update progress info
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Export
|
||||
{
|
||||
public class SalesInvoice
|
||||
{
|
||||
private Logic.Log.LogEvent log = new Log.LogEvent();
|
||||
|
||||
public SalesInvoice()
|
||||
{
|
||||
}
|
||||
|
||||
public string GetNextTempInvoiceNumber()
|
||||
{
|
||||
var sequence = new Data.Database.Programmability.Sequence();
|
||||
return "_tmp" + sequence.GetNext("ExportTempInvoiceNumber").ToString("00000000");
|
||||
}
|
||||
|
||||
public void AddTempInvoiceNumber(IEnumerable<Model.Account.IInvoice> invoiceList, bool overwriteExisting)
|
||||
{
|
||||
for (int i = 0; i < invoiceList.Count(); i++)
|
||||
{
|
||||
if (invoiceList.ElementAt(i).InvoiceNumber != null && overwriteExisting == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
invoiceList.ElementAt(i).InvoiceNumber = GetNextTempInvoiceNumber();
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveSalesInvoice(List<Model.Account.SalesInvoice> invoiceList)
|
||||
{
|
||||
// validate the list of invoices
|
||||
var validateInvoice = new Logic.Validate.SalesInvoice();
|
||||
validateInvoice.IsValidExportInvoice(invoiceList);
|
||||
if (validateInvoice.IsValidResult == false)
|
||||
{
|
||||
log.LogError("Invalid Sales invoice(s) found. See extended info.", validateInvoice.ValidationResultListToString());
|
||||
return;
|
||||
}
|
||||
validateInvoice = null;
|
||||
|
||||
// save to database
|
||||
new Data.Database.Export.CreateSalesInvoice().Execute(invoiceList);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +1,97 @@
|
||||
using bnhtrade.Core.Data.Amazon.Report;
|
||||
using bnhtrade.Core.Data.Database.UnitOfWork;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Import
|
||||
{
|
||||
public class AmazonSettlement
|
||||
{
|
||||
private readonly IUnitOfWork _providedUnitOfWork = null;
|
||||
private readonly bool _ownsUnitOfWork = false;
|
||||
private Data.Amazon.Report.SettlementReport amazonReport;
|
||||
private Logic.Log.LogEvent log = new Log.LogEvent();
|
||||
|
||||
|
||||
public AmazonSettlement()
|
||||
{
|
||||
amazonReport = new Data.Amazon.Report.SettlementReport();
|
||||
_ownsUnitOfWork = true;
|
||||
}
|
||||
|
||||
internal AmazonSettlement(IUnitOfWork unitOfWork)
|
||||
{
|
||||
_providedUnitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
|
||||
_ownsUnitOfWork = false;
|
||||
}
|
||||
|
||||
public void SyncDatabase()
|
||||
{
|
||||
string operation = "Import Amazon Settlement Reports";
|
||||
|
||||
log.LogInformation("Started '" + operation + "' operation.");
|
||||
|
||||
// get avaiable reports from amazon api
|
||||
var spapiReportIdList = amazonReport.ListAvaliableReports();
|
||||
int reportCount = spapiReportIdList.Count();
|
||||
|
||||
if (reportCount == 0)
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
log.LogInformation("Exiting '" + operation + "' operation. No settlement reports availble on Amazon SP-API.");
|
||||
return;
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
|
||||
// query db and remove reports that have already been imported
|
||||
var dbReportList = new Data.Database.Import.AmazonSettlementHeaderRead().BySpapiReportId(spapiReportIdList);
|
||||
foreach (var dbReport in dbReportList)
|
||||
|
||||
|
||||
using (currentUow != null && _ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
if (dbReport.SpapiReportIdIsSet)
|
||||
// get avaiable reports from amazon api
|
||||
var spapiReportIdList = amazonReport.ListAvaliableReports();
|
||||
int reportCount = spapiReportIdList.Count();
|
||||
|
||||
if (reportCount == 0)
|
||||
{
|
||||
for (int i = 0; i < spapiReportIdList.Count; i++)
|
||||
log.LogInformation("Exiting '" + operation + "' operation. No settlement reports availble on Amazon SP-API.");
|
||||
return;
|
||||
}
|
||||
|
||||
// query db and remove reports that have already been imported
|
||||
var dbReportList = currentUow.ImportAmazonRepository.ReadAmazonSettlementHeaderInfoBySpapiReportId(spapiReportIdList);
|
||||
foreach (var dbReport in dbReportList)
|
||||
{
|
||||
if (dbReport.SpapiReportIdIsSet)
|
||||
{
|
||||
if (spapiReportIdList[i] == dbReport.SpapiReportId)
|
||||
for (int i = 0; i < spapiReportIdList.Count; i++)
|
||||
{
|
||||
spapiReportIdList.RemoveAt(i);
|
||||
i--;
|
||||
break;
|
||||
if (spapiReportIdList[i] == dbReport.SpapiReportId)
|
||||
{
|
||||
spapiReportIdList.RemoveAt(i);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!spapiReportIdList.Any())
|
||||
{
|
||||
log.LogInformation("Exiting '" + operation + "' operation. No new reports to import (" + reportCount + " avaibale).");
|
||||
if (!spapiReportIdList.Any())
|
||||
{
|
||||
log.LogInformation("Exiting '" + operation + "' operation. No new reports to import (" + reportCount + " avaibale).");
|
||||
return;
|
||||
}
|
||||
|
||||
// import into database
|
||||
for (int i = 0; i < spapiReportIdList.Count(); i++)
|
||||
{
|
||||
UI.Console.WriteLine("Importing settlement report " + (i + 1) + " of " + spapiReportIdList.Count() + " (ReportID:" + spapiReportIdList[i] + ").");
|
||||
var filePath = amazonReport.GetReportFile(spapiReportIdList[i]);
|
||||
bool ack = currentUow.ImportAmazonRepository.CreateAmazonSettlements(filePath, spapiReportIdList[i]);
|
||||
log.LogInformation("Settlment Report imported (ReportID:" + spapiReportIdList[i] + ").");
|
||||
}
|
||||
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow.Commit();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// import into database
|
||||
var dbInsert = new Data.Database.Import.AmazonSettlementInsert();
|
||||
for (int i = 0; i < spapiReportIdList.Count(); i++)
|
||||
{
|
||||
UI.Console.WriteLine("Importing settlement report " + (i + 1) + " of " + spapiReportIdList.Count() + " (ReportID:" + spapiReportIdList[i] + ").");
|
||||
var filePath = amazonReport.GetReportFile(spapiReportIdList[i]);
|
||||
bool ack = dbInsert.ByFlatFile(filePath, spapiReportIdList[i]);
|
||||
log.LogInformation("Settlment Report imported (ReportID:" + spapiReportIdList[i] + ").");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace bnhtrade.Core.Logic.Utilities
|
||||
/// <param name="inputList"></param>
|
||||
/// <param name="includeWhiteSpace"></param>
|
||||
/// <returns>Unique list</returns>
|
||||
public List<string> UniqueList(List<string> inputList, bool removeNullorWhitespace = true)
|
||||
public List<string> UniqueList(IEnumerable<string> inputList, bool removeNullorWhitespace = true)
|
||||
{
|
||||
List<string> outputList = new List<string>();
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@ namespace bnhtrade.Core.Logic.Utilities
|
||||
{
|
||||
log.LogInformation("Nightly scheduled tasks started.");
|
||||
|
||||
var export = new bnhtrade.Core.Logic.Export.AmazonSettlement();
|
||||
|
||||
bool stockUpdate = false;
|
||||
bool exchangeRate = false;
|
||||
bool accountProcess = false;
|
||||
@@ -28,9 +26,9 @@ 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 (stockUpdate == false) { stockUpdate = true; new Logic.Import.Amazon().SyncAllWithDatabase(); ; }
|
||||
if (exchangeRate == false) { exchangeRate = true; new Logic.Account.CurrencyService().UpdateHmrcExchageRates(); }
|
||||
if (accountProcess == false) { accountProcess = true; new Logic.Export.AccountInvoice.QueueService().ImportAll(); }
|
||||
|
||||
// if (stockProcess == false) { stockProcess = true; stock.ProcessFbaStockImportData(); }
|
||||
// ^^^^^^ best to process manually, case, fba inventory recepts, if a correction is made days later (ie -1) the already incorrect value
|
||||
|
||||
23
src/bnhtrade.Core/Logic/Validate/AccountInvoiceLineItem.cs
Normal file
23
src/bnhtrade.Core/Logic/Validate/AccountInvoiceLineItem.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using bnhtrade.Core.Model.Account;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Validate
|
||||
{
|
||||
public class AccountInvoiceLineItem : Validate
|
||||
{
|
||||
public bool IsValid(Model.Account.InvoiceLineItem invoiceLineItem)
|
||||
{
|
||||
return base.IsValid(invoiceLineItem);
|
||||
}
|
||||
|
||||
public bool IsValid(List<Model.Account.InvoiceLineItem> invoiceLineItems)
|
||||
{
|
||||
return base.IsValid(invoiceLineItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using bnhtrade.Core.Model.Amazon;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -56,7 +57,7 @@ namespace bnhtrade.Core.Logic.Validate
|
||||
{
|
||||
if (ValidateMarketPlaceName) { ValidationResultAdd("MarketPlaceName is a required value."); }
|
||||
}
|
||||
else { IsValidMarketPlaceName(settlementList[i].MarketPlaceName); }
|
||||
else { IsValidMarketPlaceName(settlementList[i].MarketPlace.GetMarketplaceUrl()); }
|
||||
|
||||
if (!settlementList[i].SettlementIdIsSet) { ValidationResultAdd("SettlementId is a required value."); }
|
||||
else { IsValidSettlementId(settlementList[i].SettlementId); }
|
||||
@@ -315,23 +316,6 @@ namespace bnhtrade.Core.Logic.Validate
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public bool IsValidTransactionType(string transactionType)
|
||||
{
|
||||
if (!stringCheck.MaxLength(transactionType, 50))
|
||||
|
||||
@@ -6,27 +6,22 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Logic.Validate
|
||||
{
|
||||
public class SalesInvoice : Logic.Validate.Validate
|
||||
public class Invoice : Logic.Validate.Validate
|
||||
{
|
||||
public SalesInvoice()
|
||||
public Invoice()
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsValidExportInvoice(IEnumerable<Model.Account.Invoice> invoiceList)
|
||||
{
|
||||
if (!IsValid(invoiceList))
|
||||
bool valid = IsValid(invoiceList);
|
||||
if (valid == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var invoice in invoiceList)
|
||||
{
|
||||
if (invoice.UnitAmountIsTaxExclusive == false)
|
||||
{
|
||||
ValidationResultAdd("Tax inclusive line unit amounts are not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var invoiceLine in invoice.InvoiceLineList)
|
||||
{
|
||||
if (invoiceLine.Quantity != 1)
|
||||
53
src/bnhtrade.Core/Logic/_boilerplate/UnitOfWorkPattern.cs
Normal file
53
src/bnhtrade.Core/Logic/_boilerplate/UnitOfWorkPattern.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using bnhtrade.Core.Data.Database.UnitOfWork;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Logic._BoilerPlate
|
||||
{
|
||||
internal class UnitOfWorkPattern
|
||||
{
|
||||
private readonly IUnitOfWork _providedUnitOfWork = null;
|
||||
private readonly bool _ownsUnitOfWork = false;
|
||||
|
||||
public UnitOfWorkPattern()
|
||||
{
|
||||
_ownsUnitOfWork = true;
|
||||
}
|
||||
|
||||
internal UnitOfWorkPattern(IUnitOfWork unitOfWork)
|
||||
{
|
||||
_providedUnitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
|
||||
_ownsUnitOfWork = false;
|
||||
}
|
||||
|
||||
public void LogicThatUsesService()
|
||||
{
|
||||
IUnitOfWork currentUow = null;
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow = new UnitOfWork();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentUow = _providedUnitOfWork;
|
||||
}
|
||||
|
||||
using (currentUow != null && _ownsUnitOfWork ? currentUow : null)
|
||||
{
|
||||
|
||||
//
|
||||
// Perform operations using currentUow, e.g. repository calls
|
||||
//
|
||||
|
||||
if (_ownsUnitOfWork)
|
||||
{
|
||||
currentUow.Commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,20 @@ namespace bnhtrade.Core.Model.Account
|
||||
{
|
||||
public class CurrencyExchangeRate
|
||||
{
|
||||
public CurrencyExchangeRate(CurrencyCode currencyCode, int exchangeRateSource, DateTime dateTimeStartUtc, DateTime dateTimeEndUtc)
|
||||
public CurrencyExchangeRate(CurrencyCode currencyCode, int exchangeRateSource, decimal currencyUnitsPerGbp, DateTime dateTimeStartUtc, DateTime dateTimeEndUtc)
|
||||
{
|
||||
this.CurrencyCode = currencyCode;
|
||||
this.ExchangeRateSource = exchangeRateSource;
|
||||
|
||||
if (currencyUnitsPerGbp <= 0)
|
||||
{
|
||||
throw new ArgumentException("Currency units per GBP must be greater than zero.");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.CurrencyUnitsPerGbp = currencyUnitsPerGbp;
|
||||
}
|
||||
|
||||
if (dateTimeEndUtc > dateTimeStartUtc)
|
||||
{
|
||||
this.DateTimeStartUtc = dateTimeStartUtc;
|
||||
@@ -50,6 +59,10 @@ namespace bnhtrade.Core.Model.Account
|
||||
|
||||
public DateTime DateTimeEndUtc { get; private set; }
|
||||
|
||||
public decimal ConvertToGbp(decimal amountToConvert)
|
||||
{
|
||||
return amountToConvert / CurrencyUnitsPerGbp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a given datetime falls within the the exchange rate period
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using FikaAmazonAPI.AmazonSpApiSDK.Models.FulfillmentInbound;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
@@ -28,41 +29,33 @@ namespace bnhtrade.Core.Model.Account
|
||||
|
||||
decimal? UnitAmount { get; set; }
|
||||
|
||||
Model.Account.Account AccountCode { get; set; }
|
||||
Model.Account.Account Account { get; set; }
|
||||
|
||||
Model.Account.TaxCodeInfo TaxCode { get; set; }
|
||||
|
||||
decimal? TaxAmountAdjust { get; set; }
|
||||
decimal TaxAmountAdjust { get; set; }
|
||||
|
||||
decimal? TaxAmount { get; }
|
||||
decimal? TaxAmountTotal { get; }
|
||||
|
||||
decimal LineTotalAmount { get; }
|
||||
}
|
||||
|
||||
|
||||
public abstract class Invoice : InvoiceHeader, IInvoice
|
||||
public abstract class Invoice : InvoiceHeader, IInvoice, IValidatableObject
|
||||
{
|
||||
private bool unitAmountIsTaxExclusive = false;
|
||||
protected Invoice(Model.Account.InvoiceType invoiceType, bool invoiceLineUnitAmountIsTaxExclusive) : base (invoiceType)
|
||||
{
|
||||
// force the UnitAmountIsTaxExclusive to be set at invoice creation to avoid issues,
|
||||
InvoiceLineUnitAmountIsTaxExclusive = invoiceLineUnitAmountIsTaxExclusive;
|
||||
}
|
||||
|
||||
public decimal InvoiceNetAmount { get; }
|
||||
|
||||
public decimal InvoiceTaxAmount { get; }
|
||||
|
||||
public bool UnitAmountIsTaxExclusive
|
||||
{
|
||||
get { return unitAmountIsTaxExclusive; }
|
||||
set
|
||||
{
|
||||
unitAmountIsTaxExclusive = value;
|
||||
if (InvoiceLineList != null)
|
||||
{
|
||||
for (int i = 0; i < InvoiceLineList.Count; i++)
|
||||
{
|
||||
InvoiceLineList[i].UnitAmountIsTaxExclusive = unitAmountIsTaxExclusive;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Invoice line unit amount is tax exclusive
|
||||
/// </summary>
|
||||
public bool InvoiceLineUnitAmountIsTaxExclusive { get; set; }
|
||||
|
||||
public List<InvoiceLine> InvoiceLineList { get; set; } = new List<InvoiceLine>();
|
||||
|
||||
@@ -75,16 +68,18 @@ namespace bnhtrade.Core.Model.Account
|
||||
}
|
||||
}
|
||||
|
||||
public class InvoiceLine : IInvoiceLine, IValidatableObject
|
||||
public class InvoiceLine : IInvoiceLine, IValidatableObject
|
||||
{
|
||||
private Invoice parentInvoice;
|
||||
private decimal? unitAmount;
|
||||
private Model.Account.TaxCodeInfo taxCode;
|
||||
private decimal? taxAmountAdjust;
|
||||
private decimal taxAmountAdjust;
|
||||
private decimal? quantity;
|
||||
|
||||
public InvoiceLine(bool unitAmountIsTaxExclusive)
|
||||
public InvoiceLine(Invoice parentInvoice)
|
||||
{
|
||||
UnitAmountIsTaxExclusive = unitAmountIsTaxExclusive;
|
||||
if (parentInvoice == null) { throw new ArgumentNullException(nameof(parentInvoice), "Parent invoice cannot be null"); }
|
||||
this.parentInvoice = parentInvoice;
|
||||
}
|
||||
|
||||
public string ItemCode { get; set; }
|
||||
@@ -92,158 +87,333 @@ namespace bnhtrade.Core.Model.Account
|
||||
public string Description { get; set; }
|
||||
|
||||
[Required(), Range(0, 9999999.99)]
|
||||
public decimal? Quantity
|
||||
public decimal? Quantity
|
||||
{
|
||||
get { return quantity; }
|
||||
set
|
||||
set
|
||||
{
|
||||
if (value == null) { quantity = null; }
|
||||
else { quantity = decimal.Round((decimal)value, 4, MidpointRounding.AwayFromZero); }
|
||||
else { quantity = decimal.Round(value.GetValueOrDefault(), 4, MidpointRounding.AwayFromZero); }
|
||||
}
|
||||
}
|
||||
|
||||
[Required()]
|
||||
public decimal? UnitAmount
|
||||
{
|
||||
public decimal? UnitAmount
|
||||
{
|
||||
get { return unitAmount; }
|
||||
set
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value == null) { unitAmount = null; }
|
||||
else { unitAmount = decimal.Round(value.GetValueOrDefault(), 4, MidpointRounding.AwayFromZero); }
|
||||
}
|
||||
}
|
||||
|
||||
public bool UnitAmountIsTaxExclusive { get; set; }
|
||||
|
||||
[Required()]
|
||||
public Model.Account.Account AccountCode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
public bool UnitAmountIsTaxExclusive
|
||||
{
|
||||
get
|
||||
{
|
||||
return parentInvoice.InvoiceLineUnitAmountIsTaxExclusive;
|
||||
}
|
||||
}
|
||||
|
||||
[Required()]
|
||||
public Model.Account.TaxCodeInfo TaxCode
|
||||
{
|
||||
public Model.Account.Account Account
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Required()]
|
||||
public Model.Account.TaxCodeInfo TaxCode
|
||||
{
|
||||
get { return taxCode; }
|
||||
set
|
||||
{
|
||||
if (value == null || TaxCode == null || value.TaxCode != taxCode.TaxCode)
|
||||
{
|
||||
TaxAmountAdjust = 0;
|
||||
if (value == null || TaxCode == null || value.TaxCode != taxCode.TaxCode)
|
||||
{
|
||||
TaxAmountAdjust = 0;
|
||||
}
|
||||
|
||||
taxCode = value;
|
||||
}
|
||||
}
|
||||
|
||||
[Required()]
|
||||
/// <summary>
|
||||
/// Tax amount calculated from line amount and the taxcode rate (before any adjustments)
|
||||
/// </summary>
|
||||
public decimal? TaxAmount
|
||||
{
|
||||
get
|
||||
get
|
||||
{
|
||||
return TaxAmountAdjust + GetCalculatedTax();
|
||||
decimal calculatedTax = 0;
|
||||
if (CanCalculateLineTotalAmount)
|
||||
{
|
||||
if (UnitAmountIsTaxExclusive)
|
||||
{
|
||||
calculatedTax = ((decimal)Quantity * (decimal)UnitAmount) * (TaxCode.TaxRate / 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
calculatedTax = ((decimal)Quantity * (decimal)UnitAmount) * (TaxCode.TaxRate / (100 + TaxCode.TaxRate));
|
||||
}
|
||||
return RoundAmount(calculatedTax);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public decimal? TaxAmountAdjust
|
||||
/// <summary>
|
||||
/// Will adjust the calculated the tax total amount for the line
|
||||
/// </summary>
|
||||
public decimal TaxAmountAdjust
|
||||
{
|
||||
get { return taxAmountAdjust; }
|
||||
set
|
||||
set { taxAmountAdjust = RoundAmount(value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The calculated total tax amount for the line (including any adjustments)
|
||||
/// </summary>
|
||||
[Required()]
|
||||
public decimal? TaxAmountTotal
|
||||
{
|
||||
get
|
||||
{
|
||||
if (value == null) { taxAmountAdjust = null; }
|
||||
else { taxAmountAdjust = decimal.Round((decimal)value, 2, MidpointRounding.AwayFromZero); }
|
||||
if (CanCalculateLineTotalAmount)
|
||||
{
|
||||
return taxAmountAdjust + TaxAmount.GetValueOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public decimal LineTotalAmount
|
||||
{
|
||||
get
|
||||
get
|
||||
{
|
||||
if (CanCalculateLine())
|
||||
if (CanCalculateLineTotalAmount)
|
||||
{
|
||||
return ((decimal)Quantity * (decimal)UnitAmount) + (decimal)TaxAmount;
|
||||
}
|
||||
else { return 0; }
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanCalculateLine()
|
||||
{
|
||||
if (Quantity == null || UnitAmount == null || TaxCode == null) { return false; }
|
||||
else { return true; }
|
||||
}
|
||||
|
||||
private decimal GetCalculatedTax()
|
||||
{
|
||||
decimal calculatedTax = 0;
|
||||
if (CanCalculateLine())
|
||||
{
|
||||
if (UnitAmountIsTaxExclusive)
|
||||
{
|
||||
calculatedTax = ((decimal)Quantity * (decimal)UnitAmount) * (TaxCode.TaxRate / 100);
|
||||
return ((decimal)Quantity * (decimal)UnitAmount) + (decimal)TaxAmountTotal;
|
||||
}
|
||||
else
|
||||
{
|
||||
calculatedTax = ((decimal)Quantity * (decimal)UnitAmount) * (TaxCode.TaxRate / (100 + TaxCode.TaxRate));
|
||||
throw new InvalidOperationException("Cannot calculate line amount, ensure te Quantity, UnitAmount and TaxCode info is set.");
|
||||
}
|
||||
return decimal.Round(calculatedTax, 2, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTaxAmountAdjust(decimal lineTaxAmount)
|
||||
public bool CanCalculateLineTotalAmount
|
||||
{
|
||||
decimal adjustedAmount = lineTaxAmount - GetCalculatedTax();
|
||||
if (adjustedAmount == 0) { TaxAmountAdjust = null; }
|
||||
else { TaxAmountAdjust = adjustedAmount; }
|
||||
get
|
||||
{
|
||||
if (Quantity == null || UnitAmount == null || TaxCode == null) { return false; }
|
||||
else { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will calculate and update the tax adjustment property from a given tax amount total
|
||||
/// </summary>
|
||||
/// <param name="taxAmountTotal">The required tax amount total</param>
|
||||
public void SetTaxAdjustmentByTaxTotal(decimal taxAmountTotal)
|
||||
{
|
||||
if (CanCalculateLineTotalAmount)
|
||||
{
|
||||
TaxAmountAdjust = taxAmountTotal - TaxAmount.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the unit amount based on the specified line total amount.
|
||||
/// </summary>
|
||||
/// <remarks>This method calculates the unit amount for the current item using the
|
||||
/// provided line total amount. The calculation considers whether the unit amount is tax-exclusive or
|
||||
/// tax-inclusive, as well as the associated tax rate and quantity. If the quantity is not set, it defaults
|
||||
/// to 1.</remarks>
|
||||
/// <param name="lineTotalAmount">The total amount for the line item. Must be a whole number to two decimal places.</param>
|
||||
/// <exception cref="ArgumentException">Thrown if <paramref name="lineTotalAmount"/> is not a whole number to two decimal places.</exception>
|
||||
/// <exception cref="InvalidOperationException">Thrown if the <c>TaxCode</c> is null or the quantity is not set.</exception>
|
||||
public void SetUnitAmountByLineTotal(decimal lineTotalAmount)
|
||||
{
|
||||
//checks
|
||||
if (lineTotalAmount * 100m != Math.Truncate(lineTotalAmount * 100m))
|
||||
{
|
||||
throw new ArgumentException("Line total amount must be a whole number to two decimal places", nameof(lineTotalAmount));
|
||||
}
|
||||
if (TaxCode == null)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot set unit amount, ensure the Quantity is set.");
|
||||
}
|
||||
else if (Quantity == null)
|
||||
{
|
||||
Quantity = 1;
|
||||
}
|
||||
|
||||
decimal taxRate = (TaxCode.TaxRate / 100); // convert to decimal rate
|
||||
|
||||
if (UnitAmountIsTaxExclusive)
|
||||
{
|
||||
// set unit amount to net
|
||||
UnitAmount = (lineTotalAmount - TaxAmountAdjust) / (Quantity.Value * taxRate + Quantity.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// set unit amount to gross
|
||||
UnitAmount = (lineTotalAmount - TaxAmountAdjust) / Quantity.Value;
|
||||
}
|
||||
|
||||
if (LineTotalAmount != lineTotalAmount)
|
||||
{
|
||||
throw new InvalidOperationException("SetUnitAmountByLineTotal() has a bug, check code");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds decimals to two decimal places inline with Xero specs
|
||||
/// </summary>
|
||||
/// <param name="amount">The amount to round</param>
|
||||
/// <returns>Rounded amount to two decimal places</returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
private decimal RoundAmount(decimal amount)
|
||||
{
|
||||
return decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
public bool ParentInvoiceCheck(Invoice invoice)
|
||||
{
|
||||
if (invoice == null) { return false; }
|
||||
return Object.ReferenceEquals(invoice, parentInvoice);
|
||||
}
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
var resultList = new List<ValidationResult>();
|
||||
|
||||
if (TaxAmount > LineTotalAmount / 2)
|
||||
if (CanCalculateLineTotalAmount == false)
|
||||
{
|
||||
resultList.Add(new ValidationResult("Line tax amount is greater than net amount"));
|
||||
resultList.Add(new ValidationResult("Cannot calulate line amount, data missing"));
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ConvertToLineUnitAmountIsTaxExclusive()
|
||||
{
|
||||
if (InvoiceLineUnitAmountIsTaxExclusive)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// test whether there's enough info to do the convertion
|
||||
foreach (var line in this.InvoiceLineList)
|
||||
{
|
||||
if (line.CanCalculateLineTotalAmount == false)
|
||||
{
|
||||
// can't do it return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// do the convertion
|
||||
InvoiceLineUnitAmountIsTaxExclusive = true;
|
||||
foreach (var line in this.InvoiceLineList)
|
||||
{
|
||||
decimal lineTotal = line.LineTotalAmount;
|
||||
decimal taxTotal = line.TaxAmountTotal.Value;
|
||||
|
||||
// set unit amount to net and update
|
||||
line.UnitAmount = ((line.Quantity.Value * line.UnitAmount.Value) - line.TaxAmountTotal.Value) / line.Quantity.Value;
|
||||
line.SetTaxAdjustmentByTaxTotal(taxTotal);
|
||||
|
||||
//fail safe
|
||||
if (line.LineTotalAmount != lineTotal)
|
||||
{
|
||||
throw new Exception("ConvertToLineUnitAmountIsTaxExclusive() has a bug, check code");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ConvertToLineUnitAmountIsTaxInclusive()
|
||||
{
|
||||
if (!InvoiceLineUnitAmountIsTaxExclusive)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// test whether there's enough info to do the convertion
|
||||
foreach (var line in this.InvoiceLineList)
|
||||
{
|
||||
if (line.CanCalculateLineTotalAmount == false)
|
||||
{
|
||||
// can't do it return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// do the convertion
|
||||
InvoiceLineUnitAmountIsTaxExclusive = false;
|
||||
foreach (var line in this.InvoiceLineList)
|
||||
{
|
||||
decimal lineTotal = line.LineTotalAmount;
|
||||
decimal taxTotal = line.TaxAmountTotal.Value;
|
||||
|
||||
// set unit amount to gross and update
|
||||
line.UnitAmount = (line.UnitAmount / line.Quantity) + (line.TaxAmountTotal / line.Quantity);
|
||||
line.SetTaxAdjustmentByTaxTotal(taxTotal);
|
||||
|
||||
//fail safe
|
||||
if (line.LineTotalAmount != lineTotal)
|
||||
{
|
||||
throw new Exception("ConvertToLineUnitAmountIsTaxExclusive() has a bug, check code");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public decimal GetCalculatedInvoiceTotal()
|
||||
{
|
||||
decimal lineTotal = 0;
|
||||
foreach (var line in InvoiceLineList)
|
||||
{
|
||||
lineTotal += line.LineTotalAmount;
|
||||
}
|
||||
return lineTotal;
|
||||
}
|
||||
|
||||
public new IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
var subResult = base.Validate(validationContext);
|
||||
var resultList = new List<ValidationResult>();
|
||||
resultList.AddRange(subResult);
|
||||
|
||||
// add results from invoice header
|
||||
resultList.AddRange(base.Validate(validationContext));
|
||||
|
||||
// loop though lines and check sum totals
|
||||
// loop though invoice lines and add results from there
|
||||
if (!InvoiceLineListIsSet)
|
||||
{
|
||||
resultList.Add(new ValidationResult("No lines set on Invoice"));
|
||||
resultList.Add(new ValidationResult("No lines set on Invoice (InvoiceNumber:" + InvoiceNumber + ")"));
|
||||
}
|
||||
else
|
||||
foreach (var line in InvoiceLineList)
|
||||
{
|
||||
decimal lineTotal = 0;
|
||||
foreach (var line in InvoiceLineList)
|
||||
if (line.ParentInvoiceCheck(this) == false)
|
||||
{
|
||||
lineTotal += line.LineTotalAmount;
|
||||
|
||||
if (UnitAmountIsTaxExclusive != line.UnitAmountIsTaxExclusive)
|
||||
{
|
||||
resultList.Add(new ValidationResult("Invalid UnitAmountIsTaxExclusive"));
|
||||
}
|
||||
resultList.Add(new ValidationResult("Incorrect parent invoice set on invoice line"));
|
||||
}
|
||||
resultList.AddRange(line.Validate(validationContext));
|
||||
}
|
||||
|
||||
// check totals
|
||||
if (InvoiceTotalAmount != lineTotal)
|
||||
{ resultList.Add(new ValidationResult("Invoice line total does not equal invoice total")); }
|
||||
// check invoice total against calculated total from invoice lines
|
||||
decimal calculatedInvoiceTotal = GetCalculatedInvoiceTotal(); // don't know why this needs to be here, but it won't pickup a failed validation otherwise
|
||||
if (InvoiceTotalAmount != calculatedInvoiceTotal)
|
||||
{
|
||||
resultList.Add(new ValidationResult("Invoice line total does not equal invoice total (InvoiceNumber:" + InvoiceNumber +")"));
|
||||
}
|
||||
|
||||
return resultList;
|
||||
|
||||
@@ -9,11 +9,13 @@ namespace bnhtrade.Core.Model.Account
|
||||
{
|
||||
public interface IInvoiceHeader
|
||||
{
|
||||
Model.Account.InvoiceType InvoiceType { get; }
|
||||
|
||||
string ContactName { get; set; }
|
||||
|
||||
decimal? InvoiceTotalAmount { get; set; }
|
||||
|
||||
string InvoiceCurrencyCode { get; set; }
|
||||
Model.Account.CurrencyCode InvoiceCurrencyCode { get; set; }
|
||||
|
||||
DateTime? InvoiceDate { get; set; }
|
||||
|
||||
@@ -30,23 +32,27 @@ namespace bnhtrade.Core.Model.Account
|
||||
{
|
||||
private decimal? invoiceAmount;
|
||||
|
||||
public InvoiceHeader()
|
||||
public InvoiceHeader(Model.Account.InvoiceType invoiceType)
|
||||
{
|
||||
this.InvoiceType = invoiceType;
|
||||
IsCreditNote = false;
|
||||
}
|
||||
|
||||
[Required()]
|
||||
public Model.Account.InvoiceType InvoiceType { get; private set; }
|
||||
|
||||
[Required()]
|
||||
public string ContactName { get; set; }
|
||||
|
||||
[Required()]
|
||||
public Model.Account.CurrencyCode InvoiceCurrencyCode { get; set; }
|
||||
|
||||
[Required()]
|
||||
public DateTime? InvoiceDate { get; set; }
|
||||
|
||||
[Required()]
|
||||
public DateTime? InvoiceDueDate { get; set; }
|
||||
|
||||
[Required(), MinLength(3), MaxLength(3)]
|
||||
public string InvoiceCurrencyCode { get; set; }
|
||||
|
||||
[Required()]
|
||||
public string InvoiceNumber { get; set; }
|
||||
|
||||
@@ -55,7 +61,7 @@ namespace bnhtrade.Core.Model.Account
|
||||
[Required()]
|
||||
public decimal? InvoiceTotalAmount
|
||||
{
|
||||
get { return (decimal)invoiceAmount; }
|
||||
get { return invoiceAmount.GetValueOrDefault(); }
|
||||
set
|
||||
{
|
||||
if (value == null) { invoiceAmount = null; }
|
||||
|
||||
@@ -43,14 +43,12 @@ namespace bnhtrade.Core.Model.Account
|
||||
set;
|
||||
}
|
||||
|
||||
[Required()]
|
||||
public Model.Account.Account DefaultAccountCode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Required()]
|
||||
public Model.Account.TaxCodeInfo DefaultTaxCode
|
||||
{
|
||||
get;
|
||||
@@ -61,7 +59,27 @@ namespace bnhtrade.Core.Model.Account
|
||||
{
|
||||
var resultList = new List<ValidationResult>();
|
||||
|
||||
resultList.AddRange(DefaultTaxCode.Validate(validationContext));
|
||||
if (InvoiceLineEntryEnabled)
|
||||
{
|
||||
if (DefaultAccountCode == null)
|
||||
{
|
||||
resultList.Add(new ValidationResult("DefaultAccountCode is required when InvoiceLineEntryEnabled is true."));
|
||||
}
|
||||
if (DefaultTaxCode == null)
|
||||
{
|
||||
resultList.Add(new ValidationResult("DefaultTaxCode is required when InvoiceLineEntryEnabled is true."));
|
||||
}
|
||||
}
|
||||
|
||||
if (DefaultAccountCode != null)
|
||||
{
|
||||
resultList.AddRange(DefaultTaxCode.Validate(validationContext));
|
||||
}
|
||||
|
||||
if (DefaultTaxCode != null)
|
||||
{
|
||||
resultList.AddRange(DefaultTaxCode.Validate(validationContext));
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
|
||||
14
src/bnhtrade.Core/Model/Account/InvoiceType.cs
Normal file
14
src/bnhtrade.Core/Model/Account/InvoiceType.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Model.Account
|
||||
{
|
||||
public enum InvoiceType
|
||||
{
|
||||
Purchase = 1,
|
||||
Sale = 2
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,11 @@ namespace bnhtrade.Core.Model.Account
|
||||
{
|
||||
public class PurchaseInvoice
|
||||
{
|
||||
// the purchase/sales invoice models are fragmented. The sales invoice inherits from an abstract invoice class. However, this purchase invoice
|
||||
// model doesn't, it was setup in the early development to mirror an Amazon report.
|
||||
// At some point I would like to correct this, i.e. have the purchase invoice model inherit from the abstract invoice class.
|
||||
// this will take some unpicking
|
||||
|
||||
public int PurchaseID { get; set; }
|
||||
|
||||
public int PurchaseNumber { get; set; }
|
||||
|
||||
@@ -8,6 +8,12 @@ namespace bnhtrade.Core.Model.Account
|
||||
{
|
||||
public class SalesInvoice : Invoice
|
||||
{
|
||||
|
||||
public SalesInvoice(bool invoiceLineUnitAmountIsTaxExclusive) : base(Model.Account.InvoiceType.Sale, invoiceLineUnitAmountIsTaxExclusive)
|
||||
{
|
||||
// the purchase/sales invoice models are fragmented. This sales invoice inherits from an abstract invoice. However, the purchase invoice
|
||||
// model doesn't, it was setup in the early development to mirror an Amazon report.
|
||||
// At some point I would like to correct this, i.e. have the purchase invoice model inherit from the abstract invoice class.
|
||||
// this will take some unpicking
|
||||
}
|
||||
}
|
||||
}
|
||||
66
src/bnhtrade.Core/Model/Amazon/MarketPlaceEnum.cs
Normal file
66
src/bnhtrade.Core/Model/Amazon/MarketPlaceEnum.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Model.Amazon
|
||||
{
|
||||
public enum MarketPlaceEnum
|
||||
{
|
||||
NONE = 0,
|
||||
AmazonUK = 1,
|
||||
AmazonDE = 2,
|
||||
AmazonES = 3,
|
||||
AmazonFR = 4,
|
||||
AmazonIT = 5,
|
||||
AmazonBE = 6,
|
||||
AmazonIE = 7,
|
||||
AmazonNL = 8,
|
||||
AmazonPL = 9,
|
||||
AmazonSE = 10
|
||||
}
|
||||
|
||||
public static class MarketPlaceEnumExtensions
|
||||
{
|
||||
public static string GetMarketplaceUrl(this MarketPlaceEnum marketPlace)
|
||||
{
|
||||
return marketPlace switch
|
||||
{
|
||||
MarketPlaceEnum.AmazonUK => "Amazon.co.uk",
|
||||
MarketPlaceEnum.AmazonDE => "Amazon.de",
|
||||
MarketPlaceEnum.AmazonES => "Amazon.es",
|
||||
MarketPlaceEnum.AmazonFR => "Amazon.fr",
|
||||
MarketPlaceEnum.AmazonIT => "Amazon.it",
|
||||
MarketPlaceEnum.AmazonBE => "Amazon.com.be",
|
||||
MarketPlaceEnum.AmazonIE => "Amazon.ie",
|
||||
MarketPlaceEnum.AmazonNL => "Amazon.nl",
|
||||
MarketPlaceEnum.AmazonPL => "Amazon.pl",
|
||||
MarketPlaceEnum.AmazonSE => "Amazon.se",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(marketPlace), marketPlace, null)
|
||||
};
|
||||
}
|
||||
|
||||
public static MarketPlaceEnum FromMarketplaceUrl(string url)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
throw new ArgumentNullException(nameof(url));
|
||||
|
||||
return url.Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"amazon.co.uk" => MarketPlaceEnum.AmazonUK,
|
||||
"amazon.de" => MarketPlaceEnum.AmazonDE,
|
||||
"amazon.es" => MarketPlaceEnum.AmazonES,
|
||||
"amazon.fr" => MarketPlaceEnum.AmazonFR,
|
||||
"amazon.it" => MarketPlaceEnum.AmazonIT,
|
||||
"amazon.com.be" => MarketPlaceEnum.AmazonBE,
|
||||
"amazon.ie" => MarketPlaceEnum.AmazonIE,
|
||||
"amazon.nl" => MarketPlaceEnum.AmazonNL,
|
||||
"amazon.pl" => MarketPlaceEnum.AmazonPL,
|
||||
"amazon.se" => MarketPlaceEnum.AmazonSE,
|
||||
_ => throw new ArgumentException($"Unknown marketplace URL: {url}", nameof(url))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/bnhtrade.Core/Model/Export/AccountInvoiceType.cs
Normal file
18
src/bnhtrade.Core/Model/Export/AccountInvoiceType.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bnhtrade.Core.Model.Export
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Sale or purchase invoice, id matches the database id
|
||||
/// </summary>
|
||||
public enum AccountInvoiceType
|
||||
{
|
||||
Purchase = 1,
|
||||
Sale = 2
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using bnhtrade.Core.Model.Amazon;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
@@ -9,7 +10,6 @@ namespace bnhtrade.Core.Model.Import
|
||||
{
|
||||
public class AmazonSettlementHeader
|
||||
{
|
||||
private string marketplaceName = null;
|
||||
private string settlementId = null;
|
||||
private string currencyCode = null;
|
||||
private DateTime? startDate = null;
|
||||
@@ -26,7 +26,7 @@ namespace bnhtrade.Core.Model.Import
|
||||
|
||||
public AmazonSettlementHeader(AmazonSettlementHeader toCopy)
|
||||
{
|
||||
this.marketplaceName = toCopy.MarketPlaceName;
|
||||
this.MarketPlace = toCopy.MarketPlace;
|
||||
this.settlementId = toCopy.SettlementId;
|
||||
this.currencyCode = toCopy.CurrencyCode;
|
||||
if (toCopy.StartDateIsSet) { this.startDate = toCopy.StartDate; }
|
||||
@@ -37,15 +37,21 @@ namespace bnhtrade.Core.Model.Import
|
||||
if (toCopy.SpapiReportIdIsSet) { this.spapiReportId = toCopy.SpapiReportId; }
|
||||
}
|
||||
|
||||
public string MarketPlaceName
|
||||
{
|
||||
get { return marketplaceName; }
|
||||
set { this.marketplaceName = value; }
|
||||
}
|
||||
public Model.Amazon.MarketPlaceEnum MarketPlace { get; set; }
|
||||
|
||||
public bool MarketPlaceNameIsSet
|
||||
{
|
||||
get { return marketplaceName != null; }
|
||||
get
|
||||
{
|
||||
if (MarketPlace == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SettlementId
|
||||
|
||||
@@ -19,15 +19,13 @@ namespace bnhtrade.Core.Test.Account
|
||||
|
||||
public void UpdateHmrcExchageRates()
|
||||
{
|
||||
var logic = new bnhtrade.Core.Logic.Account.Currency();
|
||||
var logic = new bnhtrade.Core.Logic.Account.CurrencyService();
|
||||
logic.UpdateHmrcExchageRates();
|
||||
}
|
||||
|
||||
public void PurchaseInvoice()
|
||||
{
|
||||
var read = new Data.Database.Account.ReadPurchaseInvoice();
|
||||
read.PurchaseInvoiceIdList = new List<int> { 14718, 100, 101, 102, 300, 400, 1200, 2734, 6339, 9999 }; // 10 in total
|
||||
var result = read.Read();
|
||||
var result = new bnhtrade.Core.Logic.Account.PurchaseInvoice().GetPurchaseInvoice( new List<int> { 14718, 100, 101, 102, 300, 400, 1200, 2734, 6339, 9999 }); // 10 in total
|
||||
}
|
||||
|
||||
public void PurchaseInvoiceLine()
|
||||
|
||||
@@ -15,8 +15,8 @@ namespace bnhtrade.Core.Test
|
||||
}
|
||||
private void AmazonSettlement()
|
||||
{
|
||||
var instance = new Core.Logic.Export.AmazonSettlement();
|
||||
instance.ToInvoice();
|
||||
var instance = new Core.Logic.Export.AccountInvoice.AmazonSettlement();
|
||||
instance.GenerateInvoicesForExportQueue(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,9 @@ namespace bnhtrade.Core.Test.Export
|
||||
{
|
||||
public class Export
|
||||
{
|
||||
private string sqlConnectionString;
|
||||
public Export(string sqlConnectionString)
|
||||
public Export()
|
||||
{
|
||||
this.sqlConnectionString = sqlConnectionString;
|
||||
|
||||
UpdateStatusInfo();
|
||||
DeleteExportInvoice();
|
||||
}
|
||||
|
||||
private void SubmitAmazonInventoryLoader()
|
||||
@@ -39,5 +36,33 @@ namespace bnhtrade.Core.Test.Export
|
||||
{
|
||||
//new Core.Logic.Export.AmazonSubmitFileStatus(sqlConnectionString).UpdateStatusInfo();
|
||||
}
|
||||
|
||||
private void AccountInvoiceToXero()
|
||||
{
|
||||
var unitOfWork = new bnhtrade.Core.Data.Database.UnitOfWork.UnitOfWork();
|
||||
var list = unitOfWork.ExportInvoiceRepository.GetSalesInvoiceById(new List<int> { 550, 555 });
|
||||
}
|
||||
|
||||
private void ExportSalesInvoiceQueue()
|
||||
{
|
||||
var invoice = new Model.Account.SalesInvoice(true);
|
||||
invoice.InvoiceLineList = new List<Model.Account.Invoice.InvoiceLine>();
|
||||
|
||||
string path = @"%USERPROFILE%\Downloads\TEST.CSV";
|
||||
var queueService = new bnhtrade.Core.Logic.Export.AccountInvoice.QueueService();
|
||||
queueService.ImportAll();
|
||||
queueService.ExportSalesInvoice(path, 1234);
|
||||
var return1 = queueService.ExportSalesInvoiceIsComplete();
|
||||
}
|
||||
private void DeleteExportInvoice()
|
||||
{
|
||||
//var uow = new bnhtrade.Core.Data.Database.UnitOfWork.UnitOfWork();
|
||||
using (var uow = new bnhtrade.Core.Data.Database.UnitOfWork.UnitOfWork())
|
||||
{
|
||||
int dfkjl = uow.ExportInvoiceRepository.DeleteInvoice(new List<int> { 576, 577, 584 });
|
||||
int lskdjflsd = uow.ImportAmazonRepository.SetAmazonSettlementIsProcessed(new List<string> { "24684639382f", "14720584692f", "14386695522f" }, false);
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace bnhtrade.Core.Test.Import
|
||||
public void ReadSettlementFromDatabase()
|
||||
{
|
||||
|
||||
var instance = new Data.Database.Import.AmazonSettlementRead();
|
||||
var answer = instance.BySettlementId("11796400482");
|
||||
//var instance = new Data.Database.Import.AmazonSettlementRead();
|
||||
//var answer = instance.BySettlementId("11796400482");
|
||||
}
|
||||
|
||||
public void GetFromSPAPI()
|
||||
@@ -35,7 +35,7 @@ namespace bnhtrade.Core.Test.Import
|
||||
public void GetSettlementReport()
|
||||
{
|
||||
var setList = new List<string> { "15734813312", "14473886262", "13984682252" };
|
||||
var result = new Data.Database.Import.AmazonSettlementRead().BySettlementIdList(setList);
|
||||
//var result = new Data.Database.Import.AmazonSettlementRead().BySettlementId(setList);
|
||||
}
|
||||
|
||||
public void FBAInventory()
|
||||
|
||||
@@ -11,13 +11,24 @@ namespace bnhtrade.Core.Test.Logic
|
||||
public Logic()
|
||||
{
|
||||
// method you want to start here
|
||||
UpdateXeroWithAmzonSettlementData();
|
||||
UpdateExportInvoiceQueue();
|
||||
|
||||
}
|
||||
public void UpdateXeroWithAmzonSettlementData()
|
||||
{
|
||||
var instance = new bnhtrade.Core.Logic.Export.AmazonSettlement();
|
||||
instance.ToInvoice();
|
||||
var instance = new bnhtrade.Core.Logic.Export.AccountInvoice.AmazonSettlement();
|
||||
instance.GenerateInvoicesForExportQueue(true);
|
||||
}
|
||||
public void ReadAmazonSettlement()
|
||||
{
|
||||
using (var uow = new Data.Database.UnitOfWork.UnitOfWork())
|
||||
{
|
||||
var list = uow.ImportAmazonRepository.ReadAmazonSettlements(marketPlaceNameList: new List<string> { "Amazon.fr" }, returnTop: 3);
|
||||
}
|
||||
}
|
||||
public void UpdateExportInvoiceQueue()
|
||||
{
|
||||
new Core.Logic.Export.AccountInvoice.QueueService().ImportAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -27,13 +27,14 @@
|
||||
<None Remove="Data\Database\Inventory\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Data\Database\Programmability\" />
|
||||
<Folder Include="Model\Inventory\" />
|
||||
<Folder Include="Model\Product\ProductPricing\" />
|
||||
<Folder Include="Test\Product\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.Core" Version="3.7.402.47" />
|
||||
<PackageReference Include="CSharpAmazonSpAPI" Version="1.7.17" />
|
||||
<PackageReference Include="CSharpAmazonSpAPI" Version="1.8.9" />
|
||||
<PackageReference Include="CsvHelper" Version="33.0.1" />
|
||||
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
|
||||
|
||||
Reference in New Issue
Block a user