Feature: Sync MWS Shipment with Database

Various restructuring and misc. features added.
Removed reliance on ABrain Amazon MWS NuGet package, added Amazon's own C# lib
This commit is contained in:
Bobbie Hodgetts
2019-06-24 16:01:50 +01:00
committed by GitHub
parent bc44546efd
commit 116aedb897
27 changed files with 2236 additions and 289 deletions

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Data.Database
{
public class Connection
{
protected readonly string sqlConnectionString;
public Connection(string sqlConnectionString)
{
// setup sql parameters
if (sqlConnectionString.Length == 0)
{ throw new Exception("Zero length sql connectionstring passed"); }
this.sqlConnectionString = sqlConnectionString;
}
}
}

View File

@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Data.Database.FBAInbound
{
public class GetShipmentHeaderInfo : Connection
{
private List<string> shipmentIdList;
public GetShipmentHeaderInfo(string sqlConnectionString) : base(sqlConnectionString) { }
/// <summary>
/// Return active shipments only. Default is false.
/// </summary>
public bool ActiveShipments { get; set; } = false;
public bool IsSetActiveShipments { get { return true; } }
public bool IsSetShipmentIdList
{
get
{
if (ShipmentIdList == null || !ShipmentIdList.Any()) { return false; }
else { return true; }
}
}
/// <summary>
/// Filter results by Amazon's shipment Id.
/// </summary>
public List<string> ShipmentIdList
{
get { return shipmentIdList; }
set
{
if (value != null)
{
// clean list
shipmentIdList = new List<string>();
foreach (string item in value)
{
if (item.Length > 0)
{
shipmentIdList.Add(item);
}
}
if (!ShipmentIdList.Any())
{
shipmentIdList = null;
throw new Exception("Invalid shipment Id set");
}
}
}
}
/// <summary>
/// Retrives table primary key 'AmazonShipmentID' for tblAmazonShipment
/// </summary>
/// <param name="amazonShipmentId">Amazon's inbound FBA shipment Id.</param>
/// <returns>Primary key or -1 if match isn't found.</returns>
public List<Model.AmazonFBAInbound.ShipmentInfo> Execute()
{
// build the sql string
int countShipId = 0;
string sqlString = @"
SELECT
AmazonShipmentID
,ShipmentName
,ShipmentId
,CenterId
,ShipmentStatus
,LastUpdated
,IsClosed
FROM
tblAmazonShipment
WHERE
1 = 1";
if (ActiveShipments)
{
sqlString = sqlString + @"
AND IsClosed = 0";
}
var dicShipIdByParameterString = new Dictionary<string, string>();
if (IsSetShipmentIdList)
{
foreach (string item in ShipmentIdList)
{
countShipId = countShipId + 1;
string parameterString = "@shipmentId" + countShipId.ToString().PadLeft(6, '0');
dicShipIdByParameterString.Add(parameterString, item);
if (countShipId == 1)
{
sqlString = sqlString + @"
AND ( ShipmentId = " + parameterString;
}
else
{
sqlString = sqlString + @"
OR ShipmentId = " + parameterString;
}
}
if (countShipId > 0)
{
sqlString = sqlString + " )";
}
}
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sqlString, conn))
{
// add shipId parameters
if (ShipmentIdList.Any())
{
foreach (var item in dicShipIdByParameterString)
{
cmd.Parameters.AddWithValue(item.Key, item.Value);
}
}
using (var reader = cmd.ExecuteReader())
{
if (!reader.HasRows)
{
// no records
return null;
}
var infoList = new List<Model.AmazonFBAInbound.ShipmentInfo>();
while (reader.Read())
{
var info = new Model.AmazonFBAInbound.ShipmentInfo();
int tablePK = reader.GetInt32(0);
info.ShipmentName = reader.GetString(1);
info.AmazonShipmentId = reader.GetString(2);
info.DestinationFulfillmentCenterId = reader.GetString(3);
info.ShipmentStatus = reader.GetString(4);
info.LastUpdatedUtc = reader.GetDateTime(5);
bool dbIsClosed = reader.GetBoolean(6);
// db consistancy check
if (dbIsClosed != info.ShipmentIsClosed)
{
throw new Exception("Data inconstitancy in database: check shipment IsClosed where AmazonShipmentID=" + tablePK);
}
// update cache
infoList.Add(info);
}
return infoList;
}
}
}
}
}
}

