# 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", "PreOrderItemStatus", "PreOrderStatus", "DocumentType", "Files", "Pallet", "ProductDto", "Customer", "PreOrderItem", "PreOrder", "CargoPartner", "CargoTruck", "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

  PreOrderItemStatus: enum
    underlying-type: "int"
    default-value: 0
    values:
      Pending = 0
      Fulfilled = 10
      PartiallyFulfilled = 20
      Dropped = 30

  PreOrderStatus: enum
    underlying-type: "int"
    default-value: 0
    values:
      Pending = 0
      Confirmed = 10
      PartiallyFulfilled = 20
      Cancelled = 30

  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"
    Gtin: string
      purpose: "nopCommerce Gtin column — holds the EKÁER VTSZ (customs tariff number) used as the trade-card item productVtsz in NAV road-freight reporting."
    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

  PreOrderItem: "Single product line of a customer preorder with fulfilment tracking"
    table-name: "fbPreOrderItem"
    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."
    FulfilledQuantity: int
      purpose: "Quantity allocated from incoming stock so far; accumulates across conversion runs until it reaches RequestedQuantity."
      business-logic: "this >= 0 && this <= RequestedQuantity"
    PreOrderId: int
      purpose: "FK to the parent PreOrder."
    ProductId: int
      purpose: "FK to the nopCommerce Product being preordered."
    RequestedQuantity: int
      purpose: "Quantity of the product the customer requested."
      constraints: "positive"
    Status: PreOrderItemStatus
      purpose: "Item lifecycle: Pending / Fulfilled (fully allocated) / PartiallyFulfilled (partly allocated) / Dropped (expired or no incoming stock)."
      business-logic: "set during conversion: FulfilledQuantity >= RequestedQuantity ? Fulfilled : FulfilledQuantity > 0 ? PartiallyFulfilled : Dropped; stays Pending until first allocation"
    UnitPriceInclTax: decimal
      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."
      constraints: "non-negative"
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  PreOrder: "Customer advance order placed before the goods physically arrive"
    table-name: "fbPreOrder"
    purpose: "Header of a customer pre-order against an upcoming inbound delivery. When a supplier shipping document confirms incoming stock, pending items are allocated first-come-first-served by PreOrderId (only within the conversion window — PreOrderConversionWindowDays, 4 days, of DateOfReceipt) and the preorder is converted into a real NopCommerce Order once any quantity is fulfilled. Preorders past DateOfReceipt are swept closed, dropping any still-Pending items."
    CreatedOnUtc: DateTime
    CustomerId: int
      purpose: "FK to the nopCommerce Customer who placed the preorder."
    CustomerNote: string
      purpose: "Optional free-text note entered by the customer when placing the preorder."
    DateOfReceipt: DateTime
      purpose: "Requested delivery date. Drives the conversion window (only preorders within PreOrderConversionWindowDays — 4 days — of this date are eligible for allocation) and the expiry sweep — once this date is past, any still-Pending items are Dropped."
    OrderId: int?
      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."
    PreOrderItems: List<PreOrderItem>
      other-key: "PreOrderId"
      navigation: "one-to-many"
    Status: PreOrderStatus
      purpose: "Header lifecycle: Pending / Confirmed (all items Fulfilled) / PartiallyFulfilled (some Dropped or partial, none left Pending) / Cancelled."
      business-logic: "set by RefreshPreOrderStatusAsync from item states: items.All(Fulfilled) => Confirmed; (any Dropped or PartiallyFulfilled) && none Pending => PartiallyFulfilled; else Pending. Cancelled is set explicitly."
    StoreId: int
      purpose: "nopCommerce multi-store scope."
    UpdatedOnUtc: DateTime
      purpose: "Bumped to UtcNow on every status / allocation change."
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  CargoPartner: "Transport / haulage company (carrier) with its vehicle fleet"
    table-name: "fbCargoPartner"
    purpose: "A carrier that delivers goods to the warehouse and owns the CargoTrucks (both trucks and trailers). Distinct from Partner, which is the goods supplier. Name, address and tax fields are inherited from PartnerBase."
    CargoTrucks: List<CargoTruck>
      other-key: "CargoPartnerId"
      navigation: "one-to-many"
      inverse-property: "CargoPartner"
    CertificationNumber: string
    City: string
    Country: string
    CountryCode: string
    County: string
    Created: DateTime
    Currency: string
      purpose: "ISO 4217 currency code the company trades and settles in with this partner (e.g. EUR, HUF). For supplier partners it is the source currency for converting shipping-item values to example: HUF in NAV EKÁER reporting."
    Modified: DateTime
    Name: string
    PostalCode: string
    State: string
    Street: string
    TaxId: string
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  CargoTruck: "Cargo vehicle — truck or trailer — owned by a transport partner"
    table-name: "fbCargoTruck"
    purpose: "A single vehicle in a transport company's fleet. One table holds both tractor units and trailers, distinguished by IsTrailer. Truck and trailer are tracked as separate vehicles because Hungarian EKÁER road-freight reporting (NAV) declares the towing vehicle and the trailer as distinct entries (vehicle / vehicle2), each with its own licence plate and country code — i.e. these shipments carry an EKÁER declaration obligation."
    CargoPartner: CargoPartner
      foreign-key: "CargoPartnerId"
      navigation: "many-to-one"
      inverse-property: "CargoTrucks"
    CargoPartnerId: int
      purpose: "FK to the owning transport company (CargoPartner) — the carrier, not the goods supplier."
    CargoPartnerName: string
      constraints: "readonly, not-mapped"
    CountryCode: string
    Created: DateTime
    IsTrailer: bool
      purpose: "Discriminates the shared table: false = tractor/truck unit, true = trailer."
    LicencePlate: string
    Modified: DateTime
    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"
    PreOrders: List<PreOrder>
      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, used to verify that the net weight being sent to the customer is accurate and audited. NOTE: Despite the 'Pallet' name, this is a general measurement record that is ALWAYS created for every item. If the product is not measurable (IsMeasurable=false), weights are recorded as 0.0 and only TrayQuantity is stored."
    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?
    ForeignKey: int
      business-logic: "get => ForeignItemId"
      constraints: "readonly, not-mapped"
    GrossWeight: double
      purpose: "Measured gross weight; 0.0 if product is not measurable"
    IsMeasured: bool
      purpose: "Status flag"
    Modified: DateTime
    ModifierId: int?
    NetWeight: double
      business-logic: "get => GrossWeight - PalletWeight - (TrayQuantity * TareWeight)"
      constraints: "readonly, not-mapped"
    PalletWeight: double
      purpose: "Weight of the physical pallet if used; 0.0 if goods arrive without a pallet"
    TareWeight: double
    TrayQuantity: int
      purpose: "Always recorded, regardless of measurability"
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  Partner: "Business partner with address and tax information"
    table-name: "fbPartner"
    purpose: "Represents an external legal entity, specifically a Supplier who provides goods or a business partner involved in the procurement chain"
    ShippingDocuments: List<ShippingDocument>
      other-key: "PartnerId"
      navigation: "one-to-many"
      inverse-property: "Partner"
    CertificationNumber: string
    City: string
    Country: string
    CountryCode: string
    County: string
    Created: DateTime
    Currency: string
      purpose: "ISO 4217 currency code the company trades and settles in with this partner (e.g. EUR, HUF). For supplier partners it is the source currency for converting shipping-item values to example: HUF in NAV EKÁER reporting."
    Modified: DateTime
    Name: string
    PostalCode: string
    State: string
    Street: string
    TaxId: string
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  Shipping: "Shipping record with documents and measurement tracking"
    table-name: "fbShipping"
    purpose: "Inbound delivery event (truck arrival) at the warehouse. Created early and filled progressively — carrier, truck and trailer are assigned later."
    CargoCompany: string
    CargoPartner: CargoPartner
      purpose: "Carrier (transport company); assigned later, null until known. Supplier is separate — see ShippingDocument.Partner."
      foreign-key: "CargoPartnerId"
      navigation: "many-to-one"
    CargoPartnerId: int?
    CargoTrailer: CargoTruck
      purpose: "Trailer (CargoTruck table, IsTrailer=true); optional, assigned later — null if none or not yet known."
      foreign-key: "CargoTrailerId"
      navigation: "many-to-one"
    CargoTrailerId: int?
    CargoTruck: CargoTruck
      purpose: "Tractor unit (CargoTruck, IsTrailer=false) from the carrier's fleet; assigned later, null until known."
      foreign-key: "CargoTruckId"
      navigation: "many-to-one"
    CargoTruckId: int?
    Comment: string
    Created: DateTime
    IsAllMeasured: bool
      purpose: "Status flag"
    LicencePlate: string
    MeasuredDate: DateTime?
    Modified: DateTime
    ShippingDate: DateTime
    ShippingDocuments: List<ShippingDocument>
      other-key: "ShippingId"
      navigation: "one-to-many"
      inverse-property: "Shipping"
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  ShippingDocument: "Shipping document with partner, items and files"
    table-name: "fbShippingDocument"
    purpose: "Supplier's delivery note or invoice for the shipment; reconciles paper data with measured reality. Populated progressively — much entered at order time, the rest as it becomes known."
    Comment: string
    Country: string
    Created: DateTime
    DocumentIdNumber: string
    IsAllMeasured: bool
      purpose: "Status flag"
    Modified: DateTime
    Partner: Partner
      foreign-key: "PartnerId"
      navigation: "many-to-one"
      inverse-property: "ShippingDocuments"
    PartnerId: int
    PdfFileName: string
    Shipping: Shipping
      foreign-key: "ShippingId"
      navigation: "many-to-one"
      inverse-property: "ShippingDocuments"
    ShippingDate: DateTime
    ShippingDocumentToFiles: List<ShippingDocumentToFiles>
      other-key: "ShippingDocumentId"
      navigation: "one-to-many"
      inverse-property: "ShippingDocument"
    ShippingId: int?
    ShippingItems: List<ShippingItem>
      other-key: "ShippingDocumentId"
      navigation: "one-to-many"
      inverse-property: "ShippingDocument"
    TotalPallets: int
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  ShippingDocumentToFiles: "Links shipping documents to files with document type"
    table-name: "fbShippingDocumentToFiles"
    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"
    Created: DateTime
    DocumentType: DocumentType
      purpose: "Enum wrapper"
      business-logic: "get, set => DocumentTypeId"
      constraints: "not-mapped"
    DocumentTypeId: int
      constraints: "enum-reference: DocumentType, enum-type: DocumentType"
    FilesId: int
    Modified: DateTime
    ShippingDocument: ShippingDocument
      foreign-key: "ShippingDocumentId"
      navigation: "many-to-one"
      inverse-property: "ShippingDocumentToFiles"
    ShippingDocumentFile: Files
      foreign-key: "FilesId"
      navigation: "many-to-one"
    ShippingDocumentId: int
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  ShippingItem: "Shipping document item with measurements and pallets"
    table-name: "fbShippingItem"
    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"
    Created: DateTime
    GrossWeightOnDocument: double
    HungarianName: string
    IsMeasurable: bool
      purpose: "Status flag"
    IsMeasured: bool
      purpose: "Status flag"
    MeasuredGrossWeight: double
      constraints: "range: 1-100000"
    MeasuredNetWeight: double
      constraints: "range: 1-100000"
    MeasuredQuantity: int
      constraints: "range: 1-100000"
    MeasuringCount: int
      constraints: "range: 1-100000, non-negative"
    MeasuringStatus: MeasuringStatus
      business-logic: "get => complex conditional logic based on IsMeasured and ShippingItemPallets status"
      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?
    ProductName: string
      business-logic: "get => ProductDto?.Name ?? Name"
      constraints: "readonly, not-mapped"
    QuantityOnDocument: int
    ShippingDocument: ShippingDocument
      foreign-key: "ShippingDocumentId"
      navigation: "many-to-one"
      inverse-property: "ShippingItems"
    ShippingDocumentId: int
    ShippingItemPallets: List<ShippingItemPallet>
      other-key: "ShippingItemId"
      navigation: "one-to-many"
      inverse-property: "ShippingItem"
    UnitPriceOnDocument: double
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  ShippingItemPallet: "Pallet measurements for shipping items"
    table-name: "fbShippingItemPallet"
    purpose: "The smallest unit of measurement tracking, representing a single physical measurement event. NOTE: Technically named 'Pallet' for legacy reasons, but it is ALWAYS created even if goods arrive without a physical pallet. For non-measurable products, weights are 0.0 and only TrayQuantity is tracked for tare-weight calculations."
    ShippingItem: ShippingItem
      foreign-key: "ShippingItemId"
      navigation: "many-to-one"
      inverse-property: "ShippingItemPallets"
    ShippingItemId: int
    Created: DateTime
    CreatorId: int?
    ForeignKey: int
      business-logic: "get => ForeignItemId"
      constraints: "readonly, not-mapped"
    GrossWeight: double
      purpose: "Measured gross weight; 0.0 if product is not measurable"
    IsMeasured: bool
      purpose: "Status flag"
    MeasuringStatus: MeasuringStatus
      business-logic: "get => IsMeasured ? MeasuringStatus.Finnished : Id > 0 ? MeasuringStatus.Started : MeasuringStatus.NotStarted"
      constraints: "readonly, not-mapped"
    Modified: DateTime
    ModifierId: int?
    NetWeight: double
      business-logic: "get => GrossWeight - PalletWeight - (TrayQuantity * TareWeight)"
      constraints: "readonly, not-mapped"
    PalletWeight: double
      purpose: "Weight of the physical pallet if used; 0.0 if goods arrive without a pallet"
    TareWeight: double
    TrayQuantity: int
      purpose: "Always recorded, regardless of measurability"
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  StockTaking: "Inventory session record"
    table-name: "fbStockTaking"
    purpose: "Orchestrates inventory sessions by freezing logical stock states"
    Created: DateTime
    Creator: int
    IsClosed: bool
      purpose: "Status flag"
    Modified: DateTime
    StartDateTime: DateTime
    StockTakingItems: List<StockTakingItem>
      other-key: "StockTakingId"
      navigation: "one-to-many"
      inverse-property: "StockTaking"
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  StockTakingItem: "Line item for product reconciliation"
    table-name: "fbStockTakingItem"
    purpose: "Reconciles snapshot quantity with physical count to calculate final stock delta"
    DisplayText: string
      business-logic: "get => conditional string based on IsInvalid, IsMeasured, IsRequiredForMeasuring"
      constraints: "readonly, not-mapped"
    InProcessOrdersQuantity: int
      purpose: "Reserved stock buffer (not yet shipped) to prevent double-deduction during closing"
    IsInvalid: bool
      purpose: "Status flag"
      business-logic: "get => TotalOriginalQuantity < 0"
      constraints: "readonly, not-mapped"
    IsMeasurable: bool
      purpose: "Status flag"
    IsRequiredForMeasuring: bool
      purpose: "Status flag"
      business-logic: "get => !IsInvalid && (TotalOriginalQuantity != 0 || OriginalNetWeight != 0)"
      constraints: "readonly, not-mapped"
    MeasuredNetWeight: double
    NetWeightDiff: double
      business-logic: "get => IsMeasurable && IsMeasured ? double.Round(MeasuredNetWeight - OriginalNetWeight, 1) : 0d"
      constraints: "readonly, not-mapped"
    OriginalNetWeight: double
    QuantityDiff: int
      purpose: "Final adjustment value for Product.StockQuantity"
      business-logic: "get => IsMeasured ? MeasuredStockQuantity - TotalOriginalQuantity : 0"
      constraints: "readonly, not-mapped"
    StockTakingItemPallets: List<StockTakingItemPallet>
      other-key: "StockTakingItemId"
      navigation: "one-to-many"
      inverse-property: "StockTakingItem"
    TotalOriginalQuantity: int
      purpose: "Snapshot of total logical stock at session start"
      business-logic: "get => OriginalStockQuantity + InProcessOrdersQuantity"
      constraints: "readonly, not-mapped"
    Created: DateTime
    IsMeasured: bool
      purpose: "Status flag"
    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"
      inverse-property: "StockTakingItems"
    StockTakingId: int
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true

  StockTakingItemPallet: "Weight record for inventory item"
    table-name: "fbStockTakingItemPallet"
    purpose: "Granular weight-based evidence for a stock taking line item. NOTE: This record is mandatory for every inventory item. If weighing is skipped (non-measurable), it serves as a container for TrayQuantity with zeroed weight fields. The term 'Pallet' is a legacy naming convention."
    StockTakingItem: StockTakingItem
      foreign-key: "StockTakingItemId"
      navigation: "many-to-one"
      inverse-property: "StockTakingItemPallets"
    StockTakingItemId: int
    Created: DateTime
    CreatorId: int?
    ForeignKey: int
      business-logic: "get => ForeignItemId"
      constraints: "readonly, not-mapped"
    GrossWeight: double
      purpose: "Measured gross weight; 0.0 if product is not measurable"
    IsMeasured: bool
      purpose: "Status flag"
    MeasuringStatus: MeasuringStatus
      business-logic: "get => IsMeasured ? MeasuringStatus.Finnished : Id > 0 ? MeasuringStatus.Started : MeasuringStatus.NotStarted"
      constraints: "readonly, not-mapped"
    Modified: DateTime
    ModifierId: int?
    NetWeight: double
      business-logic: "get => GrossWeight - PalletWeight - (TrayQuantity * TareWeight)"
      constraints: "readonly, not-mapped"
    PalletWeight: double
      purpose: "Weight of the physical pallet if used; 0.0 if goods arrive without a pallet"
    TareWeight: double
    TrayQuantity: int
      purpose: "Always recorded, regardless of measurability"
    Id: int
      purpose: "Primary key / unique identification"
      primary-key: true
}