3 Commits

Author SHA1 Message Date
41a7f57988 Add support for inventory timestamps 2025-05-29 09:55:07 -04:00
bd6682e861 Merge conflicts 2025-05-28 10:35:06 -04:00
bbeb96dbda Minor changes 2025-05-28 10:34:35 -04:00
7 changed files with 59 additions and 30 deletions

View File

@@ -23,7 +23,7 @@ namespace PartSource.Api.Controllers
[Route("sku/{sku}/storeNumber/{storeNumber}")]
public async Task<ActionResult> GetInventory(int sku, int storeNumber)
{
PartsAvailability inventory = await _inventoryService.GetInventory(sku, storeNumber);
PartAvailability inventory = await _inventoryService.GetInventory(sku, storeNumber);
if (inventory == null)
{
@@ -36,7 +36,8 @@ namespace PartSource.Api.Controllers
{
StoreNumber = inventory.Store,
Sku = sku,
Quantity = inventory.QTY
Quantity = inventory.QTY,
Updated = inventory.Updated ?? System.DateTime.MinValue
}
});
}

View File

@@ -53,7 +53,7 @@ namespace PartSource.Automation.Jobs
connection.Open();
using SqlCommand command = new SqlCommand("TRUNCATE TABLE PartAvailability", connection);
await command.ExecuteNonQueryAsync();
await command.ExecuteNonQueryAsync(token);
using SqlBulkCopy bulk = new SqlBulkCopy(connection)
{
@@ -61,7 +61,7 @@ namespace PartSource.Automation.Jobs
BulkCopyTimeout = 14400
};
bulk.WriteToServer(dataTable);
await bulk.WriteToServerAsync(dataTable, token);
_ftpService.Delete(lastUploadedFile.Filename);
@@ -74,6 +74,7 @@ namespace PartSource.Automation.Jobs
dataTable.Columns.Add("Store", typeof(int));
dataTable.Columns.Add("SKU", typeof(string));
dataTable.Columns.Add("QTY", typeof(int));
dataTable.Columns.Add("Updated", typeof(DateTime));
using StreamReader reader = new StreamReader(filename);
string line = reader.ReadLine(); // Burn the header row
@@ -91,9 +92,10 @@ namespace PartSource.Automation.Jobs
string sku = columns[1].Trim();
if (int.TryParse(columns[0], out int store)
&& !string.IsNullOrEmpty(sku)
&& int.TryParse(columns[2], out int quantity))
&& int.TryParse(columns[2], out int quantity)
&& DateTime.TryParse(columns[3], out DateTime updated))
{
dataTable.Rows.Add(new object[] { store, sku, quantity });
dataTable.Rows.Add(new object[] { store, sku, quantity, updated });
}
}

View File