View File

@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Data.Database.FBAInbound
{
public class GetShipmentPrimaryKey : Connection
{
private bool enableCache = false;
private Dictionary<string, int> shipmentPKByAmazonShipmentIdDic;
private Dictionary<int, string> amazonShipmentIdByShipmentPKDic;
public GetShipmentPrimaryKey(string sqlConnectionString) : base(sqlConnectionString) { }
public bool CacheEnabled
{
get { return enableCache; }
set
{
if (value && enableCache == false)
{
shipmentPKByAmazonShipmentIdDic = new Dictionary<string, int>();
amazonShipmentIdByShipmentPKDic = new Dictionary<int, string>();
}
else
{
shipmentPKByAmazonShipmentIdDic = null;
amazonShipmentIdByShipmentPKDic = null;
}
}
}
private void ClearCache()
{
if (CacheEnabled)
{
shipmentPKByAmazonShipmentIdDic.Clear();
amazonShipmentIdByShipmentPKDic.Clear();
}
}
private void DeleteCachedShipmentPK(int shipmentPrimaryKey)
{
if (CacheEnabled)
{
if (amazonShipmentIdByShipmentPKDic.ContainsKey(shipmentPrimaryKey))
{
string amazonShipmentId = amazonShipmentIdByShipmentPKDic[shipmentPrimaryKey];
shipmentPKByAmazonShipmentIdDic.Remove(amazonShipmentId);
amazonShipmentIdByShipmentPKDic.Remove(shipmentPrimaryKey);
}
}
}
/// <summary>
/// Retrives table primary key 'AmazonShipmentID' for tblAmazonShipment
/// </summary>
/// <param name="amazonShipmentId">Amazon's inbound FBA shipment Id.</param>
/// <param name="forceDBQuery">Forces a database query when cache is enabled.</param>
/// <returns>Primary key or -1 if match isn't found.</returns>
public int ByAmazonShipmentId(string amazonShipmentId, bool forceDBQuery = false)
{
if (amazonShipmentId.Length < 5)
{
throw new Exception("Incorrect Amazon shipment if supplied '" + amazonShipmentId + "'");
}
// first, query class dictionary before sql call
if (CacheEnabled
&& forceDBQuery == false
&& shipmentPKByAmazonShipmentIdDic.ContainsKey(amazonShipmentId))
{
return shipmentPKByAmazonShipmentIdDic[amazonShipmentId];
}
int shipmentPK = -1;
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(@"
SELECT AmazonShipmentID
FROM tblAmazonShipment
WHERE (((tblAmazonShipment.ShipmentId)=@amazonShipmentId));
", conn))
{
cmd.Parameters.AddWithValue("@amazonShipmentId", amazonShipmentId);
object obj = cmd.ExecuteScalar();
if (obj == null || obj == DBNull.Value)
{
return shipmentPK;
}
else
{
shipmentPK = (int)obj;
}
}
}
UpdateCache(shipmentPK, amazonShipmentId);
return shipmentPK;
}
private void UpdateCache(int shipmentPK, string amazonShipmentId)
{
if (CacheEnabled)
{
DeleteCachedShipmentPK(shipmentPK);
shipmentPKByAmazonShipmentIdDic.Add(amazonShipmentId, shipmentPK);
amazonShipmentIdByShipmentPKDic.Add(shipmentPK, amazonShipmentId);
}
}
}
}

View File

