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 = "<_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 = "<" + 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; } /// /// Imports/Transaposes all data required for reconcilation, into the sku transaction table. /// 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() ); } } } /// /// Imports/Transaposes data from the Amazon FBA Reimbursement report table, into the SKU transaction table, ready for reconcilation. /// /// 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(); 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()); } } /// /// Imports/Transaposes data from the Amazon FBA Ledger Detail report table, into the SKU transaction table, ready for reconcilation. /// /// 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(); var transCodeToJournalTypeId = new Dictionary(); 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; } } }