From 3700bfdb290996f643a7627691e550cb8e4b0b08 Mon Sep 17 00:00:00 2001 From: Loretta Date: Wed, 14 Jan 2026 22:16:49 +0100 Subject: [PATCH] Add business-logic metadata to DTO serialization Introduce "business-logic" field in AcToonSerializer type metadata output, sourced from ToonDescription attributes on DTO properties. Annotate relevant OrderDto and OrderItemDto properties with business rules and constraints. Expand allowed Bash commands in settings.local.json. Add test script to verify business-logic metadata presence. Temporarily disable HasToonIgnoreAttribute logic in JsonUtilities. --- .claude/settings.local.json | 5 ++++- FruitBank.Common/Dtos/OrderDto.cs | 8 ++++++++ FruitBank.Common/Dtos/OrderItemDto.cs | 4 ++++ test_toon_businessrule.csx | 28 +++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test_toon_businessrule.csx diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 278535fa..76a90611 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -3,7 +3,10 @@ "allow": [ "Bash(dir:*)", "Bash(dotnet list:*)", - "Bash(find:*)" + "Bash(find:*)", + "Bash(grep:*)", + "Bash(dotnet test:*)", + "Bash(dotnet build:*)" ] } } diff --git a/FruitBank.Common/Dtos/OrderDto.cs b/FruitBank.Common/Dtos/OrderDto.cs index 1edbb7c3..7b523ea6 100644 --- a/FruitBank.Common/Dtos/OrderDto.cs +++ b/FruitBank.Common/Dtos/OrderDto.cs @@ -35,6 +35,7 @@ public class OrderDto : MgOrderDto, IOrderDto public List GenericAttributes { get; set; } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => Id > 0 && OrderItemDtos.Count > 0 && OrderItemDtos.All(x => x.IsMeasured)", Constraints = "[#SmartTypeConstraints], readonly")] public bool IsMeasured { get => IsMeasuredAndValid(); @@ -42,6 +43,7 @@ public class OrderDto : MgOrderDto, IOrderDto } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => OrderItemDtos.Any(oi => oi.IsMeasurable)", Constraints = "[#SmartTypeConstraints], readonly")] public bool IsMeasurable { get => OrderItemDtos.Any(oi => oi.IsMeasurable); @@ -69,18 +71,23 @@ 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))")] 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)")] 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)")] public int MeasurementOwnerId => GenericAttributes.GetValueOrDefault(nameof(IOrderDto.MeasurementOwnerId), 0); [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => OrderItemDtos.Count > 0 && OrderItemDtos.All(oi => oi.IsAudited)")] public bool IsAllOrderItemAudited => OrderItemDtos.Count > 0 && OrderItemDtos.All(oi => oi.IsAudited); [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => OrderItemDtos.All(oi => oi.AverageWeightIsValid)")] public bool IsAllOrderItemAvgWeightValid => OrderItemDtos.All(oi => oi.AverageWeightIsValid); [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] @@ -105,6 +112,7 @@ public class OrderDto : MgOrderDto, IOrderDto { } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => OrderStatus == OrderStatus.Complete")] public bool IsComplete => OrderStatus == OrderStatus.Complete; public bool HasMeasuringAccess(int? customerId, bool isRevisorUser = false) diff --git a/FruitBank.Common/Dtos/OrderItemDto.cs b/FruitBank.Common/Dtos/OrderItemDto.cs index f9db3545..c26e3f58 100644 --- a/FruitBank.Common/Dtos/OrderItemDto.cs +++ b/FruitBank.Common/Dtos/OrderItemDto.cs @@ -42,6 +42,7 @@ public class OrderItemDto : MgOrderItemDto, IOrderItemDto } [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ToonDescription(BusinessRule = "get => ProductDto!.IsMeasurable", Constraints = "[#SmartTypeConstraints], readonly")] public bool IsMeasurable { get => ProductDto!.IsMeasurable; @@ -49,6 +50,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")] public int TrayQuantity { get => OrderItemPallets.Sum(x => x.TrayQuantity); @@ -56,6 +58,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")] public double NetWeight { get @@ -73,6 +76,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")] public double GrossWeight { get diff --git a/test_toon_businessrule.csx b/test_toon_businessrule.csx new file mode 100644 index 00000000..d316fd6c --- /dev/null +++ b/test_toon_businessrule.csx @@ -0,0 +1,28 @@ +#!/usr/bin/env dotnet-script + +#r "H:/Applications/Mango/Source/FruitBankHybridApp/FruitBank.Common/bin/Debug/net9.0/FruitBank.Common.dll" +#r "H:/Applications/Aycode/Source/AyCode.Core/AyCode.Core/bin/FruitBank/Debug/net9.0/AyCode.Core.dll" + +using AyCode.Core.Serializers.Toons; +using FruitBank.Common.Dtos; + +var toon = AcToonSerializer.SerializeTypeMetadata(); +Console.WriteLine(toon); + +// Search for IsMeasurable property output +if (toon.Contains("business-logic:")) +{ + Console.WriteLine("\n✓ SUCCESS: business-logic attribute found!"); + var lines = toon.Split('\n'); + foreach (var line in lines) + { + if (line.Contains("IsMeasurable") || line.Contains("business-logic:")) + { + Console.WriteLine(line); + } + } +} +else +{ + Console.WriteLine("\n✗ FAIL: business-logic attribute NOT found!"); +}