Files
bnhtrade/src/bnhtrade.Core/Logic/Stock/SkuTransactionImport.cs
Bobbie Hodgetts f9b38a98b1 wip
2024-11-21 09:36:16 +00:00

396 lines
17 KiB
C#

using Amazon.Runtime.Internal.Transform;
using bnhtrade.Core.Model.Stock;
using FikaAmazonAPI.AmazonSpApiSDK.Models.Restrictions;
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.Stock
{
public class SkuTransactionImport
{
private Log.LogEvent log = new Log.LogEvent();
private string ConstructTransactionTypeCode(Model.Import.FbaReimbursementReport record, bool isPositive)
{
if (string.IsNullOrEmpty(record.Reason))
{
return null;
}
string transactionCode = "<AmazonReport><_GET_FBA_REIMBURSEMENTS_DATA_>";
if (isPositive)
{
transactionCode = transactionCode + "<+ve>";
}
else
{
transactionCode = transactionCode + "<-ve>";
}
transactionCode = transactionCode + "<" + record.Reason + ">";
return transactionCode;
}
private string ConstructTransactionTypeCode(Model.Import.AmazonFbaInventoryLedgerDetail record)
{
//if (string.IsNullOrEmpty(record.Reason))
//{
// return null;
//}
string transactionCode = "<AmazonReport><GET_LEDGER_DETAIL_VIEW_DATA><" + record.EventType + ">";
if (!string.IsNullOrEmpty(record.Reason))
{
transactionCode = transactionCode + "<" + record.Reason + ">";
}
if (record.Quantity < 0)
{
transactionCode = transactionCode + "<-ve>";
}
else
{
transactionCode = transactionCode + "<+ve>";
}
transactionCode = transactionCode + "<" + record.Disposition + ">";
return transactionCode;
}
/// <summary>
/// Imports/Transaposes all data required for reconcilation, into the sku transaction table.
/// </summary>
public void ImportAll()
{
bool inventoryLedgerDetail = false;
bool reimbursement = false;
while (true)
{
try
{
if (true)
{
if (inventoryLedgerDetail == false) { inventoryLedgerDetail = true; ImportAmazonFbaLedgerDetail(); }
if (reimbursement == false) { reimbursement = true; ImportAmazonFbaReimbursement(); }
}
break;
}
catch (Exception ex)
{
log.LogError(
"Exception caught running Importing amazon reports in the SKU transaction table, see for further details",
ex.ToString()
);
}
}
}
/// <summary>
/// Imports/Transaposes data from the Amazon FBA Reimbursement report table, into the SKU transaction table, ready for reconcilation.
/// </summary>
/// <exception cref="NotImplementedException"></exception>
public void ImportAmazonFbaReimbursement()
{
//throw new NotImplementedException("Needs testing");
/*
* Not to be used for stock reconciliation! A single stock item can have multiple reimburesements aginst it.
* Only use to move lost inventory to Amazon ownership (even then, with some caveats!)
*
* generally any lost or damaged stock goes to lost and found (and is hence written off)
* once amazon have reimbursed (confitmation via this report) the stock is then moved to amazon owvership
* and also the 'Cost of goods' amounts moved to the appropreate account id
*/
log.LogInformation("Starting TransposeFbaRemovalOrderReport()");
int transposeCount = 0;
int transposeSkip = 0;
var dbAmznReport = new Data.Database.Import.AmazonFbaReimbursement();
var dbTransType = new Logic.Stock.SkuTransactionTypeCrud();
try
{
var importList = dbAmznReport.Read(false);
// we need to retrive the transaction-types from the database
// run through the returned records and build list of transaction-type codes
var transTypeCodeList = new List<string>();
foreach (var item in importList)
{
transTypeCodeList.Add(ConstructTransactionTypeCode(item, false));
// any that reimburse inventory, will have two entries
if(item.QuantityReimbursedInventory > 0)
{
transTypeCodeList.Add(ConstructTransactionTypeCode(item, true));
}
}
transTypeCodeList = transTypeCodeList.Distinct().ToList();
// get transaction-type objects from db
var transTypeDict = dbTransType.GetByTypeCode(transTypeCodeList);
// new transaction-type check
var foundNewType = false;
foreach (var transTypeCode in transTypeCodeList)
{
// if a transaction-type code did not exist in the db, we need to create it
if (transTypeDict.ContainsKey(transTypeCode) == false)
{
dbTransType.Create(
transTypeCode,
(int)Data.Database.Constants.StockJournalType.SkuReconciliationFbaReimbursement
);
var newItem = dbTransType.GetByTypeCode(transTypeCode);
if (newItem == null)
{
throw new Exception("Createing new transaction-type returned null");
}
transTypeDict.Add(newItem.TypeCode, newItem);
foundNewType = true;
}
// also check existing transaction-type for 'is new'
if (transTypeDict[transTypeCode].IsNewReviewRequired)
{
foundNewType = true;
}
}
// don't go any further until the transaction-type has been reviewed/setup
if (foundNewType)
{
log.LogWarning("Cannot complete ImportAmazonFbaReimbursement, new 'Stock Trnasaction Type' found. Review required/");
return;
}
// we're all setup, time to transpose the report into the transaction table
using (TransactionScope scope = new TransactionScope())
{
var dbTrans = new Logic.Stock.SkuTransactionCrud();
foreach (var item in importList)
{
int? transId = null;
// always added
if (true)
{
string typeCode = ConstructTransactionTypeCode(item, false);
var transType = transTypeDict[typeCode];
if (transType.IsNewReviewRequired)
{
throw new Exception("Fail safe: Buggy code, should not get here!");
}
else if (transType.TransactionImportEnabled)
{
var newTransaction = new Model.Stock.SkuTransactionCreate(
item.ApprovalDate,
typeCode,
item.FbaReimbursementReportID,
item.ReimbursementId,
item.Reason,
item.Sku,
item.QuantityReimbursedInventory
);
transId = dbTrans.Create(newTransaction);
}
}
// double transaction added if true
if (item.QuantityReimbursedInventory > 0)
{
string typeCode = ConstructTransactionTypeCode(item, true);
var transType = transTypeDict[typeCode];
if (transType.IsNewReviewRequired)
{
throw new Exception("Fail safe: Buggy code, should not get here!");
}
else if (transType.TransactionImportEnabled)
{
var newTransaction = new Model.Stock.SkuTransactionCreate(
item.ApprovalDate,
typeCode,
item.FbaReimbursementReportID,
item.ReimbursementId,
item.Reason,
item.Sku,
item.QuantityReimbursedInventory
);
transId = dbTrans.Create(newTransaction);
}
}
// update the amazon report table
dbAmznReport.UpdateIsProcessed(item.FbaReimbursementReportID, true, transId);
transposeCount = transposeCount + 1;
}
// drop out of loop
scope.Complete();
}
Console.Write("\r");
log.LogInformation("ProcessFbaReimbursementData() complete, " + transposeCount + " total records transposed, " + transposeSkip + " records skipped.");
}
catch (Exception ex)
{
log.LogError("Exception catch, aborting ProcessFbaReimbursementData(), see detailed info. "
+ transposeCount + " total records completed, " + transposeSkip + " records skipped.", ex.ToString());
}
}
/// <summary>
/// Imports/Transaposes data from the Amazon FBA Ledger Detail report table, into the SKU transaction table, ready for reconcilation.
/// </summary>
/// <exception cref="NotImplementedException"></exception>
public void ImportAmazonFbaLedgerDetail()
{
//// Done but needs testing!!
//throw new NotImplementedException("Done but needs testing!!");
log.LogInformation("Starting TransposeFbaAdustmentReport()");
int transposeCount = 0;
int transposeSkip = 0;
using (var scope = new TransactionScope())
{
try
{
// get unprocessed amazon ledger report items
var dbImport = new Data.Database.Import.AmazonFbaInventoryLedgerDetail();
var reportDict = dbImport.Read(null, null, false);
// create transaction list to insert into transaction table
var transactionList = new List<SkuTransactionCreate>();
var transCodeToJournalTypeId = new Dictionary<string, int>();
foreach (var item in reportDict)
{
// test for internal Amazon stuff that we don't care about and mark as processed
if (item.Value.EventType == "WhseTransfers")
{
dbImport.UpdateIsProcessed(item.Key, true);
}
// add the the transaction list
else
{
// build the transaction code
string transactionCode = ConstructTransactionTypeCode(item.Value);
// load the report item into parameter
DateTime transactionDate = item.Value.DateAndTime;
int foreignKey = item.Key;
string reference = null;
if (!string.IsNullOrEmpty(item.Value.ReferenceId))
{ reference = item.Value.ReferenceId; }
string detail = "Fulfillment Center: " + item.Value.FulfillmentCenter;
string skuNumber = item.Value.Msku;
// quanity in the transaction table is always be +ve
int quantity = item.Value.Quantity;
if (quantity < 0)
quantity = quantity * -1;
// create the objet class and add to the list
var transaction = new Model.Stock.SkuTransactionCreate(
transactionDate
, transactionCode
, foreignKey
, reference
, detail
, skuNumber
, quantity
);
transactionList.Add(transaction);
// add transtypecode to dictionary to 'stock journal type' dictionary. Needed if there's a new transaction type
if (transCodeToJournalTypeId.ContainsKey(transactionCode) == false)
{
int journalTypeId = 0;
if (item.Value.EventType == "Receipts")
{ journalTypeId = (int)Data.Database.Constants.StockJournalType.SkuReconciliationFbaReceipt; }
else if (item.Value.EventType == "Shipments")
{ journalTypeId = (int)Data.Database.Constants.StockJournalType.SkuReconciliationFbaShipment; }
else if (item.Value.EventType == "Adjustments")
{ journalTypeId = (int)Data.Database.Constants.StockJournalType.SkuReconciliationFbaAdjustment; }
else if (item.Value.EventType == "CustomerReturns")
{ journalTypeId = (int)Data.Database.Constants.StockJournalType.SkuReconciliationFbaCustomerReturn; }
else if (item.Value.EventType == "VendorReturns")
{ journalTypeId = (int)Data.Database.Constants.StockJournalType.SkuReconciliationFbaVendorReturn; }
else if (item.Value.EventType == "WhseTransfers")
{ journalTypeId = -1; }
else
{ throw new Exception("New event-type " + item.Value.EventType + " in the GET_LEDGER_DETAIL_VIEW_DATA report"); }
transCodeToJournalTypeId.Add(transactionCode, journalTypeId);
}
}
}
// check for any new types codes, and add them if ther are
var dbTransType = new Logic.Stock.SkuTransactionTypeCrud();
var transTypeList = dbTransType.GetByTypeCode(transCodeToJournalTypeId.Keys.ToList());
foreach ( var transType in transTypeList)
{
if (transCodeToJournalTypeId.ContainsKey(transType.Key))
{
transCodeToJournalTypeId.Remove(transType.Key);
}
}
foreach (var newItem in transCodeToJournalTypeId)
{
dbTransType.Create(newItem.Key, newItem.Value);
}
// finally, add the transction list to the table
var dbTransaction = new Logic.Stock.SkuTransactionCrud();
foreach (var item in transactionList)
{
int id = dbTransaction.Create(item);
dbImport.UpdateIsProcessed((int)item.ForeignKey, id);
}
scope.Complete();
log.LogInformation(
"TransposeFbaAdustmentReport() complete, " + transposeCount + " total records transposed, " + transposeSkip + " records skipped."
);
if (transposeSkip > 0)
{
log.LogInformation(
transposeSkip + " number records skipped during TransposeFbaAdustmentReport() operation."
);
}
}
catch (Exception ex)
{
scope.Dispose();
log.LogError(
"Exception catch, aborting TransposeFbaAdustmentReport(), see detailed info. "
+ transposeCount + " total records completed, " + transposeSkip + " records skipped."
, ex.ToString()
);
}
}
return;
}
}
}