From 98799a79e151db47ff8aed3987e97764524ecbed Mon Sep 17 00:00:00 2001 From: Loretta Date: Sat, 30 May 2026 16:26:59 +0200 Subject: [PATCH] Refactor PreOrder and CargoTruck models, add ICargoTruck Standardized PreOrder naming across classes, enums, and constants. Updated DB table name constants. Added IsTrailer, Created, and Modified properties to CargoTruck and linked to CargoPartner. Introduced ICargoTruck interface. Added SignalR support for fetching CargoTrucks by CargoPartnerId, with related service, interface, and test updates. Improved ToonDescription docs and updated UI components for new properties and naming. Refactored for clarity and consistency. --- FruitBank.Common.Server/FruitBankConst.cs | 4 ++-- FruitBank.Common/Entities/CargoTruck.cs | 10 +++++++++- FruitBank.Common/Entities/Preorder.cs | 17 +++++++++++++---- FruitBank.Common/Entities/PreorderItem.cs | 16 ++++++++++++---- FruitBank.Common/Enums/PreorderItemStatus.cs | 2 +- FruitBank.Common/Enums/PreorderStatus.cs | 2 +- FruitBank.Common/FruitBankConstClient.cs | 4 ++-- FruitBank.Common/Interfaces/ICargoTruck.cs | 17 +++++++++++++++++ .../IFruitBankDataControllerCommon.cs | 2 ++ FruitBank.Common/Interfaces/IPartner.cs | 1 - FruitBank.Common/SignalRs/SignalRTags.cs | 7 ++++--- .../FruitBankClientTests.cs | 12 ++++++++++++ FruitBankHybrid.Shared.Tests/ToonTests.cs | 1 + .../Grids/Cargos/GridCargoPartner.razor | 12 ++++++------ .../Grids/Cargos/GridCargoTruck.razor | 1 + .../Grids/Cargos/GridCargoTruckBase.cs | 2 +- .../Services/SignalRs/FruitBankSignalRClient.cs | 1 + 17 files changed, 85 insertions(+), 26 deletions(-) create mode 100644 FruitBank.Common/Interfaces/ICargoTruck.cs diff --git a/FruitBank.Common.Server/FruitBankConst.cs b/FruitBank.Common.Server/FruitBankConst.cs index 4c4d2a9e..c60e6ca0 100644 --- a/FruitBank.Common.Server/FruitBankConst.cs +++ b/FruitBank.Common.Server/FruitBankConst.cs @@ -19,13 +19,13 @@ namespace FruitBank.Common.Server /// DateTime generic attribute on Product. /// The start of the window during which this product is visible for preordering. /// - public const string PreorderWindowStart = "PreorderWindowStart"; + public const string PreOrderWindowStart = "PreOrderWindowStart"; /// /// DateTime generic attribute on Product. /// The end of the window during which this product is visible for preordering. /// - public const string PreorderWindowEnd = "PreorderWindowEnd"; + public const string PreOrderWindowEnd = "PreOrderWindowEnd"; static FruitBankConst() { diff --git a/FruitBank.Common/Entities/CargoTruck.cs b/FruitBank.Common/Entities/CargoTruck.cs index bc4524ba..f9f791db 100644 --- a/FruitBank.Common/Entities/CargoTruck.cs +++ b/FruitBank.Common/Entities/CargoTruck.cs @@ -1,4 +1,5 @@ using AyCode.Core.Serializers.Attributes; +using FruitBank.Common.Interfaces; using LinqToDB.Mapping; using Mango.Nop.Core.Entities; @@ -8,10 +9,17 @@ namespace FruitBank.Common.Entities; //[ToonDescription("Business partner with address and tax information", Purpose = "Represents an external legal entity, specifically a Supplier who provides goods or a business partner involved in the procurement chain")] [Table(Name = FruitBankConstClient.CargoTruckDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.CargoTruckDbTableName)] -public sealed class CargoTruck: MgEntityBase//, ICargoPartner +public sealed class CargoTruck: MgEntityBase, ICargoTruck { public int CargoPartnerId { get; set; } + + [Association(ThisKey = nameof(CargoPartnerId), OtherKey = nameof(CargoPartner.Id), CanBeNull = true)] + public CargoPartner CargoPartner { get; set; } + public string CountryCode { get; set; } public string LicencePlate { get; set; } + public bool IsTrailer { get; set; } + public DateTime Created { get; set; } + public DateTime Modified { get; set; } } diff --git a/FruitBank.Common/Entities/Preorder.cs b/FruitBank.Common/Entities/Preorder.cs index a9c987be..a16e9fa7 100644 --- a/FruitBank.Common/Entities/Preorder.cs +++ b/FruitBank.Common/Entities/Preorder.cs @@ -9,17 +9,26 @@ namespace FruitBank.Common.Entities; [AcBinarySerializable(false, true, false, true, false, false)] [Table(Name = FruitBankConstClient.PreOrderDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.PreOrderDbTableName)] -//[ToonDescription("Shipping document with partner, items and files", Purpose = "A digital representation of a supplier's delivery note or invoice associated with the shipment, used for reconciling paper-based data with measured reality")] -public sealed class Preorder : MgEntityBase +[ToonDescription("Customer advance order placed before the goods physically arrive", Purpose = "Header of a customer pre-order against an upcoming inbound delivery. Pending items are allocated from incoming stock first-come-first-served by PreOrderId when a shipping document confirms arrival, then converted into a real NopCommerce Order once any quantity is fulfilled.")] +public sealed class PreOrder : MgEntityBase { public int CustomerId { get; set; } public int StoreId { get; set; } + + [ToonDescription(Purpose = "Requested delivery date. Drives the conversion window (only preorders within PreOrderConversionWindowDays of this date are eligible for allocation) and the expiry sweep — once this date is past, any still-Pending items are Dropped.")] public DateTime DateOfReceipt { get; set; } - public PreorderStatus Status { get; set; } + + [ToonDescription(Purpose = "Header lifecycle: Pending -> Confirmed (all items Fulfilled) / PartiallyFulfilled (some Dropped or partial, none left Pending) / Cancelled.")] + public PreOrderStatus Status { get; set; } + + [ToonDescription(Purpose = "Optional free-text note entered by the customer when placing the preorder.")] public string? CustomerNote { get; set; } + public DateTime CreatedOnUtc { get; set; } public DateTime UpdatedOnUtc { get; set; } + + [ToonDescription(Purpose = "FK to the real NopCommerce Order created from this preorder. Null until the first item is fulfilled; set once on first conversion and reused so subsequent shipping documents append items to the same order.")] public int? OrderId { get; set; } - public List PreorderItems { get; set; } = new(); + public List PreOrderItems { get; set; } = []; } diff --git a/FruitBank.Common/Entities/PreorderItem.cs b/FruitBank.Common/Entities/PreorderItem.cs index 54829345..9f8f8756 100644 --- a/FruitBank.Common/Entities/PreorderItem.cs +++ b/FruitBank.Common/Entities/PreorderItem.cs @@ -9,13 +9,21 @@ namespace FruitBank.Common.Entities; [AcBinarySerializable(false, true, false, true, false, false)] [Table(Name = FruitBankConstClient.PreOrderItemDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.PreOrderItemDbTableName)] -//[ToonDescription("Shipping document with partner, items and files", Purpose = "A digital representation of a supplier's delivery note or invoice associated with the shipment, used for reconciling paper-based data with measured reality")] -public sealed class PreorderItem : MgEntityBase +[ToonDescription("Single product line of a customer preorder with fulfilment tracking", Purpose = "A requested product line within a PreOrder. Tracks requested versus cumulatively fulfilled quantity as incoming stock is allocated across one or more shipping-document conversion runs.")] +public sealed class PreOrderItem : MgEntityBase { - public int PreorderId { get; set; } + public int PreOrderId { get; set; } public int ProductId { get; set; } + + [ToonDescription(Purpose = "Quantity of the product the customer requested.")] public int RequestedQuantity { get; set; } + + [ToonDescription(Purpose = "Quantity allocated from incoming stock so far; accumulates across conversion runs until it reaches RequestedQuantity.")] public int FulfilledQuantity { get; set; } + + [ToonDescription(Purpose = "Gross unit price locked at preorder time. Used as the order-item price on conversion for non-measurable products; measurable products are priced 0 at conversion and weighed afterwards.")] public decimal UnitPriceInclTax { get; set; } - public PreorderItemStatus Status { get; set; } + + [ToonDescription(Purpose = "Item lifecycle: Pending -> Fulfilled (fully allocated) / PartiallyFulfilled (partly allocated) / Dropped (expired or no incoming stock).")] + public PreOrderItemStatus Status { get; set; } } diff --git a/FruitBank.Common/Enums/PreorderItemStatus.cs b/FruitBank.Common/Enums/PreorderItemStatus.cs index 42d3a0de..009b67d6 100644 --- a/FruitBank.Common/Enums/PreorderItemStatus.cs +++ b/FruitBank.Common/Enums/PreorderItemStatus.cs @@ -1,6 +1,6 @@ namespace FruitBank.Common.Enums; -public enum PreorderItemStatus +public enum PreOrderItemStatus { Pending = 0, Fulfilled = 10, diff --git a/FruitBank.Common/Enums/PreorderStatus.cs b/FruitBank.Common/Enums/PreorderStatus.cs index 2b01a9c5..27943ea7 100644 --- a/FruitBank.Common/Enums/PreorderStatus.cs +++ b/FruitBank.Common/Enums/PreorderStatus.cs @@ -2,7 +2,7 @@ namespace FruitBank.Common.Enums; -public enum PreorderStatus +public enum PreOrderStatus { Pending = 0, Confirmed = 10, diff --git a/FruitBank.Common/FruitBankConstClient.cs b/FruitBank.Common/FruitBankConstClient.cs index a6e97f58..74958298 100644 --- a/FruitBank.Common/FruitBankConstClient.cs +++ b/FruitBank.Common/FruitBankConstClient.cs @@ -46,8 +46,8 @@ public static class FruitBankConstClient public const string StockTakingItemPalletDbTableName = "fbStockTakingItemPallet"; public const string CustomerCreditDbTableName = "fbCustomerCredit"; - public const string PreOrderDbTableName = "fbPreorder"; - public const string PreOrderItemDbTableName = "fbPreorderItem"; + public const string PreOrderDbTableName = "fbPreOrder"; + public const string PreOrderItemDbTableName = "fbPreOrderItem"; public const string CargoPartnerDbTableName = "fbCargoPartner"; public const string CargoTruckDbTableName = "fbCargoTruck"; diff --git a/FruitBank.Common/Interfaces/ICargoTruck.cs b/FruitBank.Common/Interfaces/ICargoTruck.cs new file mode 100644 index 00000000..538d8612 --- /dev/null +++ b/FruitBank.Common/Interfaces/ICargoTruck.cs @@ -0,0 +1,17 @@ +using AyCode.Interfaces.Entities; +using AyCode.Interfaces.TimeStampInfo; +using FruitBank.Common.Entities; + +namespace FruitBank.Common.Interfaces; + +public interface ICargoTruck : IEntityInt, ITimeStampInfo +{ + public int CargoPartnerId { get; set; } + + public CargoPartner CargoPartner { get; set; } + + public string CountryCode { get; set; } + public string LicencePlate { get; set; } + + public bool IsTrailer { get; set; } +} \ No newline at end of file diff --git a/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs b/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs index 4af46ea9..39a8ae26 100644 --- a/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs +++ b/FruitBank.Common/Interfaces/IFruitBankDataControllerCommon.cs @@ -1,6 +1,7 @@ using FruitBank.Common.Dtos; using FruitBank.Common.Entities; using FruitBank.Common.Models; +using FruitBank.Common.SignalRs; using Mango.Nop.Core.Dtos; using Mango.Nop.Core.Entities; using Mango.Nop.Core.Models; @@ -29,6 +30,7 @@ public interface IFruitBankDataControllerCommon #region CargoTruck public Task?> GetCargoTrucks(); public Task GetCargoTruckById(int id); + public Task?> GetCargoTrucksByCargoPartnerId(int cargoPartnerId); public Task AddCargoTruck(CargoTruck cargoTruck); public Task UpdateCargoTruck(CargoTruck cargoTruck); #endregion CargoTruck diff --git a/FruitBank.Common/Interfaces/IPartner.cs b/FruitBank.Common/Interfaces/IPartner.cs index f5f53ab3..b757dc7e 100644 --- a/FruitBank.Common/Interfaces/IPartner.cs +++ b/FruitBank.Common/Interfaces/IPartner.cs @@ -4,7 +4,6 @@ using FruitBank.Common.Entities; namespace FruitBank.Common.Interfaces; - public interface ICargoPartner : IPartnerBase { List? CargoTrucks { get; set; } diff --git a/FruitBank.Common/SignalRs/SignalRTags.cs b/FruitBank.Common/SignalRs/SignalRTags.cs index e57326f4..298ca2ef 100644 --- a/FruitBank.Common/SignalRs/SignalRTags.cs +++ b/FruitBank.Common/SignalRs/SignalRTags.cs @@ -21,9 +21,10 @@ public class SignalRTags : AcSignalRTags public const int UpdateCargoPartner = 33; public const int GetCargoTrucks = 35; - public const int GetCargoTruckById = 36; - public const int AddCargoTruck = 37; - public const int UpdateCargoTruck = 38; + public const int GetCargoTrucksByCargoPartnerId = 36; + public const int GetCargoTruckById = 37; + public const int AddCargoTruck = 38; + public const int UpdateCargoTruck = 39; public const int GetShippings = 40; public const int GetNotMeasuredShippings = 41; diff --git a/FruitBankHybrid.Shared.Tests/FruitBankClientTests.cs b/FruitBankHybrid.Shared.Tests/FruitBankClientTests.cs index d18fc8a4..ed23b765 100644 --- a/FruitBankHybrid.Shared.Tests/FruitBankClientTests.cs +++ b/FruitBankHybrid.Shared.Tests/FruitBankClientTests.cs @@ -131,6 +131,7 @@ namespace FruitBankHybrid.Shared.Tests Assert.IsNotNull(cargoTrucks); Assert.IsNotEmpty(cargoTrucks); } + //[TestMethod] //[DataRow(1)] public async Task GetCargoTruckByIdTest(int cargoTruckId) @@ -143,6 +144,17 @@ namespace FruitBankHybrid.Shared.Tests return cargoTruck; } + [TestMethod] + [DataRow(1)] + public async Task GetCargoTrucksByCargoPartnerIdTest(int cargoPartnerId) + { + var cargoTrucks = await _signalRClient.GetCargoTrucksByCargoPartnerId(cargoPartnerId); + + Assert.IsNotNull(cargoTrucks); + Assert.IsNotEmpty(cargoTrucks); + } + + [TestMethod] [DataRow(1)] public async Task UpdateCargoTruckTest(int cargoTruckId) diff --git a/FruitBankHybrid.Shared.Tests/ToonTests.cs b/FruitBankHybrid.Shared.Tests/ToonTests.cs index 8f8b7576..fa8609e4 100644 --- a/FruitBankHybrid.Shared.Tests/ToonTests.cs +++ b/FruitBankHybrid.Shared.Tests/ToonTests.cs @@ -86,6 +86,7 @@ public class FullProcessModel { public List Shippings { get; set; } public List Orders { get; set; } + public List PreOrders { get; set; } public List StockTakings { get; set; } } diff --git a/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoPartner.razor b/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoPartner.razor index 8c025799..1f0730c8 100644 --- a/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoPartner.razor +++ b/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoPartner.razor @@ -44,21 +44,21 @@ @if (IsMasterGrid) { - var partner = ((CargoPartner)context.DataItem); - var shipping = partner?.Shippings ?? []; - var cargoTrucks = partner?.CargoTrucks ?? []; + var cargoPartner = ((CargoPartner)context.DataItem); + var shippings = cargoPartner?.Shippings ?? []; + var cargoTrucks = cargoPartner?.CargoTrucks ?? []; @{ - var observableShippingDocuments = new AcObservableCollection(cargoTrucks); - + var observableCargoTruck = new AcObservableCollection(cargoTrucks); + } @{ - var observableShippings = new AcObservableCollection(shipping); + var observableShippings = new AcObservableCollection(shippings); } diff --git a/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoTruck.razor b/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoTruck.razor index c264877d..43f987bf 100644 --- a/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoTruck.razor +++ b/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoTruck.razor @@ -30,6 +30,7 @@ + diff --git a/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoTruckBase.cs b/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoTruckBase.cs index 2c1f1513..de1162ab 100644 --- a/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoTruckBase.cs +++ b/FruitBankHybrid.Shared/Components/Grids/Cargos/GridCargoTruckBase.cs @@ -35,7 +35,7 @@ public class GridCargoTruckBase: FruitBankGridBase, IGrid switch (ParentDataItem) { case ICargoPartner: - GetAllMessageTag = SignalRTags.GetCargoTrucks; + GetAllMessageTag = SignalRTags.GetCargoTrucksByCargoPartnerId; if (KeyFieldNameToParentId.IsNullOrWhiteSpace()) KeyFieldNameToParentId = nameof(CargoTruck.CargoPartnerId); break; diff --git a/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs b/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs index e9675de6..bf590cd1 100644 --- a/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs +++ b/FruitBankHybrid.Shared/Services/SignalRs/FruitBankSignalRClient.cs @@ -76,6 +76,7 @@ namespace FruitBankHybrid.Shared.Services.SignalRs #region CargoTruck public Task?> GetCargoTrucks() => GetAllAsync>(SignalRTags.GetCargoTrucks); public Task GetCargoTruckById(int id) => GetByIdAsync(SignalRTags.GetCargoTruckById, id); + public Task?> GetCargoTrucksByCargoPartnerId(int cargoPartnerId) => GetAllAsync>(SignalRTags.GetCargoTrucksByCargoPartnerId, [cargoPartnerId]); public Task AddCargoTruck(CargoTruck cargoTruck) => PostDataAsync(SignalRTags.AddCargoTruck, cargoTruck); public Task UpdateCargoTruck(CargoTruck cargoTruck) => PostDataAsync(SignalRTags.UpdateCargoTruck, cargoTruck); #endregion CargoTruck