@@ -0,0 +1,246 @@
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.Data.Database.FBAInbound
{
public class SetShipmentInfo : Connection
{
private GetShipmentPrimaryKey getPK;
private Data.Database.SKU.GetSKUId skuIdLoopkup;
public SetShipmentInfo(string sqlConnectionString) : base(sqlConnectionString) { }
private GetShipmentPrimaryKey GetPK
{
get
{
if (getPK == null)
{
getPK = new GetShipmentPrimaryKey(sqlConnectionString);
}
return getPK;
}
set
{
getPK = value;
}
}
private Data.Database.SKU.GetSKUId SkuIdLoopkup
{
get
{
if (skuIdLoopkup == null)
{
skuIdLoopkup = new SKU.GetSKUId(sqlConnectionString);
}
return skuIdLoopkup;
}
}
public void Excecute(Model.AmazonFBAInbound.ShipmentInfo info)
{
using (var scope = new TransactionScope())
{
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
// input checks
if (!info.IsSetAll())
{
throw new Exception("Insurficent ShipmentInfo parameters.");
}
// get tablePK
int shipmentPK = GetPK.ByAmazonShipmentId(info.AmazonShipmentId);
// add or update shipment header info
if (shipmentPK == -1)
{
shipmentPK = InsertShipmentHeaderInfo(info);
}
else
{
UpdateShipmentHeaderInfo(info);
DeleteShipmentItems(shipmentPK);
}
// insert new shipment item info
foreach (var item in info.ShipmentItemInfoList)
{
int skuId = SkuIdLoopkup.BySKUNumber(item.SKUNumber);
using (SqlCommand cmd = new SqlCommand(@"
INSERT INTO tblAmazonShipmentItem (
AmazonShipmentID
,SkuID
,QuantityAllocated
,QuantityReceived
)
VALUES (
@amazonShipmentID
,@skuID
,@quantityAllocated
,@quantityReceived
)
", conn))
{
cmd.Parameters.AddWithValue("@amazonShipmentID", shipmentPK);
cmd.Parameters.AddWithValue("@skuID", skuId);
cmd.Parameters.AddWithValue("@quantityAllocated", item.QuantityAllocated);
cmd.Parameters.AddWithValue("@quantityReceived", item.QuantityReceived);
int effected = (int)cmd.ExecuteNonQuery();
if (effected == 0)
{
throw new Exception("Error, no tblAmazonShipment was not updated.");
}
}
}
}
scope.Complete();
}
}
public void ExcecuteByList(List<Model.AmazonFBAInbound.ShipmentInfo> infoList)
{
using (var scope = new TransactionScope())
{
foreach (var item in infoList)
{
Excecute(item);
}
scope.Complete();
}
}
private void DeleteShipmentItems(int shipmentId)
{
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(@"
DELETE FROM
tblAmazonShipmentItem
WHERE
AmazonShipmentId = @shipmentId
", conn))
{
cmd.Parameters.AddWithValue("@shipmentId", shipmentId);
cmd.ExecuteNonQuery();
}
}
}
private int InsertShipmentHeaderInfo(Model.AmazonFBAInbound.ShipmentInfo info)
{
if (!info.IsSetAll())
{
throw new Exception("Unsuficent properties set in Shipment Header Info.");
}
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
if (GetPK.ByAmazonShipmentId(info.AmazonShipmentId) != -1)
{
throw new Exception("Shipment insert failed, shipment with same Amazon Id already exists.");
}
// get next shipment Number in sequence and create shipment name
int sequenceNumber;
using (SqlCommand cmd = new SqlCommand(@"
SELECT NEXT VALUE FOR dbo.ShipmentCountSequence AS SequenceNumber
", conn))
{
sequenceNumber = (int)cmd.ExecuteScalar();
}
// info.ShipmentName = "FBA_Shipment_" + sequenceNumber.ToString().PadLeft(4, '0') + "_" + info.DestinationFulfillmentCenterId;
// make the insert
using (SqlCommand cmd = new SqlCommand(@"
INSERT INTO tblAmazonShipment (
AmazonShipmentCount
,ShipmentName
,ShipmentId
,CenterId
,ShipmentStatus
,LastUpdated
,IsClosed
)
OUTPUT INSERTED.AmazonShipmentID
VALUES (
@amazonShipmentCount
,@shipmentName
,@shipmentId
,@centerId
,@shipmentStatus
,@lastUpdated
,@isClosed
)
", conn))
{
cmd.Parameters.AddWithValue("@amazonShipmentCount", sequenceNumber);
cmd.Parameters.AddWithValue("@shipmentName", info.ShipmentName);
cmd.Parameters.AddWithValue("@shipmentId", info.AmazonShipmentId);
cmd.Parameters.AddWithValue("@centerId", info.DestinationFulfillmentCenterId);
cmd.Parameters.AddWithValue("@shipmentStatus", info.ShipmentStatus);
cmd.Parameters.AddWithValue("@lastUpdated", info.LastUpdatedUtc);
cmd.Parameters.AddWithValue("@isClosed", info.ShipmentIsClosed);
int tablePk = (int)cmd.ExecuteScalar();
// update cache and return
return tablePk;
}
}
}
private void UpdateShipmentHeaderInfo(Model.AmazonFBAInbound.ShipmentInfo info)
{
int tablePK = GetPK.ByAmazonShipmentId(info.AmazonShipmentId);
if (tablePK == -1)
{
throw new Exception("Shipment insert failed, shipment with same Amazon Id already exists.");
}
// make the update
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(@"
UPDATE tblAmazonShipment
SET
ShipmentName = @shipmentName
,ShipmentStatus = @shipmentStatus
,LastUpdated = @lastUpdated
,IsClosed = @isClosed
WHERE
AmazonShipmentID = @tablePK
", conn))
{
cmd.Parameters.AddWithValue("@shipmentName", info.ShipmentName);
cmd.Parameters.AddWithValue("@shipmentStatus", info.ShipmentStatus);
cmd.Parameters.AddWithValue("@lastUpdated", info.LastUpdatedUtc);
cmd.Parameters.AddWithValue("@isClosed", info.ShipmentIsClosed);
cmd.Parameters.AddWithValue("@tablePK", tablePK);
int count = cmd.ExecuteNonQuery();
if (count == 0)
{
throw new Exception("No records updated");
}
}
}
}
}
}

View File

@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
namespace bnhtrade.Core.Data.Database.SKU
{
public class GetSKUId
{
private Dictionary<string, int> SKUIdBySKUNumber { get; set; }
private Dictionary<int, string> SKUNumberBySKUId { get; set; }
private string SqlConnectionString { get; set; }
public GetSKUId(string sqlConnectionString)
{
// setup sql parameters
if (sqlConnectionString.Length == 0)
{
throw new Exception("Zero length sql connectionstring passed");
}
SqlConnectionString = sqlConnectionString;
// set paramters
SKUIdBySKUNumber = new Dictionary<string, int>();
SKUNumberBySKUId = new Dictionary<int, string>();
}
/// <summary>
/// Get SKUId by SKU number.
/// </summary>
/// <param name="skuNumber">SKU Number to lookup Id</param>
/// <param name="enableLegacy">If SKUId is can not be retrived, function will attempt to lookup value by sku count.</param>
/// <param name="forceRequery">Results are chached, option forces database requery.</param>
/// <returns>Database SKUId or Exception if not found.</returns>
public int BySKUNumber(string skuNumber, bool enableLegacy = false, bool forceRequery = false)
{
if (forceRequery == false && SKUIdBySKUNumber.ContainsKey(skuNumber))
{
return SKUIdBySKUNumber[skuNumber];
}
int skuId = 0;
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(@"
SELECT skuSkuID
FROM tblSku
WHERE skuSkuNumber=@skuNumber;
", conn))
{
cmd.Parameters.AddWithValue("@skuNumber", skuNumber);
object obj = cmd.ExecuteScalar();
if (!(obj == null) || !(obj == DBNull.Value))
{
skuId = Convert.ToInt32(obj);
}
}
// if that didn't work, lookup buy sku count
if (skuId == 0 && skuNumber.Length == 6 && enableLegacy == true)
{
int skucount;
bool okay = int.TryParse(skuNumber, out skucount);
if (okay)
{
using (SqlCommand cmd = new SqlCommand(
"SELECT skuSkuID FROM tblSku WHERE skuSkuCount=@skuCount;"
, conn))
{
cmd.Parameters.AddWithValue("@skuCount", skucount);
object obj = cmd.ExecuteScalar();
if (!(obj == null) || !(obj == DBNull.Value))
{
skuId = Convert.ToInt32(obj);
}
}
}
}
}
// update cache
if (skuId > 0)
{
if (SKUIdBySKUNumber.ContainsKey(skuNumber))
{ SKUIdBySKUNumber.Remove(skuNumber); }
SKUIdBySKUNumber.Add(skuNumber, skuId);
if (SKUNumberBySKUId.ContainsKey(skuId))
{ SKUNumberBySKUId.Remove(skuId); }
SKUNumberBySKUId.Add(skuId, skuNumber);
}
else
{
throw new Exception("Unable to retrive an SkuID for SKU#" + skuNumber);
}
return skuId;
}
}
}