Fix amazon settlement period and other updates

This commit is contained in:
2020-02-11 17:20:45 +00:00
committed by GitHub
parent 7e50da21e7
commit 56647c7648
10 changed files with 128 additions and 46 deletions

View File

@@ -0,0 +1,277 @@
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 string sqlConnectionString;
private Dictionary<string, Model.Account.TaxCode> taxCodeBySkuNumer;
public AmazonSettlement(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
}
public void ToInvoice()
{
var console = new UI.Console.Update();
var log = new Logic.Log.LogEvent();
log.LogInformation("Starting processing of Amazon settlement data into export invoice table...");
// check settlement reports consistancy
var consistencyCheck = new Data.Database.Consistency.ImportAmazonSettlement(sqlConnectionString).PeriodDateGaps();
if (consistencyCheck == false )
{ return; }
// get list of unprocssed settlement reports to export
var settlementData = new Data.Database.Import.ReadAmazonSettlement(sqlConnectionString);
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.Import.ValidateAmazonSettlement();
for (int i = 0; i < settlementList.Count(); i++)
{
if (!validate.IsValid(settlementList[i]))
{
log.LogError("Error procesing Amazon Settlement data for export.", validate.ErrorListToString());
}
}
if (validate.ErrorListIsSet) { return; }
// get dictionary of sku-number to taxcodeId
Console.Write("\rBuilding SKU list... ");
var dicSkuNumberToTaxCodeId = new Dictionary<string, string>();
for (int i = 0; i < settlementList.Count(); i++)
{
if (settlementList[i].SettlementLineListIsSet)
{
for (int j = 0; j < settlementList[i].SettlementLineList.Count(); j++)
{
if (settlementList[i].SettlementLineList[j].SkuIsSet
&& !string.IsNullOrWhiteSpace(settlementList[i].SettlementLineList[j].Sku))
{
if (!dicSkuNumberToTaxCodeId.ContainsKey(settlementList[i].SettlementLineList[j].Sku))
{
dicSkuNumberToTaxCodeId.Add(settlementList[i].SettlementLineList[j].Sku, null);
}
}
}
}
}
var readTaxCode = new Data.Database.Account.ReadTaxCode(sqlConnectionString);
taxCodeBySkuNumer = readTaxCode.BySkuNumber(dicSkuNumberToTaxCodeId.Keys.ToList());
// 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(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();
line.ItemCode = item.Key;
line.TotalNetAmount = item.Value;
lineNetTotal += item.Value;
line.TaxAmount = 0;
lineTaxTotal += 0;
line.Quantity = 1;
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.InvoiceDateKind = DateTimeKind.Utc;
invoice.InvoiceDueDate = settlementList[i].DepositDate;
invoice.InvoiceReference = settlementList[i].SettlementId;
invoice.InvoiceAmount = lineNetTotal + lineTaxTotal;
if (invoice.InvoiceAmount < 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].InvoiceAmount;
}
else
{
invoiceTotal.Add(invoiceList[i].InvoiceReference, invoiceList[i].InvoiceAmount);
}
}
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;
}
// postfix invoices spanning 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 + " total numner of export invoices 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
{
// write to the database (gets validated there)
new Data.Database.Export.CreateSalesInvoice(sqlConnectionString).SaveInvoice(invoiceList);
// set settlements to isprocessed
new Data.Database.Import.UpdateAmazonSettlement(sqlConnectionString).SetIsProcessedTrue(settlementIdList);
scope.Complete();
}
catch (Exception ex)
{
scope.Dispose();
log.LogError(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(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"))
{
if (taxCodeBySkuNumer.ContainsKey(skuNumber))
{
matchString = matchString + "<TaxCode=" + taxCodeBySkuNumer[skuNumber].TaxCodeId + ">";
}
else
{
throw new Exception("Sku#" + skuNumber + " tax info not found in dictionary list.");
}
}
return matchString;
}
}
}