# Domain Model Schema (Toon Format)

> Part of `Nop.Plugin.Misc.FruitBankPlugin`. See `README.md` for project overview.
> Full domain model in Toon (Token-Oriented Object Notation) format — see `AyCode.Core/Serializers/Toons/README.md` (in AyCode.Core solution).
> This is the authoritative schema for entities, DTOs, and enums in the FruitBank domain.
> For behavioral documentation (workflows, lifecycles, event cascades) see `docs/DOMAIN_MODEL.md`.

@meta {
  version = "1.0"
  format = "toon"
  source-code-language = "C#"
  context = "This is a nopCommerce plugin developed for FruitBank, a fruit and vegetable wholesaler. The plugin manages supplier inbound delivery (receiving), warehouse weighing (net/gross/pallet/tare weights), and inventory stocktaking. The business logic is centered around FruitBank's requirement for precise physical measurement and quantity tracking."
  types = ["OrderStatus", "ShippingStatus", "PaymentStatus", "GenericAttributeDto", "MeasuringStatus", "VatNumberStatus", "TaxDisplayType", "OrderNote", "DocumentType", "Files", "Pallet", "ProductDto", "Customer", "FullProcessModel", "OrderDto", "OrderItemDto", "OrderItemPallet", "Partner", "Shipping", "ShippingDocument", "ShippingDocumentToFiles", "ShippingItem", "ShippingItemPallet", "StockTaking", "StockTakingItem", "StockTakingItemPallet"]
}

