Mango.Nop.Plugins/Nop.Plugin.Misc.AIPlugin/docs/SCHEMA.md

31 KiB

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", "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('AverageWeight')" constraints: "readonly, not-mapped" AverageWeightTreshold: double business-logic: "get => GenericAttributes.GetValueOrDefault('AverageWeightTreshold')" constraints: "readonly, not-mapped" GenericAttributes: List navigation: "one-to-many" IncomingQuantity: int business-logic: "get => GenericAttributes.GetValueOrDefault('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('IsMeasurable')" constraints: "not-mapped" NetWeight: double business-logic: "get => GenericAttributes.GetValueOrDefault('NetWeight')" constraints: "not-mapped" Tare: double business-logic: "get => GenericAttributes.GetValueOrDefault('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 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

FullProcessModel: "Object of type FullProcessModel" table-name: "FullProcessModel" purpose: "Container model for Shipping, Order" Orders: List navigation: "one-to-many" PreOrders: List navigation: "one-to-many" Shippings: List navigation: "one-to-many" StockTakings: List 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('DateOfReceipt')" constraints: "readonly, not-mapped" DateOfReceiptOrCreated: DateTime constraints: "readonly, not-mapped" GenericAttributes: List 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 other-key: "OrderId" navigation: "one-to-many" inverse-property: "OrderDto" OrderNotes: List 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 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 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" 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

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 other-key: "PartnerId" navigation: "one-to-many" inverse-property: "Partner" CertificationNumber: string City: string Country: string CountryCode: string County: string Created: DateTime 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: "Represents a physical inbound delivery event (truck arrival) at the warehouse, tracking the vehicle and the overall measurement status of the shipment" CargoCompany: string Comment: string Created: DateTime IsAllMeasured: bool purpose: "Status flag" LicencePlate: string MeasuredDate: DateTime? Modified: DateTime ShippingDate: DateTime ShippingDocuments: List 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: "A digital representation of a supplier's delivery note or invoice associated with the shipment, used for reconciling paper-based data with measured reality" 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 other-key: "ShippingDocumentId" navigation: "one-to-many" inverse-property: "ShippingDocument" ShippingId: int? ShippingItems: List 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 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 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 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 }