Stock SKU transaction reconciliation

This commit is contained in:
2020-05-29 14:07:46 +01:00
committed by GitHub
parent 43d61c2ef8
commit 3a53350f85
16 changed files with 520 additions and 173 deletions

View File

@@ -86,10 +86,13 @@ namespace bnhtrade.Core.Logic.Stock
logEvent.LogInformation("Starting ReconcileStockTransactions()");
int recordSkip = 0;
ProcessLostAndFound();
ReconcileLostAndFound();
// get list of sku transactions to reconcile
var transList = new Data.Database.Stock.ReadSkuTransaction(sqlConnectionString).GetUnreconciled();
dbSkuTransaction.Init();
dbSkuTransaction.IsReconciled = false;
var transList = dbSkuTransaction.Read();
var shipmentInfoDic = new Dictionary<string, Model.AmazonFba.ShipmentInfo>();
ItemsRemaining = transList.Count;
ItemsCompleted = 0;
@@ -106,88 +109,113 @@ namespace bnhtrade.Core.Logic.Stock
// setup return values
CurrentSkuTransaction = transList[i];
CurrentTransactionId = transList[i].SkuTransactionId;
CurrentTransactionTypeId = transList[i].SkuTransactionTypeId;
CurrentTransactionTypeId = transList[i].SkuTransactionType.TypeId;
LastItemDateTime = transList[i].TransactionDate;
// load type into variable
var transType = dbSkuTransactionType.GetByTypeName(transList[i].SkuTransactionTypeName);
//var transType = dbSkuTransactionType.GetByTypeName(transList[i].SkuTransactionTypeName);
// stop if a new transactiontype is encountered
if (transType.IsNewReviewRequired)
if (transList[i].SkuTransactionType.IsNewReviewRequired)
{
ProgressMessage = "New 'Transaction-Type' encountered";
//Console.Write("\r");
//MiscFunction.EventLogInsert(errMessage, 1);
goto Stop;
break;
}
// set debit/credit status' for special cases (i.e. NULL or 0 debit/credit ids in stockTransactionType table)
if (transType.StockJournalEntryEnabled == true && (transType.DebitStockStatusId == 0 || transType.CreditStockStatusId == 0))
{
// FBA Shipment Receipt +ve
if (transType.TypeCode == "<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_RECEIPTS_DATA_><+ve>"
|| transType.TypeCode == "<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_RECEIPTS_DATA_><-ve>")
{
var shipmentInfo = readShipmentInfo.HeaderByFbaShipmentId(transList[i].Reference);
if (shipmentInfo.IsSetShipmentStockStatusId())
{
// +ve shipment receipt
if (transType.CreditStockStatusId == 0 && transType.DebitStockStatusId > 0)
{ transType.CreditStockStatusId = shipmentInfo.ShipmentStockStatusId; }
// -ve shipment receipt
else if (transType.DebitStockStatusId == 0 && transType.CreditStockStatusId > 0)
{ transType.DebitStockStatusId = shipmentInfo.ShipmentStockStatusId; }
// something went wrong, raise error
else
{
ProgressMessage = "Unable to retrive FBA shipment location/status from tblAmazonShipment for Amazon shipment '" + shipmentInfo.FbaShipmentId + "'.";
recordSkip = recordSkip + 1;
goto Stop;
}
}
}
// something went wrong, raise error
else
{
ProgressMessage = "Coding required. Unhandled special case Transaction-Type encountered (Transaction-Type debit or credit is set to 0).";
recordSkip = recordSkip + 1;
goto Stop;
}
}
// make the changes
if (transType.StockJournalEntryEnabled == false)
else if (transList[i].SkuTransactionType.StockJournalEntryEnabled == false)
{
transList[i].IsProcessed = true;
dbSkuTransaction.Update(transList[i]);
}
// stock journal entry is enabled
else
{
// check debit/credits
if (transList[i].SkuTransactionType.DebitStockStatusId.GetValueOrDefault() == 0
|| transList[i].SkuTransactionType.CreditStockStatusId.GetValueOrDefault() == 0)
{
// special case FBA Shipment Receipt +ve
if (transList[i].SkuTransactionType.TypeCode == "<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_RECEIPTS_DATA_><+ve>"
|| transList[i].SkuTransactionType.TypeCode == "<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_RECEIPTS_DATA_><-ve>")
{
Model.AmazonFba.ShipmentInfo shipmentInfo = null;
if (!shipmentInfoDic.ContainsKey(transList[i].Reference))
{
shipmentInfo = readShipmentInfo.HeaderByFbaShipmentId(transList[i].Reference);
if (shipmentInfo == null)
{
throw new Exception("Unable to retrive shipment info for reference '" + transList[i].Reference + "'.");
}
else
{
shipmentInfoDic.Add(transList[i].Reference, shipmentInfo);
}
}
if (shipmentInfo.IsSetShipmentStockStatusId())
{
// +ve shipment receipt
if (transList[i].SkuTransactionType.CreditStockStatusId == 0
&& transList[i].SkuTransactionType.DebitStockStatusId > 0)
{
transList[i].SkuTransactionType.CreditStockStatusId = shipmentInfo.ShipmentStockStatusId;
}
// -ve shipment receipt
else if (transList[i].SkuTransactionType.DebitStockStatusId == 0
&& transList[i].SkuTransactionType.CreditStockStatusId > 0)
{
transList[i].SkuTransactionType.DebitStockStatusId = shipmentInfo.ShipmentStockStatusId;
}
// something went wrong, raise error
else
{
ProgressMessage = "Unable to retrive FBA shipment location/status from tblAmazonShipment for Amazon shipment '" + shipmentInfo.FbaShipmentId + "'.";
recordSkip = recordSkip + 1;
break;
}
}
else
{
throw new Exception("Unable to retrive shipment info.");
}
}
// manual entry
else
{
ProgressMessage = "Transaction-Type debit or credit is not set, is this a manual entry?";
recordSkip = recordSkip + 1;
break;
}
}
// make the journal entries
var list = new List<(int StockJournalId, int Quantity)>();
if (transType.FilterStockOnDateTime)
if (transList[i].SkuTransactionType.FilterStockOnDateTime)
{
list = stockReallocate.StockReallocateBySkuNumber(
transType.StockJournalTypeId,
transList[i].SkuTransactionType.StockJournalTypeId,
transList[i].SkuNumber,
transList[i].Quantity,
transType.DebitStockStatusId,
transType.CreditStockStatusId,
transType.FirstInFirstOut,
transList[i].SkuTransactionType.DebitStockStatusId.GetValueOrDefault(),
transList[i].SkuTransactionType.CreditStockStatusId.GetValueOrDefault(),
transList[i].SkuTransactionType.FirstInFirstOut,
transList[i].TransactionDate,
false);
}
else
{
list = stockReallocate.StockReallocateBySkuNumber(
transType.StockJournalTypeId,
transList[i].SkuTransactionType.StockJournalTypeId,
transList[i].SkuNumber,
transList[i].Quantity,
transType.DebitStockStatusId,
transType.CreditStockStatusId,
transType.FirstInFirstOut,
transList[i].SkuTransactionType.DebitStockStatusId.GetValueOrDefault(),
transList[i].SkuTransactionType.CreditStockStatusId.GetValueOrDefault(),
transList[i].SkuTransactionType.FirstInFirstOut,
DateTime.UtcNow,
false);
}
@@ -196,15 +224,15 @@ namespace bnhtrade.Core.Logic.Stock
if (list == null || !list.Any())
{
// in special case (found inventory), continue
if (transType.TypeCode.Contains("<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_ADJUSTMENTS_DATA_><F>"))
if (transList[i].SkuTransactionType.TypeCode.Contains("<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_ADJUSTMENTS_DATA_><F>"))
{
continue;// <--------------------------------------------------------------------------------------------------- is this the soruce of the bug?
continue;
}
else
{
ProgressMessage = "Insurficent status/location balance to relocate stock";
recordSkip = recordSkip + 1;
goto Stop;
break;
}
}
@@ -260,8 +288,9 @@ namespace bnhtrade.Core.Logic.Stock
dbSkuTransaction.Create(newRecordList[j]);
}
}
ItemsCompleted++;
ItemsRemaining++;
ItemsRemaining--;
scope.Complete();
}
// end of scope
@@ -275,64 +304,87 @@ namespace bnhtrade.Core.Logic.Stock
return;
}
Stop:
if (ItemsRemaining == 0)
{
ReconciliationComplete = true;
ProgressMessage = "Operation complete.";
}
//Stop:
Console.Write("\r");
MiscFunction.EventLogInsert("ProcessStockTransactions() compete. " + ItemsCompleted + " total records processed, " + recordSkip + " rows uncompllete due to insurficent stock.");
MiscFunction.EventLogInsert("ProcessStockTransactions(), " + recordSkip + " rows skipped due to insurficent stock.", 2);
MiscFunction.ConsoleUpdate("ProcessStockTransactions() compete. " + ItemsCompleted + " total records processed, " + recordSkip + " rows uncompllete due to insurficent stock.");
MiscFunction.ConsoleUpdate("ProcessStockTransactions(), " + recordSkip + " rows skipped due to insurficent stock.");
ReconciliationComplete = true;
ProgressMessage = "Operation complete.";
return;
}
/// <summary>
///
/// </summary>
public void ProcessLostAndFound()
public void ReconcileLostAndFound()
{
using (var scope = new TransactionScope())
{
// get list of sku transactions to reconcile
var transList = new Data.Database.Stock.ReadSkuTransaction(sqlConnectionString).GetUnreconciled();
ItemsRemaining = transList.Count;
ItemsCompleted = 0;
// need to loop though table and cancel out any found before they are lost (in reality they were never
// lost, therefore should not be entered into journal as lost)
string lost = "<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_ADJUSTMENTS_DATA_><M><-ve><InventoryMisplaced><SELLABLE>";
string found = "<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_ADJUSTMENTS_DATA_><F><+ve><InventoryFound><SELLABLE>";
var codeList = new List<string>();
codeList.Add(lost);
codeList.Add(found);
// get list of sku transactions to reconcile
dbSkuTransaction.Init();
dbSkuTransaction.IsReconciled = false;
dbSkuTransaction.StockTransactionTypeCode = codeList;
var transList = dbSkuTransaction.Read();
ItemsRemaining = transList.Count;
ItemsCompleted = 0;
for (int i = 0; i < transList.Count; i++)
{
var transType = dbSkuTransactionType.GetByTypeName(transList[i].SkuTransactionTypeName);
if (transType.TypeCode == found && !transList[i].IsProcessed)
if (transList[i].SkuTransactionTypeCode == found && !transList[i].IsProcessed)
{
string sku = transList[i].SkuNumber;
int foundQty = transList[i].Quantity;
int foundQtyUnAllocated = foundQty;
//loop though list and find matching missing
// loop though list and find matching missing
for (int j = 0; j < transList.Count; j++)
{
// we have a match
if (transList[j].SkuNumber == sku && !transList[j].IsProcessed && transType.TypeCode == lost)
// update the 'lost' transaction
if (transList[j].SkuNumber == sku
&& transList[j].IsProcessed == false
&& transList[j].SkuTransactionTypeCode == lost)
{
// split transaction and break
// split 'lost' transaction
if (foundQtyUnAllocated - transList[j].Quantity < 0)
{
// create and validate clone
// create 'reconciled' clone
var clone = transList[j].Clone();
clone.Quantity = (short)foundQtyUnAllocated;
clone.IsProcessed = true;
clone.Quantity = (short)foundQtyUnAllocated;
// modifiy and validate existing record
transList[j].IsProcessed = false;
transList[j].Quantity = (short)(transList[j].Quantity - foundQtyUnAllocated);
foundQtyUnAllocated = 0;
// fail safe check
if (clone.IsProcessed)
{
foundQtyUnAllocated -= clone.Quantity;
}
if (transList[j].IsProcessed)
{
foundQtyUnAllocated -= transList[j].Quantity;
}
if (foundQtyUnAllocated != 0)
{
throw new Exception("Unallocated quantity should equal zero.");
}
// submitt to database
dbSkuTransaction.Create(clone);
@@ -353,9 +405,10 @@ namespace bnhtrade.Core.Logic.Stock
break;
}
}
}
}
// drop out of the 'find lost' loop
// update the found record
// update the 'found' record
if (foundQty != foundQtyUnAllocated)
{
// set isprocess = true
@@ -367,17 +420,14 @@ namespace bnhtrade.Core.Logic.Stock
// split record
else if (foundQtyUnAllocated > 0)
{
throw new NotImplementedException();
// create clone
// create 'reconciled' clone
var clone = transList[i].Clone();
clone.Quantity -= (short)foundQtyUnAllocated;
clone.IsProcessed = true;
clone.Quantity = (short)(clone.Quantity - foundQtyUnAllocated);
// modifiy and validate existing record
// modifiy existing record
transList[i].IsProcessed = false;
transList[i].Quantity -= (short)foundQtyUnAllocated;
foundQtyUnAllocated = 0;
transList[i].Quantity = (short)foundQtyUnAllocated;
// submitt to database
dbSkuTransaction.Create(clone);
@@ -390,9 +440,15 @@ namespace bnhtrade.Core.Logic.Stock
}
}
}
}
}
// drop out of the 'find found' loop
scope.Complete();
}
}
public void UnReconcileTransaction(int skuTransactionId)
{
dbSkuTransaction.DeleteJournalEntry(skuTransactionId);
}
}
}