@@ -32,6 +32,7 @@ namespace PartSource.Automation.Jobs
_logger = logger;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities")]
public async Task Run(CancellationToken token, params string[] arguments)
{
FtpFileInfo lastUploadedFile = _ftpService.ListFilesExtended()
@@ -50,37 +51,67 @@ namespace PartSource.Automation.Jobs
string file = _ftpService.Download($"{lastUploadedFile.Filename}");
using SqlConnection connection = new SqlConnection(_connectionString);
connection.Open();
connection.Open();
using StreamReader reader = new StreamReader(file);
string line = reader.ReadLine(); // Burn the header row
IDictionary<string, object> parameters = new Dictionary<string, object>();
string command = string.Empty;
int i = 0;
while (reader.Peek() > 0)
{
line = reader.ReadLine();
string[] columns = line.Split("|");
for (int i = 0; i < columns.Length; i++)
for (int j = 0; j < columns.Length; j++)
{
columns[i] = columns[i].Replace("\"", string.Empty);
columns[j] = columns[j].Replace("\"", string.Empty);
}
if (int.TryParse(columns[0], out int store)
&& int.TryParse(columns[1], out int sku)
&& int.TryParse(columns[2], out int quantity))
&& int.TryParse(columns[2], out int quantity)
&& DateTime.TryParse(columns[3], out DateTime updated))
{
using SqlCommand sqlCommand = new SqlCommand("UPDATE PartAvailability SET QTY = @qty WHERE SKU = @sku AND Store = @store", connection);
sqlCommand.Parameters.Add(new SqlParameter("qty", quantity));
sqlCommand.Parameters.Add(new SqlParameter("sku", sku));
sqlCommand.Parameters.Add(new SqlParameter("store", store));
command += $"UPDATE PartAvailability SET QTY = @qty_{i}, Updated = @updated_{i} WHERE SKU = @sku_{i} AND Store = @store_{i};";
await sqlCommand.ExecuteNonQueryAsync();
parameters.Add($"qty_{i}", quantity);
parameters.Add($"store_{i}", store);
parameters.Add($"sku_{i}", sku);
parameters.Add($"updated_{i}", updated);
i++;
}
if (i == 250)
{
using SqlCommand nested = new SqlCommand(command, connection);
foreach (KeyValuePair<string, object> parameter in parameters)
{
nested.Parameters.Add(new SqlParameter(parameter.Key, parameter.Value));
}
await nested.ExecuteNonQueryAsync(token);
parameters.Clear();
command = string.Empty;
i = 0;
}
}
using SqlCommand sqlCommand = new SqlCommand(command, connection);
foreach (KeyValuePair<string, object> parameter in parameters)
{
sqlCommand.Parameters.Add(new SqlParameter(parameter.Key, parameter.Value));
}
await sqlCommand.ExecuteNonQueryAsync(token);
_ftpService.Delete(lastUploadedFile.Filename);
return;
}
}
}
}

View File

@@ -75,7 +75,7 @@ namespace PartSource.Automation
.AddAutomation(options =>
{
options.HasBaseInterval(new TimeSpan(0, 5, 0))
.HasMaxFailures(1)
.HasMaxFailures(5)
//.HasJob<TestJob>(options => options.HasInterval(new TimeSpan(7, 0, 0, 0)));
//
//.HasJob<SyncronizeProducts>(options => options.HasInterval(new TimeSpan(24, 0, 0)))

View File

@@ -32,7 +32,7 @@ namespace PartSource.Data.Contexts
public DbSet<Vehicle> Vehicles { get; set; }
public DbSet<PartsAvailability> PartAvailabilities { get; set; }
public DbSet<PartAvailability> PartAvailabilities { get; set; }
public DbSet<BaseVehicle> BaseVehicles { get; set; }
@@ -48,7 +48,7 @@ namespace PartSource.Data.Contexts
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PartsAvailability>().HasKey(p => new { p.Store, p.SKU });
modelBuilder.Entity<PartAvailability>().HasKey(p => new { p.Store, p.SKU });
modelBuilder.Entity<DcfMapping>().HasKey(d => new { d.LineCode, d.WhiCode });
modelBuilder.Entity<ShopifyChangelog>()

View File

@@ -1,9 +1,10 @@
using System.ComponentModel.DataAnnotations;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace PartSource.Data.Models
{
public class PartsAvailability
public class PartAvailability
{
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -13,14 +14,8 @@ namespace PartSource.Data.Models
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int SKU { get; set; }
[Column("Line Code")]
[StringLength(50)]
public string Line_Code { get; set; }
[Column("Part Number")]
[StringLength(50)]
public string Part_Number { get; set; }
public int? QTY { get; set; }
public DateTime? Updated { get; set; }
}
}

View File

@@ -21,7 +21,7 @@ namespace PartSource.Services
_fitmentContext = fitmentContext;
}
public async Task<PartsAvailability> GetInventory(int sku, int storeNumber)
public async Task<PartAvailability> GetInventory(int sku, int storeNumber)
{
return await _partSourceContext.PartAvailabilities.FirstOrDefaultAsync(s => s.Store == storeNumber && s.SKU == sku);
}