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.
This commit is contained in:
Loretta 2026-01-14 22:16:49 +01:00
parent ca186c9e90
commit 3700bfdb29
4 changed files with 44 additions and 1 deletions

View File

@ -3,7 +3,10 @@
"allow": [ "allow": [
"Bash(dir:*)", "Bash(dir:*)",
"Bash(dotnet list:*)", "Bash(dotnet list:*)",
"Bash(find:*)" "Bash(find:*)",
"Bash(grep:*)",
"Bash(dotnet test:*)",
"Bash(dotnet build:*)"
] ]
} }
} }

View File

@ -35,6 +35,7 @@ public class OrderDto : MgOrderDto<OrderItemDto, ProductDto>, IOrderDto
public List<GenericAttributeDto> GenericAttributes { get; set; } public List<GenericAttributeDto> GenericAttributes { get; set; }
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [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 public bool IsMeasured
{ {
get => IsMeasuredAndValid(); get => IsMeasuredAndValid();
@ -42,6 +43,7 @@ public class OrderDto : MgOrderDto<OrderItemDto, ProductDto>, IOrderDto
} }
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
[ToonDescription(BusinessRule = "get => OrderItemDtos.Any(oi => oi.IsMeasurable)", Constraints = "[#SmartTypeConstraints], readonly")]
public bool IsMeasurable public bool IsMeasurable
{ {
get => OrderItemDtos.Any(oi => oi.IsMeasurable); get => OrderItemDtos.Any(oi => oi.IsMeasurable);
@ -69,18 +71,23 @@ public class OrderDto : MgOrderDto<OrderItemDto, ProductDto>, IOrderDto
public DateTime DateOfReceiptOrCreated => DateOfReceipt ?? CreatedOnUtc; public DateTime DateOfReceiptOrCreated => DateOfReceipt ?? CreatedOnUtc;
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
[ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrNull<DateTime>(nameof(IOrderDto.DateOfReceipt))")]
public DateTime? DateOfReceipt => GenericAttributes.GetValueOrNull<DateTime>(nameof(IOrderDto.DateOfReceipt)); public DateTime? DateOfReceipt => GenericAttributes.GetValueOrNull<DateTime>(nameof(IOrderDto.DateOfReceipt));
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [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); public int RevisorId => GenericAttributes.GetValueOrDefault(nameof(IOrderDto.RevisorId), 0);
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [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); public int MeasurementOwnerId => GenericAttributes.GetValueOrDefault(nameof(IOrderDto.MeasurementOwnerId), 0);
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [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); public bool IsAllOrderItemAudited => OrderItemDtos.Count > 0 && OrderItemDtos.All(oi => oi.IsAudited);
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
[ToonDescription(BusinessRule = "get => OrderItemDtos.All(oi => oi.AverageWeightIsValid)")]
public bool IsAllOrderItemAvgWeightValid => OrderItemDtos.All(oi => oi.AverageWeightIsValid); public bool IsAllOrderItemAvgWeightValid => OrderItemDtos.All(oi => oi.AverageWeightIsValid);
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
@ -105,6 +112,7 @@ public class OrderDto : MgOrderDto<OrderItemDto, ProductDto>, IOrderDto
{ } { }
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
[ToonDescription(BusinessRule = "get => OrderStatus == OrderStatus.Complete")]
public bool IsComplete => OrderStatus == OrderStatus.Complete; public bool IsComplete => OrderStatus == OrderStatus.Complete;
public bool HasMeasuringAccess(int? customerId, bool isRevisorUser = false) public bool HasMeasuringAccess(int? customerId, bool isRevisorUser = false)

View File

@ -42,6 +42,7 @@ public class OrderItemDto : MgOrderItemDto<ProductDto>, IOrderItemDto
} }
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
[ToonDescription(BusinessRule = "get => ProductDto!.IsMeasurable", Constraints = "[#SmartTypeConstraints], readonly")]
public bool IsMeasurable public bool IsMeasurable
{ {
get => ProductDto!.IsMeasurable; get => ProductDto!.IsMeasurable;
@ -49,6 +50,7 @@ public class OrderItemDto : MgOrderItemDto<ProductDto>, IOrderItemDto
} }
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
[ToonDescription(BusinessRule = "get => OrderItemPallets.Sum(x => x.TrayQuantity)", Constraints = "[#SmartTypeConstraints], readonly")]
public int TrayQuantity public int TrayQuantity
{ {
get => OrderItemPallets.Sum(x => x.TrayQuantity); get => OrderItemPallets.Sum(x => x.TrayQuantity);
@ -56,6 +58,7 @@ public class OrderItemDto : MgOrderItemDto<ProductDto>, IOrderItemDto
} }
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [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 public double NetWeight
{ {
get get
@ -73,6 +76,7 @@ public class OrderItemDto : MgOrderItemDto<ProductDto>, IOrderItemDto
} }
[NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore] [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 public double GrossWeight
{ {
get get

View File

@ -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<OrderDto>();
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!");
}