mirror of
https://github.com/stokebob/bnhtrade.git
synced 2026-03-25 09:07: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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user