@types {
  OrderStatus: enum
    underlying-type: "int"
    default-value: 10
    values:
      Pending = 10
      Processing = 20
      Complete = 30
      Cancelled = 40

  ShippingStatus: enum
    underlying-type: "int"
    default-value: 10
    values:
      ShippingNotRequired = 10
      NotYetShipped = 20
      PartiallyShipped = 25
      Shipped = 30
      Delivered = 40

  PaymentStatus: enum
    underlying-type: "int"
    default-value: 10
    values:
      Pending = 10
      Authorized = 20
      Paid = 30
      PartiallyRefunded = 35
      Refunded = 40
      Voided = 50

  GenericAttributeDto: "Data transfer object for GenericAttribute"
    table-name: "GenericAttribute"
    related-type: "dto-of GenericAttribute"
    CreatedOrUpdatedDateUTC: DateTime?
    EntityId: int
      constraints: "polymorphic-fk(KeyGroup)"
    Key: string
    KeyGroup: string
    StoreId: int
    Value: string
      purpose: "Raw string representation of the Key's value"
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  MeasuringStatus: enum
    underlying-type: "int"
    default-value: 0
    values:
      NotStarted = 0
      Started = 10
      Finnished = 20
      Audited = 30

  VatNumberStatus: enum
    underlying-type: "int"
    default-value: 0
    values:
      Unknown = 0
      Empty = 10
      Valid = 20
      Invalid = 30

  TaxDisplayType: enum
    underlying-type: "int"
    default-value: 0
    values:
      IncludingTax = 0
      ExcludingTax = 10

  OrderNote: "NopCommerce order note entity"
    table-name: "OrderNote"
    CreatedOnUtc: DateTime
    DisplayToCustomer: bool
    DownloadId: int
    Note: string
    OrderId: int
      description: "Foreign key to parent Order"
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  DocumentType: enum
    underlying-type: "int"
    default-value: 0
    values:
      NotSet = 0
      Unknown = 5
      ShippingDocument = 10
      OrderConfirmation = 15
      Invoice = 20

  Files: "Uploaded file with extracted text content"
    table-name: "fbFiles"
    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"
    Created: DateTime
    FileExtension: string
    FileHash: string
    FileName: string
    FileSubPath: string
    IsCompressed: bool
      purpose: "Status flag"
    Modified: DateTime
    RawText: string
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  Pallet: "Pallet type definition with size and weight"
    table-name: "fbPallet"
    Created: DateTime
    Modified: DateTime
    Name: string
    Size: string
    Weight: double?
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  ProductDto: "Product data with measurements and generic attributes"
    table-name: "Product"
    related-type: "dto-of Product"
    AvailableQuantity: int
      business-logic: "get => StockQuantity + IncomingQuantity"
      constraints: "readonly, not-mapped"
    AverageWeight: double
      business-logic: "get => GenericAttributes.GetValueOrDefault<double>('AverageWeight')"
      constraints: "readonly, not-mapped"
    AverageWeightTreshold: double
      business-logic: "get => GenericAttributes.GetValueOrDefault<double>('AverageWeightTreshold')"
      constraints: "readonly, not-mapped"
    GenericAttributes: List<GenericAttributeDto>
      navigation: "one-to-many"
    IncomingQuantity: int
      business-logic: "get => GenericAttributes.GetValueOrDefault<int>('IncomingQuantity')"
      constraints: "not-mapped"
    IsMeasurable: bool
      purpose: "Master flag: if false, the system bypasses weight validation but still creates one Measurement Record (PalletItem) with TrayQuantity."
      business-logic: "get => GenericAttributes.GetValueOrDefault<bool>('IsMeasurable')"
      constraints: "not-mapped"
    NetWeight: double
      business-logic: "get => GenericAttributes.GetValueOrDefault<double>('NetWeight')"
      constraints: "not-mapped"
    Tare: double
      business-logic: "get => GenericAttributes.GetValueOrDefault<double>('Tare')"
      constraints: "not-mapped"
    Deleted: bool
    FullDescription: string
    Height: decimal
    Length: decimal
    LimitedToStores: bool
    Name: string
    ParentGroupedProductId: int
    Price: decimal
    ProductCost: decimal
    ProductTypeId: int
    ShortDescription: string
    StockQuantity: int
    SubjectToAcl: bool
    WarehouseId: int
    Weight: decimal
    Width: decimal
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  Customer: "NopCommerce customer entity"
    table-name: "Customer"
    Active: bool
    AdminComment: string
    AffiliateId: int
    BillingAddressId: int?
    CannotLoginUntilDateUtc: DateTime?
    City: string
    Company: string
    CountryId: int
    County: string
    CreatedOnUtc: DateTime
    CurrencyId: int?
    CustomCustomerAttributesXML: string
    CustomerGuid: Guid
    DateOfBirth: DateTime?
    Deleted: bool
    Email: string
      constraints: "email-format"
    EmailToRevalidate: string
      constraints: "email-format"
    FailedLoginAttempts: int
    Fax: string
    FirstName: string
    Gender: string
    HasShoppingCartItems: bool
    IsSystemAccount: bool
      purpose: "Status flag"
    IsTaxExempt: bool
      purpose: "Status flag"
    LanguageId: int?
      constraints: "range: 0-150"
    LastActivityDateUtc: DateTime
    LastIpAddress: string
    LastLoginDateUtc: DateTime?
    LastName: string
    MustChangePassword: bool
    Phone: string
    RegisteredInStoreId: int
    RequireReLogin: bool
    ShippingAddressId: int?
    StateProvinceId: int
    StreetAddress: string
    StreetAddress2: string
    SystemName: string
    TaxDisplayType: TaxDisplayType?
    TaxDisplayTypeId: int?
    TimeZoneId: string
    Username: string
    VatNumber: string
    VatNumberStatus: VatNumberStatus
    VatNumberStatusId: int
    VendorId: int
    ZipPostalCode: string
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  FullProcessModel: "Object of type FullProcessModel"
    table-name: "FullProcessModel"
    purpose: "Container model for Shipping, Order"
    Orders: List<OrderDto>
      navigation: "one-to-many"
    Shippings: List<Shipping>
      navigation: "one-to-many"
    StockTakings: List<StockTaking>
      navigation: "one-to-many"

  OrderDto: "Data transfer object for Order"
    table-name: "Order"
    related-type: "dto-of Order"
    DateOfReceipt: DateTime?
      business-logic: "get => GenericAttributes.GetValueOrNull<DateTime>('DateOfReceipt')"
      constraints: "readonly, not-mapped"
    DateOfReceiptOrCreated: DateTime
      constraints: "readonly, not-mapped"
    GenericAttributes: List<GenericAttributeDto>
      navigation: "one-to-many"
    IsAllOrderItemAudited: bool
      purpose: "Status flag"
      business-logic: "get => OrderItemDtos.Count > 0 && OrderItemDtos.All(oi => oi.IsAudited)"
      constraints: "readonly, not-mapped"
    IsAllOrderItemAvgWeightValid: bool
      purpose: "Status flag"
      business-logic: "get => OrderItemDtos.All(oi => oi.AverageWeightIsValid)"
      constraints: "readonly, not-mapped"
    IsComplete: bool
      purpose: "Status flag"
      business-logic: "get => OrderStatus == OrderStatus.Complete"
      constraints: "readonly, not-mapped"
    IsMeasurable: bool
      purpose: "Status flag"
      business-logic: "get => OrderItemDtos.Any(oi => oi.IsMeasurable)"
      constraints: "readonly, not-mapped"
    IsMeasured: bool
      purpose: "Status flag"
      business-logic: "get => Id > 0 && OrderItemDtos.Count > 0 && OrderItemDtos.All(x => x.IsMeasured)"
      constraints: "readonly, not-mapped"
    MeasurementOwnerId: int
      business-logic: "get => GenericAttributes.GetValueOrDefault('MeasurementOwnerId', 0)"
      constraints: "readonly, not-mapped"
    MeasuringStatus: MeasuringStatus
      constraints: "readonly, not-mapped"
    RevisorId: int
      business-logic: "get => GenericAttributes.GetValueOrDefault('RevisorId', 0)"
      constraints: "readonly, not-mapped"
    TimeOfReceiptText: string
      constraints: "readonly, not-mapped"
    CreatedOnUtc: DateTime
    CustomOrderNumber: string
    CustomValuesXml: string
    Customer: Customer
      foreign-key: "CustomerId"
      navigation: "many-to-one"
    CustomerId: int
    Deleted: bool
    OrderDiscount: decimal
    OrderGuid: Guid
    OrderItemDtos: List<OrderItemDto>
      other-key: "OrderId"
      navigation: "one-to-many"
      inverse-property: "OrderDto"
    OrderNotes: List<OrderNote>
      other-key: "OrderId"
      navigation: "one-to-many"
    OrderStatus: OrderStatus
      purpose: "Enum wrapper"
      business-logic: "get, set => OrderStatusId"
    OrderStatusId: int
    OrderTotal: decimal
    PaidDateUtc: DateTime?
    PaymentStatus: PaymentStatus
      purpose: "Enum wrapper"
      business-logic: "get, set => PaymentStatusId"
    PaymentStatusId: int
    ShippingMethod: string
    ShippingStatus: ShippingStatus
      purpose: "Enum wrapper"
      business-logic: "get, set => ShippingStatusId"
    ShippingStatusId: int
    StoreId: int
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  OrderItemDto: "Order item with measurements, pallets, and validation"
    table-name: "OrderItem"
    related-type: "dto-of OrderItem"
    AverageWeight: double
      business-logic: "get => IsMeasurable && OrderItemPallets.Count > 0 ? double.Round(OrderItemPallets.Sum(oip => oip.AverageWeight) / OrderItemPallets.Count, 1) : 0d"
      constraints: "readonly, not-mapped"
    AverageWeightDifference: double
      business-logic: "get => IsMeasurable ? double.Round(ProductDto!.AverageWeight - AverageWeight, 1) : 0"
      constraints: "readonly, not-mapped"
    AverageWeightIsValid: bool
      business-logic: "get => !IsMeasurable || (ProductDto!.AverageWeight > 0 && ((AverageWeightDifference / ProductDto!.AverageWeight) * 100) < ProductDto!.AverageWeightTreshold)"
      constraints: "readonly, not-mapped"
    GenericAttributes: List<GenericAttributeDto>
      navigation: "one-to-many"
    GrossWeight: double
      business-logic: "get => double.Round(OrderItemPallets.Sum(x => x.NetWeight), 1)"
      constraints: "not-mapped"
    IsAudited: bool
      purpose: "Status flag"
      business-logic: "get => OrderItemPallets.Count > 0 && OrderItemPallets.All(oip => oip.IsAudited)"
      constraints: "readonly, not-mapped"
    IsMeasurable: bool
      purpose: "Status flag"
      business-logic: "get => ProductDto!.IsMeasurable"
      constraints: "not-mapped"
    IsMeasured: bool
      purpose: "Status flag"
      business-logic: "get => IsMeasuredAndValid()"
      constraints: "not-mapped"
    MeasuringStatus: MeasuringStatus
      business-logic: "get => complex conditional logic based on IsAudited, IsMeasured, and OrderItemPallets status"
      constraints: "readonly, not-mapped"
    NetWeight: double
      business-logic: "get => double.Round(OrderItemPallets.Sum(x => x.NetWeight), 1)"
      constraints: "not-mapped"
    OrderDto: OrderDto
      foreign-key: "OrderId"
      navigation: "many-to-one"
      inverse-property: "OrderItemDtos"
    OrderItemPallets: List<OrderItemPallet>
      other-key: "OrderItemId"
      navigation: "one-to-many"
      inverse-property: "OrderItemDto"
    TrayQuantity: int
      business-logic: "get => OrderItemPallets.Sum(x => x.TrayQuantity)"
      constraints: "not-mapped"
    AttributesXml: string
    ItemWeight: decimal?
    OrderId: int
    OrderItemGuid: Guid
    PriceExclTax: decimal
    PriceInclTax: decimal
    ProductDto: ProductDto
      foreign-key: "ProductId"
      navigation: "many-to-one"
    ProductId: int
    ProductName: string
      business-logic: "get => ProductDto?.Name ?? 'ProductDto is null!!!'"
      constraints: "readonly"
    Quantity: int
    UnitPriceExclTax: decimal
    UnitPriceInclTax: decimal
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  OrderItemPallet: "Pallet measurements for order items with audit tracking"
    table-name: "fbOrderItemPallet"
    purpose: "A measurement record for outgoing goods. NOTE: Despite the 'Pallet' name, this is a general measurement record that is ALWAYS created for every item."
    AverageWeight: double
      business-logic: "get => double.Round(NetWeight / TrayQuantity, 1)"
      constraints: "readonly, not-mapped"
    IsAudited: bool
      purpose: "Status flag"
      business-logic: "get => RevisorId > 0"
      constraints: "readonly, not-mapped"
    MeasuringStatus: MeasuringStatus
      business-logic: "get => IsAudited ? MeasuringStatus.Audited : base.MeasuringStatus"
      constraints: "readonly, not-mapped"
    OrderItemDto: OrderItemDto
      foreign-key: "OrderItemId"
      navigation: "many-to-one"
      inverse-property: "OrderItemPallets"
    OrderItemId: int
    RevisorId: int
      purpose: "User/Customer ID of the quality auditor"
    Created: DateTime
    CreatorId: int?
    GrossWeight: double
    IsMeasured: bool
    Modified: DateTime
    ModifierId: int?
    NetWeight: double
      business-logic: "get => GrossWeight - PalletWeight - (TrayQuantity * TareWeight)"
      constraints: "readonly, not-mapped"
    PalletWeight: double
    TareWeight: double
    TrayQuantity: int
    Id: int
      primary-key: true

  Partner: "Business partner with address and tax information"
    table-name: "fbPartner"
    purpose: "External supplier providing goods"
    CertificationNumber: string
    City: string
    Country: string
    County: string
    Created: DateTime
    Modified: DateTime
    Name: string
    PostalCode: string
    ShippingDocuments: List<ShippingDocument>
      navigation: "one-to-many"
    State: string
    Street: string
    TaxId: string
    Id: int
      primary-key: true

  Shipping: "Inbound delivery event at warehouse"
    table-name: "fbShipping"
    CargoCompany: string
    Comment: string
    Created: DateTime
    IsAllMeasured: bool
    LicencePlate: string
    MeasuredDate: DateTime?
    Modified: DateTime
    ShippingDate: DateTime
    ShippingDocuments: List<ShippingDocument>
      navigation: "one-to-many"
    Id: int
      primary-key: true

  ShippingDocument: "Supplier delivery note or invoice"
    table-name: "fbShippingDocument"
    Comment: string
    Country: string
    Created: DateTime
    DocumentIdNumber: string
    IsAllMeasured: bool
    Modified: DateTime
    Partner: Partner
      foreign-key: "PartnerId"
      navigation: "many-to-one"
    PartnerId: int
    PdfFileName: string
    Shipping: Shipping
      foreign-key: "ShippingId"
      navigation: "many-to-one"
    ShippingDate: DateTime
    ShippingDocumentToFiles: List<ShippingDocumentToFiles>
      navigation: "one-to-many"
    ShippingId: int?
    ShippingItems: List<ShippingItem>
      navigation: "one-to-many"
    TotalPallets: int
    Id: int
      primary-key: true

  ShippingDocumentToFiles: "Links documents to files with type"
    table-name: "fbShippingDocumentToFiles"
    Created: DateTime
    DocumentType: DocumentType
    DocumentTypeId: int
    FilesId: int
    Modified: DateTime
    ShippingDocument: ShippingDocument
      foreign-key: "ShippingDocumentId"
      navigation: "many-to-one"
    ShippingDocumentFile: Files
      foreign-key: "FilesId"
      navigation: "many-to-one"
    ShippingDocumentId: int
    Id: int
      primary-key: true

  ShippingItem: "Product line on shipping document"
    table-name: "fbShippingItem"
    Created: DateTime
    GrossWeightOnDocument: double
    HungarianName: string
    IsMeasurable: bool
    IsMeasured: bool
    MeasuredGrossWeight: double
    MeasuredNetWeight: double
    MeasuredQuantity: int
    MeasuringCount: int
    MeasuringStatus: MeasuringStatus
      constraints: "readonly, not-mapped"
    Modified: DateTime
    Name: string
    NameOnDocument: string
    NetWeightOnDocument: double
    Pallet: Pallet
      foreign-key: "PalletId"
      navigation: "many-to-one"
    PalletId: int?
    PalletsOnDocument: int
    ProductDto: ProductDto
      foreign-key: "ProductId"
      navigation: "many-to-one"
    ProductId: int?
    QuantityOnDocument: int
    ShippingDocument: ShippingDocument
      foreign-key: "ShippingDocumentId"
      navigation: "many-to-one"
    ShippingDocumentId: int
    ShippingItemPallets: List<ShippingItemPallet>
      navigation: "one-to-many"
    UnitPriceOnDocument: double
    Id: int
      primary-key: true

  ShippingItemPallet: "Measurement record for incoming goods"
    table-name: "fbShippingItemPallet"
    purpose: "Always created even without physical pallet"
    ShippingItem: ShippingItem
      foreign-key: "ShippingItemId"
      navigation: "many-to-one"
    ShippingItemId: int
    Created: DateTime
    CreatorId: int?
    GrossWeight: double
    IsMeasured: bool
    MeasuringStatus: MeasuringStatus
      constraints: "readonly, not-mapped"
    Modified: DateTime
    ModifierId: int?
    NetWeight: double
      business-logic: "get => GrossWeight - PalletWeight - (TrayQuantity * TareWeight)"
      constraints: "readonly, not-mapped"
    PalletWeight: double
    TareWeight: double
    TrayQuantity: int
    Id: int
      primary-key: true

  StockTaking: "Inventory session record"
    table-name: "fbStockTaking"
    Created: DateTime
    Creator: int
    IsClosed: bool
    Modified: DateTime
    StartDateTime: DateTime
    StockTakingItems: List<StockTakingItem>
      navigation: "one-to-many"
    Id: int
      primary-key: true

  StockTakingItem: "Line item for product reconciliation"
    table-name: "fbStockTakingItem"
    purpose: "Reconciles snapshot quantity with physical count"
    InProcessOrdersQuantity: int
    IsInvalid: bool
      constraints: "readonly, not-mapped"
    IsMeasurable: bool
    IsRequiredForMeasuring: bool
      constraints: "readonly, not-mapped"
    MeasuredNetWeight: double
    NetWeightDiff: double
      constraints: "readonly, not-mapped"
    OriginalNetWeight: double
    QuantityDiff: int
      constraints: "readonly, not-mapped"
    StockTakingItemPallets: List<StockTakingItemPallet>
      navigation: "one-to-many"
    TotalOriginalQuantity: int
      constraints: "readonly, not-mapped"
    Created: DateTime
    IsMeasured: bool
    MeasuredStockQuantity: int
    Modified: DateTime
    OriginalStockQuantity: int
    Product: ProductDto
      foreign-key: "ProductId"
      navigation: "many-to-one"
    ProductId: int
    StockTaking: StockTaking
      foreign-key: "StockTakingId"
      navigation: "many-to-one"
    StockTakingId: int
    Id: int
      primary-key: true

  StockTakingItemPallet: "Weight record for inventory item"
    table-name: "fbStockTakingItemPallet"
    purpose: "Mandatory for every inventory item, even non-measurable"
    StockTakingItem: StockTakingItem
      foreign-key: "StockTakingItemId"
      navigation: "many-to-one"
    StockTakingItemId: int
    Created: DateTime
    CreatorId: int?
    GrossWeight: double
    IsMeasured: bool
    MeasuringStatus: MeasuringStatus
      constraints: "readonly, not-mapped"
    Modified: DateTime
    ModifierId: int?
    NetWeight: double
      business-logic: "get => GrossWeight - PalletWeight - (TrayQuantity * TareWeight)"
      constraints: "readonly, not-mapped"
    PalletWeight: double
    TareWeight: double
    TrayQuantity: int
    Id: int
      primary-key: true
}
