From 50a25ff9960732055681d24c85e49baf423b9ce9 Mon Sep 17 00:00:00 2001 From: Piotr Kus Date: Tue, 9 Sep 2025 07:00:05 +0200 Subject: [PATCH] * Introduced Marelli packing list --- .../Controllers/WzRowMareliController.cs | 12 +- .../Marelli/MarelliPackList.razor | 419 ++++++++++++++++++ .../Meyle/MeylePackList.razor} | 5 +- .../Meyle/MeylePacklistSimple.razor} | 2 +- .../Components/Pages/Warehouse.razor | 9 +- OrdersManagement/Services/WarehouseService.cs | 12 +- .../{WzRowMareliDto.cs => WzRowMarelliDto.cs} | 4 +- .../{WzRowMareli.cs => WzRowMarelli.cs} | 4 +- SytelineSaAppEfDataModel/MappingProfile.cs | 2 +- .../Services/IWzRowMareliService.cs | 8 +- .../Services/WzRowMareliService.cs | 16 +- .../SytelineSaAppDbContext.cs | 14 +- 12 files changed, 477 insertions(+), 30 deletions(-) create mode 100644 OrdersManagement/Components/Pages/PackingLists/Marelli/MarelliPackList.razor rename OrdersManagement/Components/Pages/{WarehousePackList.razor => PackingLists/Meyle/MeylePackList.razor} (99%) rename OrdersManagement/Components/Pages/{WarehousePacklistSimple.razor => PackingLists/Meyle/MeylePacklistSimple.razor} (99%) rename SytelineSaAppEfDataModel/Dtos/{WzRowMareliDto.cs => WzRowMarelliDto.cs} (77%) rename SytelineSaAppEfDataModel/Entities/{WzRowMareli.cs => WzRowMarelli.cs} (80%) diff --git a/FaKrosnoApi/Controllers/WzRowMareliController.cs b/FaKrosnoApi/Controllers/WzRowMareliController.cs index d9879a1..3569631 100644 --- a/FaKrosnoApi/Controllers/WzRowMareliController.cs +++ b/FaKrosnoApi/Controllers/WzRowMareliController.cs @@ -9,14 +9,14 @@ namespace FaKrosnoApi.Controllers; public class WzRowMareliController(IWzRowMareliService service, IMaterialTransactionService materialTransactionService) : Controller { [HttpGet] - public async Task>> GetAll() + public async Task>> GetAll() { - IEnumerable wzRows = await service.GetAll(); + IEnumerable wzRows = await service.GetAll(); return Ok(wzRows); } [HttpPost] - public async Task CreateRows([FromBody] IEnumerable rows) + public async Task CreateRows([FromBody] IEnumerable rows) { if (rows == null || !rows.Any()) { @@ -28,9 +28,9 @@ public class WzRowMareliController(IWzRowMareliService service, IMaterialTransac } [HttpGet("by-wz-header-id")] - public async Task>> GetByWzHeaderId(Guid wzHeaderId) + public async Task>> GetByWzHeaderId(Guid wzHeaderId) { - IEnumerable wzRows = await service.GetByWzHeaderId(wzHeaderId); + IEnumerable wzRows = await service.GetByWzHeaderId(wzHeaderId); return Ok(wzRows); } @@ -49,7 +49,7 @@ public class WzRowMareliController(IWzRowMareliService service, IMaterialTransac } [HttpPut] - public async Task UpdateRows([FromBody] IEnumerable rows) + public async Task UpdateRows([FromBody] IEnumerable rows) { if (rows == null || !rows.Any()) { diff --git a/OrdersManagement/Components/Pages/PackingLists/Marelli/MarelliPackList.razor b/OrdersManagement/Components/Pages/PackingLists/Marelli/MarelliPackList.razor new file mode 100644 index 0000000..d4b68ef --- /dev/null +++ b/OrdersManagement/Components/Pages/PackingLists/Marelli/MarelliPackList.razor @@ -0,0 +1,419 @@ +@page "/Warehouse/Marelli/PackList/{WzHeader:guid}" + +@using OrdersManagement.Models +@using Syncfusion.Blazor.Cards +@using Syncfusion.Blazor.Grids +@using SytelineSaAppEfDataModel.Dtos +@using Syncfusion.Blazor.Navigations +@using Syncfusion.Blazor.Popups +@using Syncfusion.Blazor.Inputs +@using FilterType = Syncfusion.Blazor.Grids.FilterType +@using Syncfusion.Blazor.Buttons +@using SelectionType = Syncfusion.Blazor.Grids.SelectionType + +@inject NavigationManager NavigationManager +@inject WarehouseService WarehouseService + +
+ + +

Packing List

+
+ + + + Zmień widok + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @if (_isValid) + { +

Packing List został wygenerowany i wysłany!

+ } + else if (string.IsNullOrWhiteSpace(EmailAddresses)) + { +

Błąd: Proszę wprowadzić przynajmniej jeden ADRES EMAIL do wysyłki raportu!

+ } + else if (!_isValid) + { +

Błąd: Nie Wszystkie linie mają wypełniony NUMER PALETY.
Packing List nie zostanie + wygenerowany!

+ } +
+
+ + + +
+ + + + +

Błąd skanowania! Wybierz NUMER PALETY większy niż 0 (Aktualnie '@PalletNumber'):

+
+
+ + + +
+ + FA Krosno Manager © @(DateTime.Now.Year) + +
+
+ +@code { + [Parameter] public Guid WzHeader { get; set; } + + private SfGrid _grid; + private List WzRowsMarelli { get; set; } = new(); + private IDictionary> TransactionModelsByPartNumber { get; set; } = new Dictionary>(); + private List ChangedRecords = new(); + private WzHeaderDto _wzHeader; + private SfTextBox _scanner; + + private WzRowMarelliDto? SelectedRow { get; set; } + private List SelectedRows { get; set; } = new(); + + private string WzNumber { get; set; } = string.Empty; + + private bool _isValid; + + private bool Visibility { get; set; } + public bool VisibilityPalletNumber { get; set; } + + private string? EmailAddresses { get; set; } = string.Empty; + private string PalletNumber { get; set; } = "0"; + + private string LastScannedValue { get; set; } = string.Empty; + private string ScannedValue { get; set; } = string.Empty; + + private bool IsDisabled => SelectedRow == null; + + private void HideModal() + { + Visibility = false; + VisibilityPalletNumber = false; + + LastScannedValue = ScannedValue; + ScannedValue = string.Empty; + + _scanner.FocusAsync(); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + _wzHeader = await WarehouseService.GetWzHeaderByIdAsync(WzHeader); + WzRowsMarelli = (await WarehouseService.GetWzRowsByWzHeaderId(WzHeader)).ToList(); + + TransactionModelsByPartNumber = await GetTransactionModels(); + + EmailAddresses = _wzHeader.EmailAddresses; + + WzNumber = _wzHeader.WzNumbers ?? string.Empty; + + await _scanner.FocusAsync(); + StateHasChanged(); + } + } + + private async Task SaveChanges() + { + if (!string.IsNullOrWhiteSpace(EmailAddresses)) + { + await WarehouseService.AddEmailsToWzHeaderAsync(WzHeader, EmailAddresses); + } + + if (ChangedRecords.Any()) + { + await UpdateRows(ChangedRecords); + } + + await _grid.EndEditAsync(); + } + + private async Task OnBatchSave(BeforeBatchSaveArgs obj) + { + var changes = obj.BatchChanges; + List changedRecords = changes.ChangedRecords; + + if (!changedRecords.Any()) return; + + await UpdateRows(changedRecords); + } + + private async Task ExportXls() + { + int count = WzRowsMarelli.Count(x => x.PalletNumber == null); + + _isValid = count == 0; + _isValid = _isValid && !string.IsNullOrWhiteSpace(EmailAddresses); + + if (_isValid) + { + await WarehouseService.AddEmailsToWzHeaderAsync(WzHeader, EmailAddresses); + await WarehouseService.GenerateXlsForMarelliAsync(WzHeader); + } + + Visibility = true; + } + + private void ScanValue(ChangedEventArgs obj) + { + if (string.IsNullOrWhiteSpace(obj.Value)) return; + if (int.Parse(PalletNumber) <= 0) + { + VisibilityPalletNumber = true; + return; + } + + ScannedValue = obj.Value.Trim(); + + StateHasChanged(); + + TransactionModelsByPartNumber.TryGetValue(obj.Value.Trim(), out List? materialTransactionsByPartNumber); + TransactionModel? materialTransactionByPartNumber = materialTransactionsByPartNumber?.FirstOrDefault(); + + _grid.Query = null; + _grid.ClearFilteringAsync(); + + if (materialTransactionByPartNumber != null) + { + SelectedRows.Clear(); + FillFaPartNumberAndPalletNumber(materialTransactionByPartNumber, obj.Value.Trim()); + } + + LastScannedValue = ScannedValue; + ScannedValue = string.Empty; + + _scanner.FocusAsync(); + } + + private async Task FillFaPartNumberAndPalletNumber(TransactionModel materialTransactionByPartNumber, string scannedValue) + { + await _grid.ClearSelectionAsync(); + await _grid.Refresh(); + await InvokeAsync(StateHasChanged); + + List selectedIndices = new List(); + int palletNumber = int.Parse(PalletNumber); + int rowIndex = WzRowsMarelli.FindIndex(x => x.FaIndex == materialTransactionByPartNumber.ItemNumber && x.Quantity == materialTransactionByPartNumber.Quantity); + + switch (rowIndex) + { + case -1: + { + SelectedRows = WzRowsMarelli.Where(x => x.FaIndex == materialTransactionByPartNumber.ItemNumber).ToList(); + + rowIndex = WzRowsMarelli.FindIndex(x => x.FaIndex == SelectedRows.First().FaIndex && x.Quantity == SelectedRows.First().Quantity); + + var validCombinations = FindCombinations(SelectedRows, (int?)materialTransactionByPartNumber.Quantity ?? 0); + + foreach (var combination in validCombinations) + { + foreach (var record in combination) + { + record.PalletNumber = palletNumber; + ChangedRecords.Add(record); + + int index = WzRowsMarelli.IndexOf(record); + + if (index >= 0) + { + selectedIndices.Add(index); + } + } + } + + SelectedRows.Clear(); + SelectedRows.AddRange(ChangedRecords); + + SelectedRow = SelectedRows.FirstOrDefault(); + + await ApplyFilter(ChangedRecords); + + break; + } + default: + { + SelectedRow = WzRowsMarelli[rowIndex]; + selectedIndices.Add(rowIndex); + + SelectedRow.PalletNumber = palletNumber; + + if (ChangedRecords.All(x => x.TransactionNumber != SelectedRow.TransactionNumber)) + { + ChangedRecords.Add(SelectedRow); + } + + break; + } + } + + await SaveChanges(); + ChangedRecords.Clear(); + + if (selectedIndices.Any()) + { + await _grid.SelectRowsAsync(selectedIndices.ToArray()); + } + + await _grid.Refresh(); + await InvokeAsync(StateHasChanged); + + FocusGridRow(rowIndex); + await _scanner.FocusAsync(); + } + + private void FocusGridRow(int rowIndex) + { + if (_grid.AllowPaging) + { + int pageSize = _grid.PageSettings.PageSize; + int targetPage = (rowIndex / pageSize) + 1; + _grid.GoToPageAsync(targetPage); + + rowIndex %= pageSize; + } + + _grid.SelectRowAsync(rowIndex); + _grid.ScrollIntoViewAsync(rowIndex: rowIndex); + _grid.FocusAsync(); + } + + private async Task>> GetTransactionModels() + { + return await WarehouseService.GetTransactionsModels(); + } + + private async Task UpdateRows(IList changedRecords) + { + await WarehouseService.UpdateWzRowsMeyleAsync(changedRecords); + + WzRowsMarelli = (await WarehouseService.GetWzRowsByWzHeaderId(WzHeader)).ToList(); + await InvokeAsync(StateHasChanged); + await _grid.Refresh(); + } + + private void OnRowSelected(RowSelectEventArgs obj) + { + SelectedRow = obj.Data; + } + + private List> FindCombinations(List records, int targetSum) + { + var result = new List>(); + var currentCombination = new List(); + + void Backtrack(int start, int currentSum) + { + if (currentSum == targetSum) + { + result.Add(new List(currentCombination)); + return; + } + + for (int i = start; i < records.Count; i++) + { + if (currentSum + records[i].Quantity <= targetSum) + { + currentCombination.Add(records[i]); + Backtrack(i + 1, currentSum + records[i].Quantity ?? 0); + currentCombination.RemoveAt(currentCombination.Count - 1); + } + } + } + + Backtrack(0, 0); + return result; + } + + private async Task ApplyFilter(IList selectedRecords) + { + await _grid.FilterByColumnAsync(nameof(WzRowMarelliDto.FaIndex), "equal", selectedRecords.First().FaIndex); + } + + private void ChangeView() + { + NavigationManager.NavigateTo($"/Warehouse/Marelli/PackList/{WzHeader}/Simple"); + } +} \ No newline at end of file diff --git a/OrdersManagement/Components/Pages/WarehousePackList.razor b/OrdersManagement/Components/Pages/PackingLists/Meyle/MeylePackList.razor similarity index 99% rename from OrdersManagement/Components/Pages/WarehousePackList.razor rename to OrdersManagement/Components/Pages/PackingLists/Meyle/MeylePackList.razor index 75aa5b1..37594ce 100644 --- a/OrdersManagement/Components/Pages/WarehousePackList.razor +++ b/OrdersManagement/Components/Pages/PackingLists/Meyle/MeylePackList.razor @@ -1,4 +1,5 @@ -@page "/Warehouse/PackList/{WzHeader:guid}" +@page "/Warehouse/Meyle/PackList/{WzHeader:guid}" + @using OrdersManagement.Models @using Syncfusion.Blazor.Cards @using Syncfusion.Blazor.Grids @@ -562,6 +563,6 @@ private void ChangeView() { - NavigationManager.NavigateTo($"/Warehouse/PackList/{WzHeader}/Simple"); + NavigationManager.NavigateTo($"/Warehouse/Meyle/PackList/{WzHeader}/Simple"); } } \ No newline at end of file diff --git a/OrdersManagement/Components/Pages/WarehousePacklistSimple.razor b/OrdersManagement/Components/Pages/PackingLists/Meyle/MeylePacklistSimple.razor similarity index 99% rename from OrdersManagement/Components/Pages/WarehousePacklistSimple.razor rename to OrdersManagement/Components/Pages/PackingLists/Meyle/MeylePacklistSimple.razor index dea363d..e900381 100644 --- a/OrdersManagement/Components/Pages/WarehousePacklistSimple.razor +++ b/OrdersManagement/Components/Pages/PackingLists/Meyle/MeylePacklistSimple.razor @@ -1,4 +1,4 @@ -@page "/Warehouse/PackList/{WzHeader:guid}/Simple" +@page "/Warehouse/Meyle/PackList/{WzHeader:guid}/Simple" @using OrdersManagement.Models @using Syncfusion.Blazor.Cards @using Syncfusion.Blazor.Grids diff --git a/OrdersManagement/Components/Pages/Warehouse.razor b/OrdersManagement/Components/Pages/Warehouse.razor index eac9d5e..bbf81ab 100644 --- a/OrdersManagement/Components/Pages/Warehouse.razor +++ b/OrdersManagement/Components/Pages/Warehouse.razor @@ -175,9 +175,11 @@ private void OnRowDoubleClick(RecordDoubleClickEventArgs obj) { + if (_selectedClient is null) return; + Guid headerId = obj.RowData.ID; - NavigationManager.NavigateTo("/Warehouse/PackList/" + headerId); + NavigationManager.NavigateTo($"/Warehouse/{_selectedClient.Name}/PackList/" + headerId); } private async Task CreatePackingList() @@ -234,10 +236,11 @@ } await WarehouseService.CreateWzRowsMeyleAsync(rows); + + NavigationManager.NavigateTo("/Warehouse/Meyle/PackList/" + wzHeader.ID); + break; } - - NavigationManager.NavigateTo("/Warehouse/PackList/" + wzHeader.ID); } private void HideModal() diff --git a/OrdersManagement/Services/WarehouseService.cs b/OrdersManagement/Services/WarehouseService.cs index c05c99f..824a7ec 100644 --- a/OrdersManagement/Services/WarehouseService.cs +++ b/OrdersManagement/Services/WarehouseService.cs @@ -128,11 +128,21 @@ public class WarehouseService(IHttpClientFactory httpClientFactory) { var response = await _httpClient.GetAsync($"api/ExcelGenerator/generate-meyle?packListId={wzHeaderId}"); response.EnsureSuccessStatusCode(); - if (response.StatusCode != System.Net.HttpStatusCode.OK) + if (response.StatusCode != HttpStatusCode.OK) { throw new Exception("Failed to generate XLS for Mayle"); } } + + public async Task GenerateXlsForMarelliAsync(Guid wzHeaderId) + { + var response = await _httpClient.GetAsync($"api/ExcelGenerator/generate-marelli?packListId={wzHeaderId}"); + response.EnsureSuccessStatusCode(); + if (response.StatusCode != HttpStatusCode.OK) + { + throw new Exception("Failed to generate XLS for Marelli"); + } + } public async Task AddEmailsToWzHeaderAsync(Guid wzHeaderId, string? emailAddresses) { diff --git a/SytelineSaAppEfDataModel/Dtos/WzRowMareliDto.cs b/SytelineSaAppEfDataModel/Dtos/WzRowMarelliDto.cs similarity index 77% rename from SytelineSaAppEfDataModel/Dtos/WzRowMareliDto.cs rename to SytelineSaAppEfDataModel/Dtos/WzRowMarelliDto.cs index 1fb112c..5bb1507 100644 --- a/SytelineSaAppEfDataModel/Dtos/WzRowMareliDto.cs +++ b/SytelineSaAppEfDataModel/Dtos/WzRowMarelliDto.cs @@ -1,6 +1,6 @@ namespace SytelineSaAppEfDataModel.Dtos; -public class WzRowMareliDto +public class WzRowMarelliDto { public Guid ID { get; set; } public Guid? FKHeader { get; set; } @@ -11,4 +11,6 @@ public class WzRowMareliDto public int? Quantity { get; set; } public string OrderNumber { get; set; } public string WzNumber { get; set; } + public string FaIndex { get; set; } + public int? TransactionNumber { get; set; } } \ No newline at end of file diff --git a/SytelineSaAppEfDataModel/Entities/WzRowMareli.cs b/SytelineSaAppEfDataModel/Entities/WzRowMarelli.cs similarity index 80% rename from SytelineSaAppEfDataModel/Entities/WzRowMareli.cs rename to SytelineSaAppEfDataModel/Entities/WzRowMarelli.cs index 3b13bdc..1a5dab2 100644 --- a/SytelineSaAppEfDataModel/Entities/WzRowMareli.cs +++ b/SytelineSaAppEfDataModel/Entities/WzRowMarelli.cs @@ -1,6 +1,6 @@ namespace SytelineSaAppEfDataModel.Entities; -public class WzRowMareli +public class WzRowMarelli { public Guid ID { get; set; } public Guid? FKHeader { get; set; } @@ -11,6 +11,8 @@ public class WzRowMareli public int? Quantity { get; set; } public string OrderNumber { get; set; } public string WzNumber { get; set; } + public string FaIndex { get; set; } + public int? TransactionNumber { get; set; } // Navigation property public WzHeader Header { get; set; } diff --git a/SytelineSaAppEfDataModel/MappingProfile.cs b/SytelineSaAppEfDataModel/MappingProfile.cs index cc28cee..6b24589 100644 --- a/SytelineSaAppEfDataModel/MappingProfile.cs +++ b/SytelineSaAppEfDataModel/MappingProfile.cs @@ -32,7 +32,7 @@ namespace SytelineSaAppEfDataModel CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); - CreateMap().ReverseMap(); + CreateMap().ReverseMap(); } } } diff --git a/SytelineSaAppEfDataModel/Services/IWzRowMareliService.cs b/SytelineSaAppEfDataModel/Services/IWzRowMareliService.cs index 91fb853..5817563 100644 --- a/SytelineSaAppEfDataModel/Services/IWzRowMareliService.cs +++ b/SytelineSaAppEfDataModel/Services/IWzRowMareliService.cs @@ -4,8 +4,8 @@ namespace SytelineSaAppEfDataModel.Services; public interface IWzRowMareliService { - Task> GetAll(); - Task CreateRows(IEnumerable rows); - Task> GetByWzHeaderId(Guid wzHeaderId); - Task UpdateRows(IEnumerable rows); + Task> GetAll(); + Task CreateRows(IEnumerable rows); + Task> GetByWzHeaderId(Guid wzHeaderId); + Task UpdateRows(IEnumerable rows); } \ No newline at end of file diff --git a/SytelineSaAppEfDataModel/Services/WzRowMareliService.cs b/SytelineSaAppEfDataModel/Services/WzRowMareliService.cs index 3e3cd36..93f4c05 100644 --- a/SytelineSaAppEfDataModel/Services/WzRowMareliService.cs +++ b/SytelineSaAppEfDataModel/Services/WzRowMareliService.cs @@ -7,29 +7,29 @@ namespace SytelineSaAppEfDataModel.Services; public class WzRowMareliService(SytelineSaAppDbContext context, IMapper mapper) : IWzRowMareliService { - public async Task> GetAll() + public async Task> GetAll() { - return await context.WzRowsMeyle.Select(x => mapper.Map(x)).ToListAsync(); + return await context.WzRowsMeyle.Select(x => mapper.Map(x)).ToListAsync(); } - public async Task CreateRows(IEnumerable rows) + public async Task CreateRows(IEnumerable rows) { - var entities = mapper.Map>(rows); + var entities = mapper.Map>(rows); await context.WzRowsMareli.AddRangeAsync(entities); await context.SaveChangesAsync(); } - public async Task> GetByWzHeaderId(Guid wzHeaderId) + public async Task> GetByWzHeaderId(Guid wzHeaderId) { return await context.WzRowsMareli .Where(x => x.FKHeader == wzHeaderId) - .Select(x => mapper.Map(x)) + .Select(x => mapper.Map(x)) .ToListAsync(); } - public async Task UpdateRows(IEnumerable rows) + public async Task UpdateRows(IEnumerable rows) { - var entities = mapper.Map>(rows); + var entities = mapper.Map>(rows); context.WzRowsMareli.UpdateRange(entities); await context.SaveChangesAsync(); } diff --git a/SytelineSaAppEfDataModel/SytelineSaAppDbContext.cs b/SytelineSaAppEfDataModel/SytelineSaAppDbContext.cs index 41abf7c..1d9ee84 100644 --- a/SytelineSaAppEfDataModel/SytelineSaAppDbContext.cs +++ b/SytelineSaAppEfDataModel/SytelineSaAppDbContext.cs @@ -22,7 +22,7 @@ namespace SytelineSaAppEfDataModel public DbSet WzClients { get; set; } public DbSet WzHeaders { get; set; } public DbSet WzRowsMeyle { get; set; } - public DbSet WzRowsMareli { get; set; } + public DbSet WzRowsMareli { get; set; } public DbSet ItemCusts { get; set; } public DbSet Lots { get; set; } public DbSet EdiCustomerOrderImports { get; set; } @@ -2508,7 +2508,7 @@ namespace SytelineSaAppEfDataModel .IsRequired(); }); - modelBuilder.Entity(entity => + modelBuilder.Entity(entity => { entity.ToTable("wz_row_mareli"); @@ -2558,6 +2558,16 @@ namespace SytelineSaAppEfDataModel .HasColumnName("wz_number") .HasMaxLength(50) .IsRequired(false); + + entity.Property(e => e.WzNumber) + .HasColumnName("fa_index") + .HasMaxLength(30) + .IsRequired(false); + + entity.Property(e => e.TransactionNumber) + .HasColumnName("trans_num") + .HasColumnType("int") + .IsRequired(false); // Foreign Key entity.HasOne(e => e.Header)