mirror of
https://github.com/stokebob/bnhtrade.git
synced 2026-03-19 14:37:16 +00:00
SP-API stock reconciliation
Amazon had depreciated a number of reports that were used for stock reconciliation. Application now uses the new fba ledger report to reconcile. It is currently untested, as this requires data from Amazon. Methods that require testing will return a 'NotImplementedException'. Also, removed the depreciated ILMerge and replaced with ILRepack. Plus much more tidying up, and improvements.
This commit is contained in:
395
src/bnhtrade.Core/Logic/Stock/SkuTransactionImport.cs
Normal file
395
src/bnhtrade.Core/Logic/Stock/SkuTransactionImport.cs
Normal file
@@ -0,0 +1,395 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user