Compare commits
22 Commits
cbf7bb8de6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| dcd1a9ccec | |||
| aee37eb8f7 | |||
| 0ce0dc35e1 | |||
| 1ddf18a400 | |||
| 41a7f57988 | |||
| bd6682e861 | |||
| bbeb96dbda | |||
| ca45a77a0f | |||
| 8b6892df60 | |||
| f1ca48c1d0 | |||
| 57f42a0e47 | |||
| eb928a7c56 | |||
| 36b05af60e | |||
| a3a08d9cff | |||
| 857f94c59a | |||
| cc2cbd09e1 | |||
| aed30707be | |||
| b8406a7f71 | |||
| 469fb0ff5f | |||
| 9a2d57f975 | |||
| 6a81fe4f87 | |||
| 547c5c935c |
@@ -1,8 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using PartSource.Data.Models;
|
||||
using PartSource.Services;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PartSource.Api.Controllers
|
||||
{
|
||||
@@ -23,7 +22,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 +35,8 @@ namespace PartSource.Api.Controllers
|
||||
{
|
||||
StoreNumber = inventory.Store,
|
||||
Sku = sku,
|
||||
Quantity = inventory.QTY
|
||||
Quantity = inventory.QTY,
|
||||
Updated = inventory.Updated
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using PartSource.Data.Dtos;
|
||||
using PartSource.Data.Models;
|
||||
using PartSource.Data.Nexpart;
|
||||
using PartSource.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -41,6 +42,77 @@ namespace PartSource.Api.Controllers
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string[] segments = vehicleFitment.NoteText.Split(']');
|
||||
vehicleFitment.PartDescription = segments[0].TrimStart('[');
|
||||
vehicleFitment.DriveTypes = GetDriveTypesFromNote(vehicleFitment.NoteText);
|
||||
vehicleFitment.Notes = segments[1].Split(';')
|
||||
.Select(n => n.Trim())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
catch
|
||||
{
|
||||
throw new InvalidOperationException($"The note_text field provided by WHI for {vehicleFitment.LineCode} {vehicleFitment.PartNumber} was in an invalid format.");
|
||||
}
|
||||
|
||||
SmartPageDataSearch smartPageDataSearch = new SmartPageDataSearch
|
||||
{
|
||||
Items = new[]
|
||||
{
|
||||
new Item { PartNumber = vehicleFitment.PartNumber, MfrCode = vehicleFitment.LineCode }
|
||||
}
|
||||
};
|
||||
|
||||
SmartPageDataSearchResponse smartPageResponse = await _nexpartService.SendRequest<SmartPageDataSearch, SmartPageDataSearchResponse>(smartPageDataSearch);
|
||||
if (smartPageResponse.ResponseBody?.Item != null)
|
||||
{
|
||||
PartType[] partTypes = smartPageResponse.ResponseBody.Item.Select(i => new PartType
|
||||
{
|
||||
Id = i.Part.PartType.Id
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
ApplicationSearch applicationSearch = new ApplicationSearch
|
||||
{
|
||||
VehicleIdentifier = new VehicleIdentifier
|
||||
{
|
||||
BaseVehicleId = vehicleFitment.BaseVehicleId,
|
||||
EngineConfigId = vehicleFitment.EngineConfigId
|
||||
},
|
||||
MfrCode = new[] { vehicleFitment.LineCode },
|
||||
PartType = partTypes,
|
||||
GroupBy = "MFR",
|
||||
QuestionOption = "QUESTION_OTHERWISE_APP"
|
||||
};
|
||||
|
||||
ApplicationSearchResponse response = await _nexpartService.SendRequest<ApplicationSearch, ApplicationSearchResponse>(applicationSearch);
|
||||
if (response.ResponseBody != null && response.ResponseBody is Questions)
|
||||
{
|
||||
Question driveTypeQuestion = ((Questions)response.ResponseBody).Question
|
||||
.Where(q => q.Attribute == "DRIVE_TYPE")
|
||||
.FirstOrDefault();
|
||||
|
||||
if (driveTypeQuestion != null)
|
||||
{
|
||||
foreach (Answer answer in driveTypeQuestion.Answer)
|
||||
{
|
||||
applicationSearch.Criterion = new[]
|
||||
{
|
||||
new Criterion { Attribute = "DRIVE_TYPE", Id = answer.Id}
|
||||
};
|
||||
|
||||
ApplicationSearchResponse driveTypeResponse = await _nexpartService.SendRequest<ApplicationSearch, ApplicationSearchResponse>(applicationSearch);
|
||||
if (driveTypeResponse.ResponseBody != null && ((Apps)driveTypeResponse.ResponseBody).App.Where(a => a.Part == vehicleFitment.PartNumber).Any())
|
||||
{
|
||||
vehicleFitment.DriveTypes.Add(answer.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(vehicleFitment);
|
||||
}
|
||||
|
||||
@@ -51,15 +123,24 @@ namespace PartSource.Api.Controllers
|
||||
Part part = await _partService.GetPartBySku(sku);
|
||||
Vehicle vehicle = await _vehicleService.GetVehicleById(vehicleId);
|
||||
|
||||
if (part == null || vehicle == null)
|
||||
if (part == null)
|
||||
{
|
||||
return BadRequest(new
|
||||
{
|
||||
Message = $"No data is available for SKU {sku}. Confirm it is available in the database maintained by Sound Press.",
|
||||
Message = $"No part data is available for SKU {sku}. Confirm it is available in the database maintained by Sound Press.",
|
||||
Reason = $"{nameof(_partService.GetPartBySku)} returned null"
|
||||
});
|
||||
}
|
||||
|
||||
if (vehicle == null)
|
||||
{
|
||||
return BadRequest(new
|
||||
{
|
||||
Message = $"No vehicle data is available for vehicle ID {vehicleId}. Confirm it is available in the database maintained by Sound Press.",
|
||||
Reason = $"{nameof(_vehicleService.GetVehicleById)} returned null"
|
||||
});
|
||||
}
|
||||
|
||||
IList<DcfMapping> mappings = await _partService.GetDcfMapping(part.LineCode);
|
||||
Item[] items = mappings.Select(m => new Item
|
||||
{
|
||||
@@ -120,7 +201,7 @@ namespace PartSource.Api.Controllers
|
||||
}
|
||||
|
||||
IList<string> positions = new List<string>();
|
||||
foreach (App app in response.ResponseBody?.App)
|
||||
foreach (App app in ((Apps)response.ResponseBody)?.App)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(app.Position) && app.Part == part.PartNumber)
|
||||
{
|
||||
@@ -136,5 +217,33 @@ namespace PartSource.Api.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
private IList<string> GetDriveTypesFromNote(string fitmentNote)
|
||||
{
|
||||
fitmentNote = fitmentNote.ToUpperInvariant();
|
||||
IList<string> driveTypes = new List<string>();
|
||||
|
||||
if (fitmentNote.Contains("FWD"))
|
||||
{
|
||||
driveTypes.Add("FWD");
|
||||
}
|
||||
|
||||
if (fitmentNote.Contains("RWD"))
|
||||
{
|
||||
driveTypes.Add("RWD");
|
||||
}
|
||||
|
||||
if (fitmentNote.Contains("AWD"))
|
||||
{
|
||||
driveTypes.Add("AWD");
|
||||
}
|
||||
|
||||
if (fitmentNote.Contains("4WD"))
|
||||
{
|
||||
driveTypes.Add("4WD");
|
||||
}
|
||||
|
||||
return driveTypes;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace PartSource.Api.Controllers
|
||||
|
||||
if (response.ResponseBody != null)
|
||||
{
|
||||
return NexpartResponse<ApplicationSearchResponse, Apps>(response);
|
||||
return NexpartResponse<ApplicationSearchResponse, object>(response);
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<UserSecretsId>f9e2fd37-0f2d-4e3a-955a-8e49a16fce1c</UserSecretsId>
|
||||
<Configurations>Debug;Release;Also Debug</Configurations>
|
||||
<SatelliteResourceLanguages>en-us;en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
@@ -34,6 +35,7 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.5" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Ratermania.Shopify" Version="6.16.11" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:31337",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
@@ -18,12 +9,20 @@
|
||||
},
|
||||
"PartSource.Api": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "api/values",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:31337",
|
||||
"sslPort": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"PartSourceDatabase": "Server=tcp:ps-whi.database.windows.net,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=ps-whi;Password=9-^*N5dw!6:|.5Q;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;",
|
||||
"FitmentDatabase": "Server=tcp:ps-automation.eastus2.cloudapp.azure.com,1433;Initial Catalog=WhiFitment;User ID=automation;Password=)6L)XP%m(x-UU#M;Encrypt=True;TrustServerCertificate=True;Connection Timeout=300"
|
||||
//"FitmentDatabase": "Data Source=localhost;Initial Catalog=WhiFitment;Integrated Security=true"
|
||||
"FitmentDatabase": "Server=tcp:ps-automation.eastus2.cloudapp.azure.com,1433;Initial Catalog=WhiFitment;User ID=sa;Password=GZ0`-ekd~[2u;Encrypt=True;TrustServerCertificate=True;Connection Timeout=300"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
|
||||
@@ -8,44 +8,62 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PartSource.Automation.Models.Configuration;
|
||||
using PartSource.Automation.Models.Ftp;
|
||||
using PartSource.Automation.Services;
|
||||
using Ratermania.Automation.Interfaces;
|
||||
|
||||
namespace PartSource.Automation.Jobs.POC
|
||||
namespace PartSource.Automation.Jobs
|
||||
{
|
||||
public class BulkUpdateInventory : IAutomationJob
|
||||
{
|
||||
private readonly FtpService _ftpService;
|
||||
private readonly ILogger<BulkUpdateInventory> _logger;
|
||||
private readonly string _connectionString;
|
||||
|
||||
public BulkUpdateInventory(IConfiguration configuration)
|
||||
public BulkUpdateInventory(IConfiguration configuration, ILogger<BulkUpdateInventory> logger)
|
||||
{
|
||||
FtpConfiguration ftpConfiguration = configuration.GetSection("FtpServers:AzureConfiguration").Get<FtpConfiguration>();
|
||||
FtpConfiguration ftpConfiguration = configuration.GetSection("FtpServers:AutomationConfiguration").Get<FtpConfiguration>();
|
||||
_ftpService = new FtpService(ftpConfiguration);
|
||||
|
||||
_connectionString = configuration.GetConnectionString("PartSourceDatabase");
|
||||
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Run(CancellationToken token, params string[] arguments)
|
||||
{
|
||||
FtpFileInfo lastUploadedFile = _ftpService.ListFilesExtended()
|
||||
.Where(f => f.FileType == FtpFileType.File && f.Filename.IndexOf("Availability") > -1)
|
||||
.Where(f => f.FileType == FtpFileType.File && f.Filename.IndexOf("Availability Full") > -1)
|
||||
.OrderByDescending(f => f.Modified)
|
||||
.First();
|
||||
.FirstOrDefault();
|
||||
|
||||
string file = _ftpService.Download(lastUploadedFile.Filename, Path.GetTempPath());
|
||||
if (lastUploadedFile == null)
|
||||
{
|
||||
_logger.LogInformation($"No full inventory file available.");
|
||||
return;
|
||||
}
|
||||
|
||||
string file = _ftpService.Download(lastUploadedFile.Filename);
|
||||
|
||||
DataTable dataTable = GetDataTable(file);
|
||||
|
||||
using SqlConnection connection = new SqlConnection("Server=tcp:ps-automation-stage.eastus2.cloudapp.azure.com,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=stageuser;Password=]FXepK^cFYS|[H<;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=True;Connection Timeout=30;");
|
||||
using SqlConnection connection = new SqlConnection(_connectionString);
|
||||
connection.Open();
|
||||
|
||||
using SqlCommand command = new SqlCommand("TRUNCATE TABLE PartAvailability", connection);
|
||||
await command.ExecuteNonQueryAsync(token);
|
||||
|
||||
using SqlBulkCopy bulk = new SqlBulkCopy(connection)
|
||||
{
|
||||
DestinationTableName = $"PartAvailability",
|
||||
DestinationTableName = "PartAvailability",
|
||||
BulkCopyTimeout = 14400
|
||||
};
|
||||
|
||||
bulk.WriteToServer(dataTable);
|
||||
await bulk.WriteToServerAsync(dataTable, token);
|
||||
|
||||
_ftpService.Delete(lastUploadedFile.Filename);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -56,6 +74,7 @@ namespace PartSource.Automation.Jobs.POC
|
||||
dataTable.Columns.Add("Store", typeof(int));
|
||||
dataTable.Columns.Add("SKU", typeof(string));
|
||||
dataTable.Columns.Add("QTY", typeof(int));
|
||||
dataTable.Columns.Add("Updated", typeof(string));
|
||||
|
||||
using StreamReader reader = new StreamReader(filename);
|
||||
string line = reader.ReadLine(); // Burn the header row
|
||||
@@ -71,11 +90,14 @@ namespace PartSource.Automation.Jobs.POC
|
||||
}
|
||||
|
||||
string sku = columns[1].Trim();
|
||||
string updated = columns[3].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)
|
||||
&& !string.IsNullOrEmpty(updated))
|
||||
{
|
||||
dataTable.Rows.Add(new object[] { store, sku, quantity });
|
||||
dataTable.Rows.Add(new object[] { store, sku, quantity, updated });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PartSource.Automation.Models.Configuration;
|
||||
using PartSource.Automation.Models.Ftp;
|
||||
using PartSource.Automation.Services;
|
||||
using Ratermania.Automation.Interfaces;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -17,11 +19,11 @@ namespace PartSource.Automation.Jobs
|
||||
private readonly ILogger<ExecuteSsisPackages> _logger;
|
||||
|
||||
// TODO: set from config
|
||||
private readonly string[] _ssisPackages = { "Parts Availability" };
|
||||
private readonly string[] _ssisPackages = {"Parts Price" };
|
||||
|
||||
public ExecuteSsisPackages(EmailService emailService, IConfiguration configuration, SsisService ssisService, ILogger<ExecuteSsisPackages> logger)
|
||||
{
|
||||
FtpConfiguration ftpConfiguration = configuration.GetSection("FtpServers:AzureConfiguration").Get<FtpConfiguration>();
|
||||
FtpConfiguration ftpConfiguration = configuration.GetSection("FtpServers:AutomationConfiguration").Get<FtpConfiguration>();
|
||||
|
||||
_emailService = emailService;
|
||||
_ftpService = new FtpService(ftpConfiguration);
|
||||
@@ -36,6 +38,17 @@ namespace PartSource.Automation.Jobs
|
||||
{
|
||||
try
|
||||
{
|
||||
FtpFileInfo lastUploadedFile = _ftpService.ListFilesExtended()
|
||||
.Where(f => f.FileType == FtpFileType.File && f.Filename.IndexOf(package) > -1)
|
||||
.OrderByDescending(f => f.Modified)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (lastUploadedFile == null)
|
||||
{
|
||||
_logger.LogInformation($"No {package} file available.");
|
||||
return;
|
||||
}
|
||||
|
||||
_ftpService.Download($"{package}.txt");
|
||||
_ssisService.Execute($"{package}.dtsx");
|
||||
|
||||
@@ -45,7 +58,6 @@ namespace PartSource.Automation.Jobs
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"Execution of SSIS package {package} failed.", ex);
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
95
PartSource.Automation/Jobs/POC/ImageList.cs
Normal file
95
PartSource.Automation/Jobs/POC/ImageList.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json;
|
||||
using PartSource.Data.Contexts;
|
||||
using PartSource.Data.Models;
|
||||
using PartSource.Data.Nexpart;
|
||||
using PartSource.Services;
|
||||
using Ratermania.Automation.Interfaces;
|
||||
using Ratermania.Shopify;
|
||||
using Ratermania.Shopify.Resources;
|
||||
|
||||
namespace PartSource.Automation.Jobs.POC
|
||||
{
|
||||
public class ImageList : IAutomationJob
|
||||
{
|
||||
private readonly NexpartService _nexpartService;
|
||||
private readonly PartSourceContext _partSourceContext;
|
||||
private readonly FitmentContext _fitmentContext;
|
||||
|
||||
public ImageList(NexpartService nexpartService, PartSourceContext partSourceContext, FitmentContext fitmentContext)
|
||||
{
|
||||
_nexpartService = nexpartService;
|
||||
_partSourceContext = partSourceContext;
|
||||
_fitmentContext = fitmentContext;
|
||||
}
|
||||
|
||||
public async Task Run(CancellationToken token, params string[] arguments)
|
||||
{
|
||||
IList<string> rows = new List<string> {
|
||||
"\"Line Code\", \"Part Number\", \"Image URL(s)\""
|
||||
};
|
||||
|
||||
IList<Data.Models.Part> parts = await _fitmentContext.Parts.ToListAsync();
|
||||
string oldLineCode = string.Empty;
|
||||
IList<DcfMapping> mappings = new List<DcfMapping>();
|
||||
|
||||
foreach (Data.Models.Part part in parts)
|
||||
{
|
||||
if (part.LineCode != oldLineCode)
|
||||
{
|
||||
mappings = await _fitmentContext.DcfMappings.Where(d => d.LineCode == part.LineCode).ToListAsync();
|
||||
}
|
||||
;
|
||||
|
||||
foreach (DcfMapping mapping in mappings)
|
||||
{
|
||||
SmartPageDataSearch dataSearch = new SmartPageDataSearch
|
||||
{
|
||||
Items = new Item[]
|
||||
{
|
||||
new Item
|
||||
{
|
||||
MfrCode = mapping.WhiCode,
|
||||
PartNumber = part.PartNumber
|
||||
}
|
||||
},
|
||||
DataOption = new[] { "ALL" }
|
||||
};
|
||||
|
||||
SmartPageDataSearchResponse response = await _nexpartService.SendRequest<SmartPageDataSearch, SmartPageDataSearchResponse>(dataSearch);
|
||||
|
||||
if (response.ResponseBody.Item?.Length > 0)
|
||||
{
|
||||
List<string> urls = new List<string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(response.ResponseBody.Item[0].PrimaryImg?.ImgUrl))
|
||||
{
|
||||
urls.Add(response.ResponseBody.Item[0].PrimaryImg?.ImgUrl);
|
||||
};
|
||||
|
||||
if (response.ResponseBody.Item[0].AddImgs?.AddImg?.Length > 0)
|
||||
{
|
||||
urls.AddRange(response.ResponseBody.Item[0].AddImgs.AddImg.Select(i => i.AddImgUrl));
|
||||
}
|
||||
|
||||
if (urls.Count > 0)
|
||||
{
|
||||
rows.Add($"\"{part.LineCode}\", \"{part.PartNumber}\", \"{string.Join(";", urls)}\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await File.WriteAllLinesAsync("C:\\users\\Tommy\\desktop\\WHI Images.csv", rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PartSource.Automation.Models.Configuration;
|
||||
using PartSource.Automation.Models.Ftp;
|
||||
using PartSource.Automation.Services;
|
||||
using Ratermania.Automation.Interfaces;
|
||||
|
||||
namespace PartSource.Automation.Jobs.POC
|
||||
{
|
||||
public class PartialInventoryUpdate : IAutomationJob
|
||||
{
|
||||
private readonly FtpService _ftpService;
|
||||
private readonly ILogger<PartialInventoryUpdate> _logger;
|
||||
|
||||
public PartialInventoryUpdate(IConfiguration configuration, ILogger<PartialInventoryUpdate> logger)
|
||||
{
|
||||
FtpConfiguration ftpConfiguration = configuration.GetSection("FtpServers:AutomationConfiguration").Get<FtpConfiguration>();
|
||||
_ftpService = new FtpService(ftpConfiguration);
|
||||
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Run(CancellationToken token, params string[] arguments)
|
||||
{
|
||||
FtpFileInfo lastUploadedFile = _ftpService.ListFilesExtended()
|
||||
.Where(f => f.FileType == FtpFileType.File && f.Modified >= DateTime.Now.AddHours(-24) && f.Filename.IndexOf("Availability Partial") > -1)
|
||||
.OrderByDescending(f => f.Modified)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (lastUploadedFile == null)
|
||||
{
|
||||
_logger.LogInformation($"No partial inventory file available for the time period {DateTime.Now.AddHours(-24)} - {DateTime.Now}");
|
||||
return;
|
||||
}
|
||||
|
||||
string file = _ftpService.Download($"{lastUploadedFile.Filename}", "C:\\Users\\Tom\\Desktop");
|
||||
|
||||
|
||||
using SqlConnection connection = new SqlConnection("Server=tcp:ps-automation-stage.eastus2.cloudapp.azure.com,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=stageuser;Password=]FXepK^cFYS|[H<;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=True;Connection Timeout=30;");
|
||||
connection.Open();
|
||||
|
||||
using StreamReader reader = new StreamReader(file);
|
||||
string line = reader.ReadLine(); // Burn the header row
|
||||
|
||||
while (reader.Peek() > 0)
|
||||
{
|
||||
line = reader.ReadLine();
|
||||
|
||||
string[] columns = line.Split(",");
|
||||
for (int i = 0; i < columns.Length; i++)
|
||||
{
|
||||
columns[i] = columns[i].Replace("\"", string.Empty);
|
||||
}
|
||||
|
||||
if (int.TryParse(columns[0], out int store)
|
||||
&& int.TryParse(columns[1], out int quantity)
|
||||
&& int.TryParse(columns[2], out int sku))
|
||||
{
|
||||
using SqlCommand sqlCommand = new SqlCommand("UPDATE Inventory 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));
|
||||
|
||||
await sqlCommand.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ namespace PartSource.Automation.Jobs.POC
|
||||
ApplicationSearchResponse response = await _nexpartService.SendRequest<ApplicationSearch, ApplicationSearchResponse>(applicationSearch);
|
||||
if (response.ResponseBody != null)
|
||||
{
|
||||
foreach (App app in response.ResponseBody.App)
|
||||
foreach (App app in ((Apps)response.ResponseBody).App)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace PartSource.Automation.Jobs.POC
|
||||
ApplicationSearchResponse response = await _nexpartService.SendRequest<ApplicationSearch, ApplicationSearchResponse>(applicationSearch);
|
||||
if (response.ResponseBody != null)
|
||||
{
|
||||
foreach (App app in response.ResponseBody.App)
|
||||
foreach (App app in ((Apps)response.ResponseBody).App)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
119
PartSource.Automation/Jobs/PartialInventoryUpdate.cs
Normal file
119
PartSource.Automation/Jobs/PartialInventoryUpdate.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PartSource.Automation.Models.Configuration;
|
||||
using PartSource.Automation.Models.Ftp;
|
||||
using PartSource.Automation.Services;
|
||||
using Ratermania.Automation.Interfaces;
|
||||
|
||||
namespace PartSource.Automation.Jobs
|
||||
{
|
||||
public class PartialInventoryUpdate : IAutomationJob
|
||||
{
|
||||
private readonly FtpService _ftpService;
|
||||
private readonly ILogger<PartialInventoryUpdate> _logger;
|
||||
private readonly string _connectionString;
|
||||
|
||||
public PartialInventoryUpdate(IConfiguration configuration, ILogger<PartialInventoryUpdate> logger)
|
||||
{
|
||||
FtpConfiguration ftpConfiguration = configuration.GetSection("FtpServers:AutomationConfiguration").Get<FtpConfiguration>();
|
||||
_ftpService = new FtpService(ftpConfiguration);
|
||||
|
||||
_connectionString = _connectionString = configuration.GetConnectionString("PartSourceDatabase");
|
||||
|
||||
_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()
|
||||
.Where(f => f.FileType == FtpFileType.File && f.Filename.IndexOf("Availability Partial") > -1)
|
||||
.OrderByDescending(f => f.Modified)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (lastUploadedFile == null)
|
||||
{
|
||||
_logger.LogInformation($"No partial inventory file available.");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Processing {filename}", lastUploadedFile.Filename);
|
||||
|
||||
string file = _ftpService.Download($"{lastUploadedFile.Filename}");
|
||||
|
||||
using SqlConnection connection = new SqlConnection(_connectionString);
|
||||
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 j = 0; j < columns.Length; j++)
|
||||
{
|
||||
columns[j] = columns[j].Replace("\"", string.Empty);
|
||||
}
|
||||
string sku = columns[1].Trim();
|
||||
string updated = columns[3].Trim();
|
||||
|
||||
if (int.TryParse(columns[0], out int store)
|
||||
&& !string.IsNullOrEmpty(sku)
|
||||
&& int.TryParse(columns[2], out int quantity)
|
||||
&& !string.IsNullOrEmpty(updated))
|
||||
{
|
||||
command += $"UPDATE PartAvailability SET QTY = @qty_{i}, Updated = @updated_{i} WHERE SKU = @sku_{i} AND Store = @store_{i};";
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
89
PartSource.Automation/Jobs/PartsSync.cs
Normal file
89
PartSource.Automation/Jobs/PartsSync.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PartSource.Automation.Models.Jobs;
|
||||
using PartSource.Automation.Services;
|
||||
using PartSource.Data.Contexts;
|
||||
using PartSource.Data.Models;
|
||||
using Ratermania.Automation.Interfaces;
|
||||
using Ratermania.Shopify;
|
||||
using Ratermania.Shopify.Resources;
|
||||
using Ratermania.Shopify.Resources.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Mail;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PartSource.Automation.Jobs
|
||||
{
|
||||
public class PartsSync : IAutomationJob
|
||||
{
|
||||
private readonly ILogger<UpdatePricing> _logger;
|
||||
private readonly FitmentContext _fitmentContext;
|
||||
private readonly ShopifyClient _shopifyClient;
|
||||
|
||||
public PartsSync(ILogger<UpdatePricing> logger, FitmentContext fitmentContext, ShopifyClient shopifyClient)
|
||||
{
|
||||
_logger = logger;
|
||||
_fitmentContext = fitmentContext;
|
||||
_shopifyClient = shopifyClient;
|
||||
}
|
||||
|
||||
public async Task Run(CancellationToken token, params string[] arguments)
|
||||
{
|
||||
IEnumerable<Product> products = await _shopifyClient.Products.Get(new Dictionary<string, object> { { "limit", 250 } });
|
||||
|
||||
while (products != null && products.Any())
|
||||
{
|
||||
|
||||
foreach (Product product in products)
|
||||
{
|
||||
try
|
||||
{
|
||||
IEnumerable<Metafield> metafields = await _shopifyClient.Metafields.Get(new Dictionary<string, object> { { "metafield[owner_id]", product.Id }, { "metafield[owner_resource]", "product" } });
|
||||
Part part = new Part
|
||||
{
|
||||
LineCode = metafields.FirstOrDefault(m => m.Key == "custom_label_0")?.Value ?? string.Empty,
|
||||
PartNumber = metafields.FirstOrDefault(m => m.Key == "custom_label_1")?.Value ?? string.Empty,
|
||||
Sku = product.Variants[0].Sku // They know we can't do fitment for variants
|
||||
};
|
||||
|
||||
// part.PartNumber = part.PartNumber.Replace("-", string.Empty);
|
||||
|
||||
if (
|
||||
string.IsNullOrEmpty(part.LineCode)
|
||||
|| string.IsNullOrEmpty(part.PartNumber)
|
||||
|| int.TryParse(part.LineCode, out _)) //If the line code is numeric, it cannot have fitment data associated with it.
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Part? existing = await _fitmentContext.Parts.FirstOrDefaultAsync(p => p.Sku == part.Sku);
|
||||
if (existing == null)
|
||||
{
|
||||
await _fitmentContext.Parts.AddAsync(part);
|
||||
await _fitmentContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogInformation(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
products = await _shopifyClient.Products.GetNext();
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogInformation(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,24 +56,25 @@ namespace PartSource.Automation.Jobs
|
||||
fileGroups.Enqueue(fileGroup);
|
||||
}
|
||||
|
||||
Task[] taskArray = new Task[8];
|
||||
|
||||
Task[] taskArray = new Task[18];
|
||||
for (int i = 0; i < taskArray.Length; i++)
|
||||
{
|
||||
taskArray[i] = Task.Factory.StartNew(() =>
|
||||
{
|
||||
while (fileGroups.TryDequeue(out IGrouping<string, FileInfo> fileGroup))
|
||||
{
|
||||
string tableName = string.Empty;
|
||||
|
||||
foreach (FileInfo fileInfo in fileGroup)
|
||||
{
|
||||
try
|
||||
{
|
||||
string filename = Decompress(fileInfo);
|
||||
string tableName = fileInfo.Name.Substring(0, fileInfo.Name.IndexOf('.'));
|
||||
DataTable dataTable = GetDataTable(filename, out tableName);
|
||||
|
||||
DataTable dataTable = GetDataTable(filename);
|
||||
string tempTable = $"Fitment_{Guid.NewGuid():N}_{tableName}";
|
||||
|
||||
_whiSeoService.BulkCopyFitment(dataTable, tableName);
|
||||
_whiSeoService.BulkCopyFitment(dataTable, tempTable);
|
||||
_logger.LogInformation($"Copied {fileInfo.Name} to the database.");
|
||||
|
||||
File.Delete(filename);
|
||||
@@ -85,19 +86,18 @@ namespace PartSource.Automation.Jobs
|
||||
}
|
||||
}
|
||||
|
||||
string fitmentTable = fileGroup.Key.Substring(0, fileGroup.Key.IndexOf('.'));
|
||||
_whiSeoService.CreateFitmentTable(fitmentTable);
|
||||
_whiSeoService.CreateFitmentTable(tableName);
|
||||
|
||||
_logger.LogInformation($"Created fitment table for part group {fitmentTable}.");
|
||||
_logger.LogInformation($"Created fitment table for part group {tableName}.");
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Task.WaitAll(taskArray);
|
||||
|
||||
_whiSeoService.SaveNotes(_noteDictionary);
|
||||
//_whiSeoService.CreateFitmentView();
|
||||
|
||||
_whiSeoService.CreateFitmentView();
|
||||
}
|
||||
|
||||
public string Decompress(FileInfo fileInfo)
|
||||
@@ -112,8 +112,10 @@ namespace PartSource.Automation.Jobs
|
||||
return decompressedFile;
|
||||
}
|
||||
|
||||
private DataTable GetDataTable(string filename)
|
||||
private DataTable GetDataTable(string filename, out string lineCode)
|
||||
{
|
||||
lineCode = string.Empty;
|
||||
|
||||
using DataTable dataTable = new DataTable();
|
||||
dataTable.Columns.Add("LineCode", typeof(string));
|
||||
dataTable.Columns.Add("PartNumber", typeof(string));
|
||||
@@ -121,6 +123,7 @@ namespace PartSource.Automation.Jobs
|
||||
dataTable.Columns.Add("EngineConfigId", typeof(int));
|
||||
dataTable.Columns.Add("Position", typeof(string));
|
||||
dataTable.Columns.Add("FitmentNoteHash", typeof(string));
|
||||
dataTable.Columns.Add("PartTerminologyId", typeof(int));
|
||||
|
||||
using StreamReader reader = new StreamReader(filename);
|
||||
string line = reader.ReadLine(); // Burn the header row
|
||||
@@ -135,7 +138,7 @@ namespace PartSource.Automation.Jobs
|
||||
columns[i] = columns[i].Replace("\"", string.Empty);
|
||||
}
|
||||
|
||||
string lineCode = Regex.Replace(columns[0], "[^a-zA-Z0-9]", string.Empty).Trim();
|
||||
lineCode = Regex.Replace(columns[0], "[^a-zA-Z0-9]", string.Empty).Trim();
|
||||
string partNumber = Regex.Replace(columns[1], "[^a-zA-Z0-9]", string.Empty).Trim();
|
||||
string position = columns[7].Trim();
|
||||
|
||||
@@ -149,10 +152,11 @@ namespace PartSource.Automation.Jobs
|
||||
|
||||
if (!string.IsNullOrEmpty(lineCode)
|
||||
&& !string.IsNullOrEmpty(partNumber)
|
||||
&& int.TryParse(columns[2], out int partTerminologyId)
|
||||
&& int.TryParse(columns[5], out int baseVehicleId)
|
||||
&& int.TryParse(columns[6], out int engineConfigId))
|
||||
{
|
||||
dataTable.Rows.Add(new object[] { lineCode, partNumber, baseVehicleId, engineConfigId, position, noteTextHash });
|
||||
dataTable.Rows.Add(new object[] { lineCode, partNumber, baseVehicleId, engineConfigId, position, noteTextHash, partTerminologyId });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PartSource.Automation.Extensions;
|
||||
using PartSource.Automation.Models.Configuration;
|
||||
using PartSource.Automation.Models.Enums;
|
||||
using PartSource.Automation.Services;
|
||||
using Ratermania.Automation.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PartSource.Automation.Jobs
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace PartSource.Automation.Jobs
|
||||
#pragma warning disable CS1998, CA1303
|
||||
public async Task Run(CancellationToken token, params string[] arguments)
|
||||
{
|
||||
// _emailService.Send("Automation Test Message", "This is a test email from the automation server. If this message was in your spam folder, whitelist the address that sent this email.");
|
||||
_emailService.Send("Automation Test Message", "This is a test email from the automation server. If this message was in your spam folder, whitelist the address that sent this email.");
|
||||
|
||||
_logger.LogInformation("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc scelerisque congue euismod. Curabitur enim eros, sollicitudin ac purus eget, dignissim mattis augue. In quam sapien, tincidunt et elementum vitae, interdum vitae sem.");
|
||||
_logger.LogWarning("Praesent feugiat sapien non suscipit faucibus. Mauris fermentum ut augue a feugiat. Integer felis sem, laoreet et augue at, finibus maximus ex. Fusce sit amet erat non tortor porta condimentum condimentum quis ipsum.");
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using PartSource.Automation.Services;
|
||||
@@ -13,6 +14,7 @@ using PartSource.Data.Models;
|
||||
using Ratermania.Automation.Interfaces;
|
||||
using Ratermania.Shopify;
|
||||
using Ratermania.Shopify.Resources;
|
||||
using System.Web;
|
||||
|
||||
namespace PartSource.Automation.Jobs
|
||||
{
|
||||
@@ -36,15 +38,23 @@ namespace PartSource.Automation.Jobs
|
||||
public async Task Run(CancellationToken token, params string[] arguments)
|
||||
{
|
||||
|
||||
IEnumerable<Product> products = null;
|
||||
IList<string> productTypes = await _fitmentContext.ProductTypes
|
||||
.Where(p => p.Active)
|
||||
.Select(p => HttpUtility.UrlEncode(p.Name))
|
||||
.ToListAsync();
|
||||
|
||||
foreach (string productType in productTypes)
|
||||
{
|
||||
_logger.LogInformation("Processing {productType}", HttpUtility.UrlDecode(productType));
|
||||
|
||||
IEnumerable<Product> products = null;
|
||||
try
|
||||
{
|
||||
//products = await _shopifyClient.Products.Get(new Dictionary<string, object> { { "limit", 250 } });
|
||||
products = new List<Product>
|
||||
{
|
||||
await _shopifyClient.Products.GetById(7285013446703)
|
||||
};
|
||||
products = await _shopifyClient.Products.Get(new Dictionary<string, object> { { "limit", 250 }, { "product_type", productType } });
|
||||
//products = new List<Product>
|
||||
//{
|
||||
// await _shopifyClient.Products.GetById(7458071052335)
|
||||
//};
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
@@ -53,13 +63,12 @@ namespace PartSource.Automation.Jobs
|
||||
throw;
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
|
||||
while (products != null && products.Any())
|
||||
{
|
||||
foreach (Product product in products)
|
||||
{
|
||||
ImportData importData = null;
|
||||
bool isFitment = false;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -71,46 +80,33 @@ namespace PartSource.Automation.Jobs
|
||||
VariantSku = product.Variants[0].Sku // They know we can't do fitment for variants
|
||||
};
|
||||
|
||||
bool isFitment = false;
|
||||
importData.PartNumber = importData.PartNumber.Replace("-", string.Empty);
|
||||
|
||||
//If the line code is numeric, it cannot have fitment data associated with it.
|
||||
if (int.TryParse(importData.LineCode, out _))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract Partsource bullet points if present.
|
||||
string bodyHtml = string.IsNullOrEmpty(product.BodyHtml)
|
||||
? "<ul></ul>"
|
||||
? string.Empty
|
||||
: product.BodyHtml.Substring(0, product.BodyHtml.IndexOf("</ul>") + "</ul>".Length);
|
||||
|
||||
IList<Vehicle> vehicles = await _vehicleFitmentService.GetVehiclesForPart(importData.PartNumber, importData.LineCode);
|
||||
IList<int> vehicleIdFitment = _vehicleFitmentService.GetVehicleIdFitment(vehicles);
|
||||
|
||||
if (vehicleIdFitment.Count == 0)
|
||||
if (!vehicleIdFitment.Any())
|
||||
{
|
||||
Console.WriteLine($"No fitment data for {importData.LineCode} {importData.PartNumber}");
|
||||
continue;
|
||||
}
|
||||
|
||||
string vehicleIdString = string.Join(',', vehicleIdFitment.Select(j => $"v{j}"));
|
||||
|
||||
bodyHtml += $"<div id=\"vehicleIDs\" style=\"display:none;\">{vehicleIdString}</div>";
|
||||
|
||||
isFitment = true;
|
||||
|
||||
string json = JsonConvert.SerializeObject(vehicleIdFitment);
|
||||
if (json.Length < 100000)
|
||||
{
|
||||
Metafield vehicleMetafield = new Metafield
|
||||
{
|
||||
Namespace = "fitment",
|
||||
Key = "ids",
|
||||
Value = json,
|
||||
Type = "json_string",
|
||||
OwnerResource = "product",
|
||||
OwnerId = product.Id
|
||||
};
|
||||
|
||||
await _shopifyClient.Metafields.Add(vehicleMetafield);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
_logger.LogWarning($"Vehicle ID fitment data for SKU {importData.VariantSku} is too large for Shopify and cannot be added.");
|
||||
continue;
|
||||
}
|
||||
|
||||
IList<string> ymmFitment = _vehicleFitmentService.GetYmmFitment(vehicles);
|
||||
if (ymmFitment.Count > 0)
|
||||
{
|
||||
@@ -124,79 +120,20 @@ namespace PartSource.Automation.Jobs
|
||||
try
|
||||
{
|
||||
string[] parts = fitment.Split(' ', 2);
|
||||
|
||||
stringBuilder.AppendLine($"<tr><td>{parts[1]}</td><td>{parts[0].Replace("-", ", ")}</td></tr>");
|
||||
}
|
||||
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
// This is still a POC at this point. Oh well...
|
||||
_logger.LogWarning(ex, "YMM fitment for {fitment} was in an invalid format", fitment);
|
||||
}
|
||||
}
|
||||
|
||||
stringBuilder.AppendLine("</table>");
|
||||
|
||||
bodyHtml += $"<div id=\"seoData\">{stringBuilder.ToString()}</div>";
|
||||
|
||||
json = JsonConvert.SerializeObject(ymmFitment);
|
||||
if (json.Length < 100000)
|
||||
{
|
||||
Metafield ymmMetafield = new Metafield
|
||||
{
|
||||
Namespace = "fitment",
|
||||
Key = "seo",
|
||||
Value = json,
|
||||
Type = "json_string",
|
||||
OwnerResource = "product",
|
||||
OwnerId = product.Id
|
||||
};
|
||||
|
||||
await _shopifyClient.Metafields.Add(ymmMetafield);
|
||||
bodyHtml += $"<div id=\"seoData\">{stringBuilder}</div>";
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
_logger.LogWarning($"Year/make/model fitment data for SKU {importData.VariantSku} is too large for Shopify and cannot be added.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Metafield isFitmentMetafield = new Metafield
|
||||
{
|
||||
Namespace = "Flags",
|
||||
Key = "IsFitment",
|
||||
Value = isFitment.ToString(),
|
||||
Type = "string",
|
||||
OwnerResource = "product",
|
||||
OwnerId = product.Id
|
||||
};
|
||||
|
||||
await _shopifyClient.Metafields.Add(isFitmentMetafield);
|
||||
|
||||
//Metafield lineCodeMetafield = new Metafield
|
||||
//{
|
||||
// Namespace = "google",
|
||||
// Key = "custom_label_0",
|
||||
// Value = importData.LineCode,
|
||||
// Type = "string",
|
||||
// OwnerResource = "product",
|
||||
// OwnerId = product.Id
|
||||
//};
|
||||
|
||||
//await _shopifyClient.Metafields.Add(lineCodeMetafield);
|
||||
|
||||
//Metafield partNumberMetafield = new Metafield
|
||||
//{
|
||||
// Namespace = "google",
|
||||
// Key = "custom_label_1",
|
||||
// Value = importData.PartNumber,
|
||||
// Type = "string",
|
||||
// OwnerResource = "product",
|
||||
// OwnerId = product.Id
|
||||
//};
|
||||
|
||||
//await _shopifyClient.Metafields.Add(partNumberMetafield);
|
||||
|
||||
List<string> tags = new List<string>();
|
||||
|
||||
for (int j = 0; j < vehicleIdFitment.Count; j += 25)
|
||||
@@ -234,12 +171,8 @@ namespace PartSource.Automation.Jobs
|
||||
}
|
||||
try
|
||||
{
|
||||
Console.WriteLine(i);
|
||||
|
||||
_partSourceContext.SaveChanges();
|
||||
products = await _shopifyClient.Products.GetNext();
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
@@ -250,4 +183,5 @@ namespace PartSource.Automation.Jobs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,8 @@ namespace PartSource.Automation.Jobs
|
||||
|
||||
try
|
||||
{
|
||||
_shopifyClient.BulkActions.Add(new Metafield
|
||||
|
||||
await _shopifyClient.Metafields.Add(new Metafield
|
||||
{
|
||||
Namespace = "Pricing",
|
||||
Key = "CorePrice",
|
||||
@@ -85,24 +86,16 @@ namespace PartSource.Automation.Jobs
|
||||
OwnerResource = "product",
|
||||
OwnerId = product.Id
|
||||
});
|
||||
|
||||
await _shopifyClient.Products.Update(product);
|
||||
|
||||
_logger.LogInformation("Updated product id {productId}", product.Id);
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, $"Failed to update pricing for product ID {product.Id}");
|
||||
_logger.LogWarning(ex, "Failed to update pricing for product ID {productId}", product.Id);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_shopifyClient.BulkActions.Update(product);
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, $"Failed to update pricing for product ID {product.Id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,14 +112,9 @@ namespace PartSource.Automation.Jobs
|
||||
_logger.LogWarning(ex, "Failed to get the next set of products. Retrying");
|
||||
products = await _shopifyClient.Products.GetPrevious();
|
||||
}
|
||||
}
|
||||
|
||||
while (_shopifyClient.BulkActions.PendingCount() > 0)
|
||||
{
|
||||
await Task.Delay(15 * 1000, token);
|
||||
_logger.LogInformation(_shopifyClient.BulkActions.PendingCount().ToString());
|
||||
_emailService.Send("Pricing Update Completed", $"The pricing update has completed.");
|
||||
}
|
||||
// _emailService.Send("Pricing Update Completed", $"The pricing update has completed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,5 +16,7 @@ namespace PartSource.Automation.Models.Configuration
|
||||
public string Username { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public int Port { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
using AutoMapper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PartSource.Automation.Jobs;
|
||||
using PartSource.Automation.Jobs.POC;
|
||||
using PartSource.Automation.Services;
|
||||
using PartSource.Data;
|
||||
using PartSource.Data.AutoMapper;
|
||||
using PartSource.Data.Contexts;
|
||||
using PartSource.Services;
|
||||
using Ratermania.Automation.DependencyInjection;
|
||||
using Ratermania.Automation.Logging;
|
||||
using Ratermania.Shopify.DependencyInjection;
|
||||
using System;
|
||||
using System.IO;
|
||||
@@ -22,7 +18,8 @@ namespace PartSource.Automation
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static async Task Main(string[] args){
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
using IHost host = CreateHostBuilder().Build();
|
||||
@@ -66,7 +63,7 @@ namespace PartSource.Automation
|
||||
{
|
||||
options.ApiKey = builder.Configuration["Shopify:ApiKey"];
|
||||
options.ApiSecret = builder.Configuration["Shopify:ApiSecret"];
|
||||
options.ApiVersion = "2022-10";
|
||||
options.ApiVersion = "2024-10";
|
||||
options.ShopDomain = builder.Configuration["Shopify:ShopDomain"];
|
||||
|
||||
//options.ApiKey = "9a533dad460321c6ce8f30bf5b8691ed";
|
||||
@@ -78,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)))
|
||||
@@ -95,10 +92,23 @@ namespace PartSource.Automation
|
||||
//.HasJob<StatusCheck>(options => options.HasInterval(new TimeSpan(24, 0, 0))
|
||||
// .StartsAt(DateTime.Parse("2021-04-01 08:00:00"))
|
||||
//)
|
||||
.HasJob<ProcessWhiVehicles>(options =>
|
||||
//.HasJob<ExecuteSsisPackages>(options =>
|
||||
// options.HasInterval(new TimeSpan(24, 0, 0))
|
||||
// .StartsAt(DateTime.Today.AddHours(-24))
|
||||
// )
|
||||
.HasJob<UpdatePricing>(options =>
|
||||
options.HasInterval(new TimeSpan(24, 0, 0))
|
||||
.StartsAt(DateTime.Today)
|
||||
.StartsAt(DateTime.Today.AddHours(-22))
|
||||
//.HasDependency<ExecuteSsisPackages>()
|
||||
);
|
||||
//.HasJob<BulkUpdateInventory>(options =>
|
||||
// options.HasInterval(new TimeSpan(1, 0, 0))
|
||||
// .StartsAt(DateTime.Today.AddHours(-27))
|
||||
// );
|
||||
//.HasJob<TestJob>(options =>
|
||||
// options.HasInterval(new TimeSpan(1, 0, 0))
|
||||
// .StartsAt(DateTime.Today.AddHours(-27).AddMinutes(30))
|
||||
//);
|
||||
|
||||
//.HasJob<PartialInventoryUpdate>(options => options.HasInterval(new TimeSpan(1, 0, 0))
|
||||
//.HasDependency<ExecuteSsisPackages>()
|
||||
|
||||
@@ -85,8 +85,6 @@ namespace PartSource.Automation.Services
|
||||
|
||||
public string Download(string filename, string destination = null)
|
||||
{
|
||||
|
||||
|
||||
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri($"{_ftpConfiguration.Url}/{filename}"));
|
||||
request.Credentials = new NetworkCredential(_ftpConfiguration.Username, _ftpConfiguration.Password);
|
||||
request.Method = WebRequestMethods.Ftp.DownloadFile;
|
||||
@@ -116,5 +114,15 @@ namespace PartSource.Automation.Services
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
public void Delete(string filename)
|
||||
{
|
||||
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri($"{_ftpConfiguration.Url}/{filename}"));
|
||||
request.Credentials = new NetworkCredential(_ftpConfiguration.Username, _ftpConfiguration.Password);
|
||||
request.Method = WebRequestMethods.Ftp.DeleteFile;
|
||||
|
||||
|
||||
using FtpWebResponse response = (FtpWebResponse)request.GetResponse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,9 @@ namespace PartSource.Automation.Services
|
||||
|
||||
public IList<int> GetVehicleIdFitment(IList<Vehicle> vehicles)
|
||||
{
|
||||
return vehicles.Select(v => v.VehicleToEngineConfigId).Distinct().ToArray();
|
||||
return vehicles != null
|
||||
? vehicles.Select(v => v.VehicleToEngineConfigId).Distinct().ToArray()
|
||||
: new List<int>();
|
||||
}
|
||||
|
||||
public async Task<IList<Vehicle>> GetVehiclesForPart(string partNumber, string lineCode, int maxVehicles = 0)
|
||||
|
||||
@@ -5,10 +5,12 @@ using Microsoft.Extensions.Logging;
|
||||
using PartSource.Automation.Models.Configuration;
|
||||
using PartSource.Automation.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PartSource.Automation.Services
|
||||
{
|
||||
@@ -31,26 +33,11 @@ namespace PartSource.Automation.Services
|
||||
public void GetFiles(SeoDataType seoDataType)
|
||||
{
|
||||
string seoDataTypeString = seoDataType.ToString().ToLowerInvariant();
|
||||
string[] files = _ftpService.ListFiles(seoDataTypeString);
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (file.Contains(".csv"))
|
||||
{
|
||||
try
|
||||
{
|
||||
_ftpService.Download($"{seoDataTypeString}/{file}");
|
||||
_logger.LogInformation($"Finished downloading {file}.");
|
||||
}
|
||||
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning($"Failed to download {file}, quitting", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
// WHI changed the transfer protocol to SFTP and then messed with the directory structure.
|
||||
// Since fitment isn't really all that automated anyway, just download the files manually with an SFTP client.
|
||||
Console.WriteLine($"Remember to manually download the {seoDataTypeString} files with an SFTP client. Press any key to continue.");
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
public void TruncateVehicleTable()
|
||||
@@ -153,7 +140,7 @@ namespace PartSource.Automation.Services
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
using SqlCommand command2 = new SqlCommand($"exec CreateFitmentIndexes", connection);
|
||||
command.CommandTimeout = 1800;
|
||||
command.CommandTimeout = 3600;
|
||||
command2.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,28 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"FitmentDatabase": "Data Source=localhost;Initial Catalog=WhiFitment;Integrated Security=true;TrustServerCertificate=True",
|
||||
"FitmentDatabase": "Data Source=localhost;Initial Catalog=WhiFitment;Integrated Security=true;TrustServerCertificate=true",
|
||||
//"FitmentDatabase": "Server=tcp:ps-automation.eastus2.cloudapp.azure.com,1433;Initial Catalog=WhiFitment;User ID=sa;Password=GZ0`-ekd~[2u;Encrypt=True;TrustServerCertificate=True;Connection Timeout=300",
|
||||
"PartSourceDatabase": "Server=tcp:ps-whi.database.windows.net,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=ps-whi;Password=9-^*N5dw!6:|.5Q;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
|
||||
},
|
||||
"emailConfiguration": {
|
||||
"From": "alerts@ps-automation.eastus2.cloudapp.azure.com",
|
||||
"To": "tom@soundpress.com,Anas.Bajwa@Partsource.ca,josh@soundpress.com,alex.au@partsource.ca,michael.massara@partsource.ca",
|
||||
//"To": "tom@tomraterman.com",
|
||||
"To": "tom@soundpress.com,josh@soundpress.com,alex.au@partsource.ca,michael.massara@partsource.ca",
|
||||
//"To": "tom@tomraterman.com,josh@soundpress.com",
|
||||
"SmtpHost": "localhost"
|
||||
},
|
||||
"FtpServers": {
|
||||
"AzureConfiguration": {
|
||||
"Username": "ps-ftp\\$ps-ftp",
|
||||
"Password": "ycvXptffBxqkBXW4vuRYqn4Zi1soCvnvMMolTe5HNSeAlcl3bAyJYtNhG579",
|
||||
"Url": "ftp://waws-prod-yq1-007.ftp.azurewebsites.windows.net/site/wwwroot",
|
||||
"Destination": "C:\\Partsource.Automation\\Downloads"
|
||||
"WhiConfiguration": {
|
||||
"Username": "ctc_seo",
|
||||
"Password": "YD3gtaQ5kPdtNKs",
|
||||
"Url": "ftp://ftp.whisolutions.com",
|
||||
"Destination": "C:\\Partsource.Automation\\Downloads\\WHI",
|
||||
"Port": 3001
|
||||
},
|
||||
"AutomationConfiguration": {
|
||||
"Username": "stageuser",
|
||||
"Password": "FXepK^cFYS|[H<",
|
||||
"Url": "ftp://ps-automation-stage.eastus2.cloudapp.azure.com",
|
||||
"Destination": "C:\\Partsource.Automation\\Downloads\\Stage"
|
||||
},
|
||||
"WhiConfiguration": {
|
||||
"Username": "ctc_seo",
|
||||
"Password": "be34hz64e4",
|
||||
"Url": "ftp://ftp.whisolutions.com",
|
||||
"Destination": "C:\\Partsource.Automation\\Downloads\\WHI"
|
||||
"Url": "ftp://localhost",
|
||||
"Destination": "C:\\Partsource.Automation\\Downloads"
|
||||
}
|
||||
},
|
||||
"ssisConfiguration": {
|
||||
@@ -41,7 +37,7 @@
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
"Microsoft.Hosting.Lifetime": "Information",
|
||||
// "Microsoft.EntityFrameworkCore.Database.Command": "Information"
|
||||
},
|
||||
"EventLog": {
|
||||
|
||||
@@ -13,10 +13,14 @@ namespace PartSource.Data.Contexts
|
||||
|
||||
public DbSet<DcfMapping> DcfMappings { get; set; }
|
||||
|
||||
public DbSet<Part> Parts { get; set; }
|
||||
|
||||
public DbSet<Fitment> Fitments { get; set; }
|
||||
|
||||
public DbSet<FitmentNote> FitmentNotes { get; set; }
|
||||
|
||||
public DbSet<ProductType> ProductTypes { get; set; }
|
||||
|
||||
public DbSet<Vehicle> Vehicles { get; set; }
|
||||
|
||||
public DbSet<VehicleFitment> VehicleFitments { get; set; }
|
||||
|
||||
@@ -26,13 +26,13 @@ namespace PartSource.Data.Contexts
|
||||
|
||||
public DbSet<PartPrice> PartPrices { get; set; }
|
||||
|
||||
public DbSet<Part> Parts { get; set; }
|
||||
// public DbSet<Part> Parts { get; set; }
|
||||
|
||||
public DbSet<ShopifyChangelog> ShopifyChangelogs { get; set; }
|
||||
|
||||
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>()
|
||||
|
||||
@@ -7,6 +7,13 @@ namespace PartSource.Data.Dtos
|
||||
{
|
||||
public class VehicleFitmentDto : VehicleFitment
|
||||
{
|
||||
public string PartDescription { get; set; }
|
||||
|
||||
// May not be needed, but don't remove just yet
|
||||
public IList<string> SubmodelNames { get; set; }
|
||||
|
||||
public IList<string> DriveTypes { get; set; }
|
||||
|
||||
public IList<string> Notes { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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 string Updated { get; set; }
|
||||
}
|
||||
}
|
||||
17
PartSource.Data/Models/ProductType.cs
Normal file
17
PartSource.Data/Models/ProductType.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PartSource.Data.Models
|
||||
{
|
||||
public class ProductType
|
||||
{
|
||||
[Key]
|
||||
public string Name { get; set; }
|
||||
|
||||
public bool Active { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace PartSource.Data.Models
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PartSource.Data.Models
|
||||
{
|
||||
public class VehicleFitment
|
||||
{
|
||||
@@ -8,6 +10,7 @@
|
||||
|
||||
public string PartNumber { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string NoteText { get; set; }
|
||||
|
||||
public int Year { get; set; }
|
||||
|
||||
22
PartSource.Data/Nexpart/Answer.cs
Normal file
22
PartSource.Data/Nexpart/Answer.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace PartSource.Data.Nexpart
|
||||
{
|
||||
[XmlType(Namespace = "http://whisolutions.com/pss/common/helper/parts")]
|
||||
public class Answer
|
||||
{
|
||||
[XmlAttribute]
|
||||
public int Id { get; set; }
|
||||
|
||||
[XmlAttribute]
|
||||
public int Count { get; set; }
|
||||
|
||||
[XmlText]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,15 @@ namespace PartSource.Data.Nexpart
|
||||
public bool SecondaryDCF => true;
|
||||
|
||||
[XmlElement(Order = 6)]
|
||||
public Criterion[] Criterion { get; set; }
|
||||
public string[] AppOption { get; set; }
|
||||
|
||||
[XmlElement(Order = 7)]
|
||||
public Criterion[] Criterion { get; set; }
|
||||
|
||||
[XmlElement(Order = 8)]
|
||||
public string GroupBy { get; set; }
|
||||
|
||||
[XmlElement(Order = 9)]
|
||||
public string QuestionOption { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ using PartSource.Data.Nexpart.Interfaces;
|
||||
namespace PartSource.Data.Nexpart
|
||||
{
|
||||
[XmlType(AnonymousType = true, Namespace = "http://whisolutions.com/pss/common/model/parts")]
|
||||
public class ApplicationSearchResponse : IResponseElement<Apps>
|
||||
public class ApplicationSearchResponse : IResponseElement<object>
|
||||
{
|
||||
|
||||
[XmlElement]
|
||||
public PSResponseHeader PSResponseHeader { get; set; }
|
||||
|
||||
[XmlElement(ElementName = nameof(Apps))]
|
||||
public Apps ResponseBody { get; set; }
|
||||
[XmlElement(ElementName = nameof(Apps), Namespace = "http://whisolutions.com/pss/common/model/parts", Type = typeof(Apps))]
|
||||
[XmlElement(ElementName = nameof(Questions), Namespace = "http://whisolutions.com/pss/common/model/parts", Type = typeof(Questions))]
|
||||
public object ResponseBody { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace PartSource.Data.Nexpart
|
||||
{
|
||||
public PSRequestHeader()
|
||||
{
|
||||
this.SvcVersion = "1.0";
|
||||
this.SvcVersion = "2.0";
|
||||
this.ReturnWarnings = "true";
|
||||
}
|
||||
|
||||
|
||||
25
PartSource.Data/Nexpart/Question.cs
Normal file
25
PartSource.Data/Nexpart/Question.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace PartSource.Data.Nexpart
|
||||
{
|
||||
[XmlType(Namespace = "http://whisolutions.com/pss/common/helper/parts")]
|
||||
public class Question
|
||||
{
|
||||
[XmlAttribute(AttributeName = "Attrib")]
|
||||
public string Attribute { get; set; }
|
||||
|
||||
[XmlAttribute]
|
||||
public int Count { get; set; }
|
||||
|
||||
[XmlAttribute]
|
||||
public string Text { get; set; }
|
||||
|
||||
[XmlElement]
|
||||
public Answer[] Answer { get; set; }
|
||||
}
|
||||
}
|
||||
19
PartSource.Data/Nexpart/Questions.cs
Normal file
19
PartSource.Data/Nexpart/Questions.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace PartSource.Data.Nexpart
|
||||
{
|
||||
[XmlType(Namespace = "http://whisolutions.com/pss/common/model/parts")]
|
||||
public class Questions
|
||||
{
|
||||
[XmlElement(Namespace = "http://whisolutions.com/pss/common/helper/parts")]
|
||||
public Question[] Question { get; set; }
|
||||
|
||||
[XmlAttribute]
|
||||
public int NumApps { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PartSource.Data.Contexts;
|
||||
using PartSource.Data.Dtos;
|
||||
using PartSource.Data.Models;
|
||||
using PartSource.Data.Nexpart;
|
||||
|
||||
namespace PartSource.Services
|
||||
{
|
||||
@@ -35,21 +35,11 @@ namespace PartSource.Services
|
||||
ModelName = vf.ModelName,
|
||||
BaseVehicleId = vf.BaseVehicleId,
|
||||
EngineConfigId = vf.EngineConfigId,
|
||||
VehicleToEngineConfigId = vf.VehicleToEngineConfigId
|
||||
VehicleToEngineConfigId = vf.VehicleToEngineConfigId,
|
||||
SubmodelName = vf.SubmodelName
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (vehicleFitment == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
vehicleFitment.SubmodelNames = await _fitmentContext.VehicleFitments
|
||||
.Where(vf => vf.BaseVehicleId == vehicleFitment.BaseVehicleId && vf.Sku == sku)
|
||||
.Select(vf => vf.SubmodelName)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
return vehicleFitment;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace PartSource.Services
|
||||
U content;
|
||||
|
||||
string x = textWriter.ToString();
|
||||
System.Diagnostics.Debug.WriteLine(x);
|
||||
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
@@ -32,9 +33,11 @@ namespace PartSource.Services
|
||||
try
|
||||
{
|
||||
//HttpResponseMessage response = await client.PostAsync(ConfigurationManager.AppSettings["NexpartUrl"], (HttpContent)new StringContent(sb.ToString(), Encoding.UTF8, "text/xml"));
|
||||
HttpResponseMessage response = await client.PostAsync("http://acespssprod.nexpart.com:8085/partselect/2.0/services/PartSelectService.PartSelectHttpSoap11Endpoint", new StringContent(textWriter.ToString(), Encoding.UTF8));
|
||||
HttpResponseMessage response = await client.PostAsync("http://acespssprod.nexpart.com:8085/partselect/2.0/services/PartSelectService.PartSelectHttpSoap11Endpoint", new StringContent(textWriter.ToString(), Encoding.UTF8, "text/xml"));
|
||||
Stream result = await response.Content.ReadAsStreamAsync();
|
||||
|
||||
string str = await response.Content.ReadAsStringAsync();
|
||||
System.Diagnostics.Debug.WriteLine(str);
|
||||
|
||||
content = (U)((Envelope)serializer.Deserialize(result)).Body.Content;
|
||||
;
|
||||
@@ -44,6 +47,7 @@ namespace PartSource.Services
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,26 +12,28 @@ namespace PartSource.Services
|
||||
{
|
||||
public class PartService
|
||||
{
|
||||
private readonly PartSourceContext _context;
|
||||
private readonly PartSourceContext _partSourceContext;
|
||||
private readonly FitmentContext _fitmentContext;
|
||||
|
||||
public PartService(PartSourceContext context)
|
||||
public PartService(PartSourceContext partSourceContext, FitmentContext fitmentContext)
|
||||
{
|
||||
_context = context;
|
||||
_partSourceContext = partSourceContext;
|
||||
_fitmentContext = fitmentContext;
|
||||
}
|
||||
|
||||
public async Task<PartsAvailability> GetInventory(int sku, int storeNumber)
|
||||
public async Task<PartAvailability> GetInventory(int sku, int storeNumber)
|
||||
{
|
||||
return await _context.PartAvailabilities.FirstOrDefaultAsync(s => s.Store == storeNumber && s.SKU == sku);
|
||||
return await _partSourceContext.PartAvailabilities.FirstOrDefaultAsync(s => s.Store == storeNumber && s.SKU == sku);
|
||||
}
|
||||
|
||||
public async Task<Part> GetPartBySku(string sku)
|
||||
{
|
||||
return await _context.Parts.SingleOrDefaultAsync(p => p.Sku == sku);
|
||||
return await _fitmentContext.Parts.SingleOrDefaultAsync(p => p.Sku == sku);
|
||||
}
|
||||
|
||||
public async Task<IList<DcfMapping>> GetDcfMapping(string partsourceLineCode)
|
||||
{
|
||||
return await _context.DcfMappings
|
||||
return await _fitmentContext.DcfMappings
|
||||
.Where(dcf => dcf.LineCode == partsourceLineCode)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user