diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 76a90611..062a7b19 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -6,7 +6,11 @@ "Bash(find:*)", "Bash(grep:*)", "Bash(dotnet test:*)", - "Bash(dotnet build:*)" + "Bash(dotnet build:*)", + "Bash(ls:*)", + "Bash(while read:*)", + "Bash(do sed -i '1a using AyCode.Core.Serializers.Toons;\\\\n' \"$f\")", + "Bash(done)" ] } } diff --git a/FruitBank.Common/Dtos/OrderDto.cs b/FruitBank.Common/Dtos/OrderDto.cs index 7b523ea6..701afba5 100644 --- a/FruitBank.Common/Dtos/OrderDto.cs +++ b/FruitBank.Common/Dtos/OrderDto.cs @@ -71,15 +71,15 @@ public class OrderDto : MgOrderDto, IOrderDto public DateTime DateOfReceiptOrCreated => DateOfReceipt ?? CreatedOnUtc; [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] - [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrNull(nameof(IOrderDto.DateOfReceipt))")] + [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrNull('DateOfReceipt')")] public DateTime? DateOfReceipt => GenericAttributes.GetValueOrNull(nameof(IOrderDto.DateOfReceipt)); [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] - [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrDefault(nameof(IOrderDto.RevisorId), 0)")] + [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrDefault('RevisorId', 0)")] public int RevisorId => GenericAttributes.GetValueOrDefault(nameof(IOrderDto.RevisorId), 0); [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] - [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrDefault(nameof(IOrderDto.MeasurementOwnerId), 0)")] + [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrDefault('MeasurementOwnerId', 0)")] public int MeasurementOwnerId => GenericAttributes.GetValueOrDefault(nameof(IOrderDto.MeasurementOwnerId), 0); [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] diff --git a/FruitBank.Common/Dtos/OrderItemDto.cs b/FruitBank.Common/Dtos/OrderItemDto.cs index c26e3f58..d4a43904 100644 --- a/FruitBank.Common/Dtos/OrderItemDto.cs +++ b/FruitBank.Common/Dtos/OrderItemDto.cs @@ -17,7 +17,7 @@ namespace FruitBank.Common.Dtos; [LinqToDB.Mapping.Table(Name = nameof(OrderItem))] [System.ComponentModel.DataAnnotations.Schema.Table(nameof(OrderItem))] -[ToonDescription($"Data transfer object for {nameof(OrderItem)}", TypeRelation = ToonTypeRelation.DtoOf, RelatedTypes = [typeof(OrderItem)])] +[ToonDescription("Order item with measurements, pallets, and validation", TypeRelation = ToonTypeRelation.DtoOf, RelatedTypes = [typeof(OrderItem)])] public class OrderItemDto : MgOrderItemDto, IOrderItemDto { [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] @@ -35,6 +35,7 @@ public class OrderItemDto : MgOrderItemDto, IOrderItemDto public OrderDto OrderDto { get; set; } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(Purpose = "Status flag", BusinessRule = "get => IsMeasuredAndValid()")] public bool IsMeasured { get => IsMeasuredAndValid(); @@ -42,7 +43,7 @@ public class OrderItemDto : MgOrderItemDto, IOrderItemDto } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] - [ToonDescription(BusinessRule = "get => ProductDto!.IsMeasurable", Constraints = "[#SmartTypeConstraints], readonly")] + [ToonDescription(Purpose = "Status flag", BusinessRule = "get => ProductDto!.IsMeasurable")] public bool IsMeasurable { get => ProductDto!.IsMeasurable; @@ -50,7 +51,7 @@ public class OrderItemDto : MgOrderItemDto, IOrderItemDto } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] - [ToonDescription(BusinessRule = "get => OrderItemPallets.Sum(x => x.TrayQuantity)", Constraints = "[#SmartTypeConstraints], readonly")] + [ToonDescription(BusinessRule = "get => OrderItemPallets.Sum(x => x.TrayQuantity)")] public int TrayQuantity { get => OrderItemPallets.Sum(x => x.TrayQuantity); @@ -58,7 +59,7 @@ public class OrderItemDto : MgOrderItemDto, IOrderItemDto } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] - [ToonDescription(BusinessRule = "get => double.Round(OrderItemPallets.Sum(x => x.NetWeight), 1)", Constraints = "[#SmartTypeConstraints], readonly")] + [ToonDescription(BusinessRule = "get => double.Round(OrderItemPallets.Sum(x => x.NetWeight), 1)")] public double NetWeight { get @@ -76,7 +77,7 @@ public class OrderItemDto : MgOrderItemDto, IOrderItemDto } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] - [ToonDescription(BusinessRule = "get => double.Round(OrderItemPallets.Sum(x => x.NetWeight), 1)", Constraints = "[#SmartTypeConstraints], readonly")] + [ToonDescription(BusinessRule = "get => double.Round(OrderItemPallets.Sum(x => x.NetWeight), 1)")] public double GrossWeight { get @@ -94,19 +95,24 @@ public class OrderItemDto : MgOrderItemDto, IOrderItemDto } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => IsMeasurable && OrderItemPallets.Count > 0 ? double.Round(OrderItemPallets.Sum(oip => oip.AverageWeight) / OrderItemPallets.Count, 1) : 0d")] public double AverageWeight => IsMeasurable && OrderItemPallets.Count > 0 ? double.Round(OrderItemPallets.Sum(oip => oip.AverageWeight) / OrderItemPallets.Count, 1) : 0d; [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => IsMeasurable ? double.Round(ProductDto!.AverageWeight - AverageWeight, 1) : 0")] public double AverageWeightDifference => IsMeasurable ? double.Round(ProductDto!.AverageWeight - AverageWeight, 1) : 0; [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] - public bool AverageWeightIsValid => !IsMeasurable || + [ToonDescription(BusinessRule = "get => !IsMeasurable || (ProductDto!.AverageWeight > 0 && ((AverageWeightDifference / ProductDto!.AverageWeight) * 100) < ProductDto!.AverageWeightTreshold)")] + public bool AverageWeightIsValid => !IsMeasurable || (ProductDto!.AverageWeight > 0 && ((AverageWeightDifference / ProductDto!.AverageWeight) * 100) < ProductDto!.AverageWeightTreshold); [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(Purpose = "Status flag", BusinessRule = "get => OrderItemPallets.Count > 0 && OrderItemPallets.All(oip => oip.IsAudited)")] public bool IsAudited => OrderItemPallets.Count > 0 && OrderItemPallets.All(oip => oip.IsAudited); [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => complex conditional logic based on IsAudited, IsMeasured, and OrderItemPallets status")] public MeasuringStatus MeasuringStatus { get diff --git a/FruitBank.Common/Dtos/ProductDto.cs b/FruitBank.Common/Dtos/ProductDto.cs index 75465b9f..b95a661f 100644 --- a/FruitBank.Common/Dtos/ProductDto.cs +++ b/FruitBank.Common/Dtos/ProductDto.cs @@ -7,6 +7,7 @@ using Mango.Nop.Core.Interfaces.ForeignKeys; using Newtonsoft.Json; //using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Common; +using Nop.Core.Domain.Orders; using System.ComponentModel.DataAnnotations.Schema; using System.Linq.Expressions; @@ -14,7 +15,7 @@ namespace FruitBank.Common.Dtos; [LinqToDB.Mapping.Table(Name = "Product")] [System.ComponentModel.DataAnnotations.Schema.Table("Product")] -[ToonDescription($"Data transfer object for Product")] +[ToonDescription("Product data with measurements and generic attributes", TypeRelation = ToonTypeRelation.DtoOf)] public class ProductDto : MgProductDto, IProductDto { [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] @@ -33,6 +34,7 @@ public class ProductDto : MgProductDto, IProductDto //{ } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(Purpose = "Status flag", BusinessRule = "get => GenericAttributes.GetValueOrDefault('IsMeasurable')")] public bool IsMeasurable { get => GenericAttributes.GetValueOrDefault(nameof(IMeasurable.IsMeasurable)); @@ -47,6 +49,7 @@ public class ProductDto : MgProductDto, IProductDto } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrDefault('Tare')")] public double Tare { get => GenericAttributes.GetValueOrDefault(nameof(ITare.Tare)); @@ -55,6 +58,7 @@ public class ProductDto : MgProductDto, IProductDto } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrDefault('NetWeight')")] public double NetWeight { get => GenericAttributes.GetValueOrDefault(nameof(IMeasuringNetWeight.NetWeight)); @@ -62,13 +66,14 @@ public class ProductDto : MgProductDto, IProductDto } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrDefault('IncomingQuantity')")] public int IncomingQuantity { get => GenericAttributes.GetValueOrDefault(nameof(IIncomingQuantity.IncomingQuantity)); set => throw new Exception($"ProductDto.IncomingQuantity not set"); //set //{ - // var ga = GenericAttributes.FirstOrDefault(ga => ga.Key == nameof(IIncomingQuantity.IncomingQuantity)) ?? + // var ga = GenericAttributes.FirstOrDefault(ga => ga.Key == nameof(IIncomingQuantity.IncomingQuantity)) ?? // GenericAttributes.AddNewGenericAttribute("Product", nameof(IIncomingQuantity.IncomingQuantity), value.ToString(), Id); // ga.Value = value.ToString(); @@ -76,12 +81,15 @@ public class ProductDto : MgProductDto, IProductDto } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => StockQuantity + IncomingQuantity")] public int AvailableQuantity => StockQuantity + IncomingQuantity; [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrDefault('AverageWeight')")] public double AverageWeight => GenericAttributes.GetValueOrDefault(nameof(IProductDto.AverageWeight)); [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrDefault('AverageWeightTreshold')")] public double AverageWeightTreshold => GenericAttributes.GetValueOrDefault(nameof(IProductDto.AverageWeightTreshold)); public bool HasMeasuringValues() => Id > 0 && NetWeight != 0 && IsMeasurable; diff --git a/FruitBank.Common/Dtos/StockQuantityHistoryDto.cs b/FruitBank.Common/Dtos/StockQuantityHistoryDto.cs index 1718cae1..64b8d0bf 100644 --- a/FruitBank.Common/Dtos/StockQuantityHistoryDto.cs +++ b/FruitBank.Common/Dtos/StockQuantityHistoryDto.cs @@ -1,4 +1,4 @@ -using AyCode.Core.Serializers.Toons; +using AyCode.Core.Serializers.Toons; using FruitBank.Common.Interfaces; using LinqToDB.Mapping; using Mango.Nop.Core.Dtos; @@ -18,10 +18,11 @@ namespace FruitBank.Common.Dtos { [LinqToDB.Mapping.Table(Name = nameof(StockQuantityHistory))] [System.ComponentModel.DataAnnotations.Schema.Table(nameof(StockQuantityHistory))] - [ToonDescription($"Data transfer object for {nameof(StockQuantityHistory)}", TypeRelation = ToonTypeRelation.DtoOf, RelatedTypes = [typeof(StockQuantityHistory)])] + [ToonDescription("Stock quantity history with net weight adjustments", TypeRelation = ToonTypeRelation.DtoOf, RelatedTypes = [typeof(StockQuantityHistory)])] public class StockQuantityHistoryDto : MgStockQuantityHistoryDto, IStockQuantityHistoryDto { [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => StockQuantityHistoryExt?.StockQuantityHistoryId")] public int? StockQuantityHistoryId { get => StockQuantityHistoryExt?.StockQuantityHistoryId; @@ -29,6 +30,7 @@ namespace FruitBank.Common.Dtos } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => StockQuantityHistoryExt?.NetWeightAdjustment")] public double? NetWeightAdjustment { get => StockQuantityHistoryExt?.NetWeightAdjustment; @@ -36,20 +38,22 @@ namespace FruitBank.Common.Dtos } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => StockQuantityHistoryExt?.NetWeight")] public double? NetWeight { get => StockQuantityHistoryExt?.NetWeight; set => StockQuantityHistoryExt!.NetWeight = value; - } + } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(Purpose = "Status flag", BusinessRule = "get => StockQuantityHistoryExt?.IsInconsistent ?? false")] public bool IsInconsistent { get => StockQuantityHistoryExt?.IsInconsistent ?? false; set => StockQuantityHistoryExt!.IsInconsistent = value; - } + } [Association(ThisKey = nameof(Id), OtherKey = nameof(StockQuantityHistoryExt.StockQuantityHistoryId), CanBeNull = true)] - public StockQuantityHistoryExt? StockQuantityHistoryExt { get; set; } + public StockQuantityHistoryExt? StockQuantityHistoryExt { get; set; } } } diff --git a/FruitBank.Common/Entities/Files.cs b/FruitBank.Common/Entities/Files.cs index 34d626a2..158e54e2 100644 --- a/FruitBank.Common/Entities/Files.cs +++ b/FruitBank.Common/Entities/Files.cs @@ -1,9 +1,11 @@ -using FruitBank.Common.Interfaces; +using AyCode.Core.Serializers.Toons; +using FruitBank.Common.Interfaces; using LinqToDB.Mapping; using Mango.Nop.Core.Entities; namespace FruitBank.Common.Entities; +[ToonDescription("Uploaded file with extracted text content", Purpose = "A centralized repository for all uploaded binary content and metadata, featuring a 'RawText' field that stores OCR-extracted information for full-text search and automated data validation across the system")] [Table(Name = FruitBankConstClient.FilesDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.FilesDbTableName)] public class Files : MgEntityBase, IFiles diff --git a/FruitBank.Common/Entities/MeasuringItemPalletBase.cs b/FruitBank.Common/Entities/MeasuringItemPalletBase.cs index 97336d0f..87236498 100644 --- a/FruitBank.Common/Entities/MeasuringItemPalletBase.cs +++ b/FruitBank.Common/Entities/MeasuringItemPalletBase.cs @@ -1,3 +1,4 @@ +using AyCode.Core.Serializers.Toons; using FruitBank.Common.Dtos; using FruitBank.Common.Enums; using FruitBank.Common.Interfaces; @@ -8,6 +9,7 @@ using Newtonsoft.Json; namespace FruitBank.Common.Entities; +[ToonDescription("Base class for pallet measurements with net weight calculation")] public abstract class MeasuringItemPalletBase : MgEntityBase, IMeasuringItemPalletBase { private double _palletWeight; @@ -16,7 +18,9 @@ public abstract class MeasuringItemPalletBase : MgEntityBase, IMeasuringItemPall [NotColumn] protected int ForeignItemId; + [NotColumn] + [ToonDescription(BusinessRule = "get => ForeignItemId")] public int ForeignKey => ForeignItemId; public int TrayQuantity { get; set; } @@ -36,6 +40,7 @@ public abstract class MeasuringItemPalletBase : MgEntityBase, IMeasuringItemPall } [NotColumn, System.ComponentModel.DataAnnotations.Schema.NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => CalculateNetWeight()", Constraints = "[#SmartTypeConstraints], readonly")] public double NetWeight { get => CalculateNetWeight(); @@ -60,6 +65,7 @@ public abstract class MeasuringItemPalletBase : MgEntityBase, IMeasuringItemPall public DateTime Modified { get; set; } [NotColumn, System.ComponentModel.DataAnnotations.Schema.NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => IsMeasured ? MeasuringStatus.Finnished : Id > 0 ? MeasuringStatus.Started : MeasuringStatus.NotStarted")] public virtual MeasuringStatus MeasuringStatus => IsMeasured ? MeasuringStatus.Finnished : Id > 0 ? MeasuringStatus.Started : MeasuringStatus.NotStarted; diff --git a/FruitBank.Common/Entities/OrderItemPallet.cs b/FruitBank.Common/Entities/OrderItemPallet.cs index 1014977b..c87dd9d2 100644 --- a/FruitBank.Common/Entities/OrderItemPallet.cs +++ b/FruitBank.Common/Entities/OrderItemPallet.cs @@ -1,3 +1,4 @@ +using AyCode.Core.Serializers.Toons; using FruitBank.Common.Dtos; using FruitBank.Common.Enums; using FruitBank.Common.Interfaces; @@ -10,6 +11,7 @@ using Table = LinqToDB.Mapping.TableAttribute; namespace FruitBank.Common.Entities; +[ToonDescription("Pallet measurements for order items with audit tracking", Purpose = "A measurement record for outgoing goods, used to verify that the net weight being sent to the customer is accurate and audited")] [Table(Name = FruitBankConstClient.OrderItemPalletDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.OrderItemPalletDbTableName)] public class OrderItemPallet : MeasuringItemPalletBase, IOrderItemPallet @@ -21,6 +23,9 @@ public class OrderItemPallet : MeasuringItemPalletBase, IOrderItemPallet } public int RevisorId { get; set; } + + [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(Purpose = "Status flag", BusinessRule = "get => RevisorId > 0")] public bool IsAudited => RevisorId > 0; //[JsonIgnore, System.Text.Json.Serialization.JsonIgnore] @@ -28,6 +33,7 @@ public class OrderItemPallet : MeasuringItemPalletBase, IOrderItemPallet public OrderItemDto? OrderItemDto { get; set; } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => IsAudited ? MeasuringStatus.Audited : base.MeasuringStatus")] public override MeasuringStatus MeasuringStatus => IsAudited ? MeasuringStatus.Audited : base.MeasuringStatus; public override double CalculateNetWeight() => base.CalculateNetWeight(); @@ -37,6 +43,7 @@ public class OrderItemPallet : MeasuringItemPalletBase, IOrderItemPallet } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => double.Round(NetWeight / TrayQuantity, 1)")] public double AverageWeight => double.Round(NetWeight / TrayQuantity, 1); /// diff --git a/FruitBank.Common/Entities/Pallet.cs b/FruitBank.Common/Entities/Pallet.cs index 8d2afc56..8bdc79e0 100644 --- a/FruitBank.Common/Entities/Pallet.cs +++ b/FruitBank.Common/Entities/Pallet.cs @@ -1,10 +1,12 @@ -using FruitBank.Common.Interfaces; +using AyCode.Core.Serializers.Toons; +using FruitBank.Common.Interfaces; using LinqToDB; using LinqToDB.Mapping; using Mango.Nop.Core.Entities; namespace FruitBank.Common.Entities; +[ToonDescription("Pallet type definition with size and weight")] [Table(Name = FruitBankConstClient.PalletDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.PalletDbTableName)] public class Pallet : MgEntityBase, IPallet diff --git a/FruitBank.Common/Entities/Partner.cs b/FruitBank.Common/Entities/Partner.cs index be56228f..1b568bc0 100644 --- a/FruitBank.Common/Entities/Partner.cs +++ b/FruitBank.Common/Entities/Partner.cs @@ -1,9 +1,11 @@ -using FruitBank.Common.Interfaces; +using AyCode.Core.Serializers.Toons; +using FruitBank.Common.Interfaces; using LinqToDB.Mapping; using Mango.Nop.Core.Entities; 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.PartnerDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.PartnerDbTableName)] public class Partner : MgEntityBase, IPartner diff --git a/FruitBank.Common/Entities/Shipping.cs b/FruitBank.Common/Entities/Shipping.cs index c15aa779..a87e0abd 100644 --- a/FruitBank.Common/Entities/Shipping.cs +++ b/FruitBank.Common/Entities/Shipping.cs @@ -1,9 +1,11 @@ -using FruitBank.Common.Interfaces; +using AyCode.Core.Serializers.Toons; +using FruitBank.Common.Interfaces; using LinqToDB.Mapping; using Mango.Nop.Core.Entities; namespace FruitBank.Common.Entities; +[ToonDescription("Shipping record with documents and measurement tracking", Purpose = "Represents a physical inbound delivery event (truck arrival) at the warehouse, tracking the vehicle and the overall measurement status of the shipment")] [Table(Name = FruitBankConstClient.ShippingDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.ShippingDbTableName)] public class Shipping : MgEntityBase, IShipping diff --git a/FruitBank.Common/Entities/ShippingDocument.cs b/FruitBank.Common/Entities/ShippingDocument.cs index 4ca89b89..d3ce8e35 100644 --- a/FruitBank.Common/Entities/ShippingDocument.cs +++ b/FruitBank.Common/Entities/ShippingDocument.cs @@ -1,10 +1,12 @@ using System.Collections.ObjectModel; +using AyCode.Core.Serializers.Toons; using FruitBank.Common.Interfaces; using LinqToDB.Mapping; using Mango.Nop.Core.Entities; namespace FruitBank.Common.Entities; +[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")] [Table(Name = FruitBankConstClient.ShippingDocumentDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.ShippingDocumentDbTableName)] public class ShippingDocument : MgEntityBase, IShippingDocument diff --git a/FruitBank.Common/Entities/ShippingDocumentToFiles.cs b/FruitBank.Common/Entities/ShippingDocumentToFiles.cs index 1e5f2f90..c85c6c2a 100644 --- a/FruitBank.Common/Entities/ShippingDocumentToFiles.cs +++ b/FruitBank.Common/Entities/ShippingDocumentToFiles.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; +using AyCode.Core.Serializers.Toons; using FruitBank.Common.Interfaces; using LinqToDB.Mapping; using Mango.Nop.Core.Entities; @@ -6,6 +7,7 @@ using Newtonsoft.Json; namespace FruitBank.Common.Entities; +[ToonDescription("Links shipping documents to files with document type", Purpose = "A many-to-many link table that associates general uploaded files with specific shipping documents, assigning a functional context (DocumentType) to each file, such as identifying which PDF is the supplier's invoice versus the packing list")] [LinqToDB.Mapping.Table(Name = FruitBankConstClient.ShippingDocumentToFilesDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.ShippingDocumentToFilesDbTableName)] public class ShippingDocumentToFiles : MgEntityBase, IShippingDocumentToFiles @@ -16,6 +18,7 @@ public class ShippingDocumentToFiles : MgEntityBase, IShippingDocumentToFiles public int DocumentTypeId { get; set; } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => DocumentTypeId")] public DocumentType DocumentType { get => (DocumentType)DocumentTypeId; diff --git a/FruitBank.Common/Entities/ShippingItem.cs b/FruitBank.Common/Entities/ShippingItem.cs index 83841e6e..652acf61 100644 --- a/FruitBank.Common/Entities/ShippingItem.cs +++ b/FruitBank.Common/Entities/ShippingItem.cs @@ -1,4 +1,5 @@ using AyCode.Core.Interfaces; +using AyCode.Core.Serializers.Toons; using FruitBank.Common.Dtos; using FruitBank.Common.Enums; using FruitBank.Common.Interfaces; @@ -17,6 +18,7 @@ using Table = LinqToDB.Mapping.TableAttribute; namespace FruitBank.Common.Entities; +[ToonDescription("Shipping document item with measurements and pallets", Purpose = "Represents a specific product line item within a shipping document, storing the discrepancy between the supplier's declared weight/quantity and the warehouse's measured values")] [Table(Name = FruitBankConstClient.ShippingItemDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.ShippingItemDbTableName)] public class ShippingItem : MgEntityBase, IShippingItem @@ -29,10 +31,8 @@ public class ShippingItem : MgEntityBase, IShippingItem public string NameOnDocument { get; set; } public string HungarianName { get; set; } - /// - /// get => ProductDto?.Name ?? Name - /// [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => ProductDto?.Name ?? Name")] public string ProductName => ProductDto?.Name ?? Name; public int PalletsOnDocument { get; set; } @@ -84,6 +84,7 @@ public class ShippingItem : MgEntityBase, IShippingItem public DateTime Modified { get; set; } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => complex conditional logic based on IsMeasured and ShippingItemPallets status")] public MeasuringStatus MeasuringStatus { get diff --git a/FruitBank.Common/Entities/ShippingItemPallet.cs b/FruitBank.Common/Entities/ShippingItemPallet.cs index 05c5e047..ba4e4b72 100644 --- a/FruitBank.Common/Entities/ShippingItemPallet.cs +++ b/FruitBank.Common/Entities/ShippingItemPallet.cs @@ -1,4 +1,5 @@ -using FruitBank.Common.Interfaces; +using AyCode.Core.Serializers.Toons; +using FruitBank.Common.Interfaces; using LinqToDB.Mapping; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -6,6 +7,7 @@ using System.Security.Cryptography.X509Certificates; namespace FruitBank.Common.Entities; +[ToonDescription("Pallet measurements for shipping items", Purpose = "The smallest unit of measurement tracking, representing a single physical pallet of a shipping item, used for precise gross-to-net weight calculation and quality audit")] [LinqToDB.Mapping.Table(Name = FruitBankConstClient.ShippingItemPalletDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.ShippingItemPalletDbTableName)] public class ShippingItemPallet : MeasuringItemPalletBase, IShippingItemPallet diff --git a/FruitBank.Common/Entities/StockQuantityHistoryExt.cs b/FruitBank.Common/Entities/StockQuantityHistoryExt.cs index 20ab9287..4c4a871d 100644 --- a/FruitBank.Common/Entities/StockQuantityHistoryExt.cs +++ b/FruitBank.Common/Entities/StockQuantityHistoryExt.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using AyCode.Core.Serializers.Toons; namespace Mango.Nop.Core.Entities { @@ -23,6 +24,7 @@ namespace Mango.Nop.Core.Entities [Table(Name = FruitBankConstClient.StockQuantityHistoryExtDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.StockQuantityHistoryExtDbTableName)] + [ToonDescription("Extended weight-metadata for StockQuantityHistory", Purpose = "Validates quantity deltas against measured weight to detect inconsistencies")] public class StockQuantityHistoryExt : MgEntityBase, IStockQuantityHistoryExt { public int StockQuantityHistoryId { get; set; } diff --git a/FruitBank.Common/Entities/StockTaking.cs b/FruitBank.Common/Entities/StockTaking.cs index ac397b5f..616042fb 100644 --- a/FruitBank.Common/Entities/StockTaking.cs +++ b/FruitBank.Common/Entities/StockTaking.cs @@ -1,8 +1,10 @@ -using LinqToDB.Mapping; +using AyCode.Core.Serializers.Toons; +using LinqToDB.Mapping; using Mango.Nop.Core.Entities; namespace FruitBank.Common.Entities; +[ToonDescription("Inventory session record", Purpose = "Orchestrates inventory sessions by freezing logical stock states")] [Table(Name = FruitBankConstClient.StockTakingDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.StockTakingDbTableName)] public class StockTaking : MgStockTaking diff --git a/FruitBank.Common/Entities/StockTakingItem.cs b/FruitBank.Common/Entities/StockTakingItem.cs index a51fb5ee..cc4d7189 100644 --- a/FruitBank.Common/Entities/StockTakingItem.cs +++ b/FruitBank.Common/Entities/StockTakingItem.cs @@ -1,4 +1,5 @@ -using FruitBank.Common.Dtos; +using AyCode.Core.Serializers.Toons; +using FruitBank.Common.Dtos; using LinqToDB; using LinqToDB.Mapping; using Mango.Nop.Core.Entities; @@ -9,6 +10,7 @@ using Table = LinqToDB.Mapping.TableAttribute; namespace FruitBank.Common.Entities; +[ToonDescription("Line item for product reconciliation", Purpose = "Reconciles snapshot quantity with physical count to calculate final stock delta")] [Table(Name = FruitBankConstClient.StockTakingItemDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.StockTakingItemDbTableName)] public class StockTakingItem : MgStockTakingItem @@ -21,27 +23,34 @@ public class StockTakingItem : MgStockTakingItem [Column(DataType = DataType.DecFloat, CanBeNull = false)] public double MeasuredNetWeight { get; set; } + [ToonDescription(Purpose = "Reserved stock buffer to prevent double-deduction during closing")] public int InProcessOrdersQuantity { get; set; } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => OriginalStockQuantity + InProcessOrdersQuantity")] public int TotalOriginalQuantity => OriginalStockQuantity + InProcessOrdersQuantity; [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(Purpose = "Final adjustment value for Product.StockQuantity", BusinessRule = "get => IsMeasured ? MeasuredStockQuantity - TotalOriginalQuantity : 0")] public int QuantityDiff => IsMeasured ? MeasuredStockQuantity - TotalOriginalQuantity : 0; [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => IsMeasurable && IsMeasured ? double.Round(MeasuredNetWeight - OriginalNetWeight, 1) : 0d")] public double NetWeightDiff => IsMeasurable && IsMeasured ? double.Round(MeasuredNetWeight - OriginalNetWeight, 1) : 0d; [Association(ThisKey = nameof(Id), OtherKey = nameof(StockTakingItemPallet.StockTakingItemId), CanBeNull = true)] public List? StockTakingItemPallets { get; set; } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(Purpose = "Status flag", BusinessRule = "get => !IsInvalid && (TotalOriginalQuantity != 0 || OriginalNetWeight != 0)")] public bool IsRequiredForMeasuring => !IsInvalid && (TotalOriginalQuantity != 0 || OriginalNetWeight != 0); [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(Purpose = "Status flag", BusinessRule = "get => TotalOriginalQuantity < 0")] public bool IsInvalid => TotalOriginalQuantity < 0; [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => conditional string based on IsInvalid, IsMeasured, IsRequiredForMeasuring")] public string DisplayText { get diff --git a/FruitBank.Common/Entities/StockTakingItemPallet.cs b/FruitBank.Common/Entities/StockTakingItemPallet.cs index 6aba0ff2..eb881783 100644 --- a/FruitBank.Common/Entities/StockTakingItemPallet.cs +++ b/FruitBank.Common/Entities/StockTakingItemPallet.cs @@ -1,4 +1,5 @@ -using FruitBank.Common.Dtos; +using AyCode.Core.Serializers.Toons; +using FruitBank.Common.Dtos; using FruitBank.Common.Interfaces; using LinqToDB.Mapping; using Mango.Nop.Core.Entities; @@ -12,6 +13,7 @@ public interface IStockTakingItemPallet : IMeasuringItemPalletBase public StockTakingItem? StockTakingItem{ get; set; } } +[ToonDescription("Weight record for inventory item", Purpose = "Granular weight-based evidence for a stock taking line item")] [LinqToDB.Mapping.Table(Name = FruitBankConstClient.StockTakingItemPalletDbTableName)] [System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.StockTakingItemPalletDbTableName)] public class StockTakingItemPallet : MeasuringItemPalletBase, IStockTakingItemPallet diff --git a/FruitBankHybrid.Shared.Tests/ToonTests.cs b/FruitBankHybrid.Shared.Tests/ToonTests.cs index 37a4b7c0..4f30b123 100644 --- a/FruitBankHybrid.Shared.Tests/ToonTests.cs +++ b/FruitBankHybrid.Shared.Tests/ToonTests.cs @@ -1,18 +1,20 @@ using AyCode.Core.Enums; using AyCode.Core.Extensions; using AyCode.Core.Loggers; +using AyCode.Core.Serializers.Toons; using FruitBank.Common; using FruitBank.Common.Dtos; using FruitBank.Common.Entities; using FruitBank.Common.Loggers; using FruitBankHybrid.Shared.Services.SignalRs; +using Mango.Nop.Core.Entities; using Newtonsoft.Json; +using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Common; using Nop.Core.Domain.Orders; using Nop.Core.Domain.Payments; using System.Linq.Expressions; using System.Runtime.Serialization; -using AyCode.Core.Serializers.Toons; namespace FruitBankHybrid.Shared.Tests; @@ -37,9 +39,9 @@ public sealed class ToonTests [TestMethod] - public void OrderDtoToToon() + public async Task OrderDtoToToon() { - var orderDtos = _signalRClient.GetAllOrderDtos(); + var orderDtos = await _signalRClient.GetAllOrderDtos(); var toon = AcToonSerializer.Serialize(orderDtos, AcToonSerializerOptions.Default); Console.WriteLine(toon); @@ -106,7 +108,7 @@ public sealed class ToonTests [TestMethod] public void ToonTypes_NavigationMetadata_ShouldBeComplete() { - var toon = AcToonSerializer.SerializeMetadata([typeof(Shipping), typeof(OrderDto)]); + var toon = AcToonSerializer.SerializeMetadata([typeof(Shipping), typeof(OrderDto), typeof(StockTaking), typeof(StockQuantityHistory), typeof(StockQuantityHistoryExt)]); Console.WriteLine(toon); Console.WriteLine("\n=== NAVIGATION METADATA ELLENŐRZÉS ===\n"); diff --git a/FruitBankHybrid.Shared.Tests/test_debug.ps1 b/FruitBankHybrid.Shared.Tests/test_debug.ps1 deleted file mode 100644 index f466dd9c..00000000 --- a/FruitBankHybrid.Shared.Tests/test_debug.ps1 +++ /dev/null @@ -1,17 +0,0 @@ -# Test debugger script for JsonExtensionTests -$projectPath = "H:\Applications\Mango\Source\FruitBankHybridApp" -Set-Location $projectPath - -Write-Host "Building test project..." -dotnet build FruitBankHybrid.Shared.Tests/FruitBankHybrid.Shared.Tests.csproj -c Debug - -Write-Host "`nRunning JsonExtensionTests..." -# Use --no-build to avoid the MSBuild conflict -dotnet test FruitBankHybrid.Shared.Tests/FruitBankHybrid.Shared.Tests.csproj ` - --no-build ` - -c Debug ` - --filter "ClassName=FruitBankHybrid.Shared.Tests.JsonExtensionTests" ` - 2>&1 | Tee-Object -FilePath "test_results.txt" - -Write-Host "`n=== Test Results ===" -Get-Content "test_results.txt" | Select-String -Pattern "FAILED|PASSED|Error|Assert" | tail -50