# Data Layer > Part of `Nop.Plugin.Misc.FruitBankPlugin`. See `README.md` for project overview. > For entity model see `docs/DOMAIN_MODEL.md`. > For measurement workflows see `docs/MEASUREMENT.md`. ## DbContext Classes ### FruitBankDbContext Inherits `MgDbContextBase` (from `Mango.Nop.Data`). Main data context for orders, shipping, partners, and files. **Implements interfaces:** IOrderDtoDbSet, IOrderItemDtoDbSet, IPartnerDbSet, IShippingDbSet, IShippingDocumentDbSet, IShippingItemDbSet, IShippingItemPalletDbSet, IOrderItemPalletDbSet, IShippingDocumentToFilesDbSet, IFilesDbSet **DbSet properties:** | DbSet | Entity | Table | |---|---|---| | `OrderDtos` | OrderDto | Order | | `OrderItemDtos` | OrderItemDto | OrderItem | | `OrderItemPallets` | OrderItemPallet | fbOrderItemPallet | | `Partners` | Partner | fbPartner | | `Shippings` | Shipping | fbShipping | | `ShippingDocuments` | ShippingDocument | fbShippingDocument | | `ShippingItems` | ShippingItem | fbShippingItem | | `ShippingItemPallets` | ShippingItemPallet | fbShippingItemPallet | | `Files` | Files | fbFiles | | `ShippingDocumentToFiles` | ShippingDocumentToFiles | fbShippingDocumentToFiles | | `ProductDtos` | ProductDto | Product | | `GenericAttributeDtos` | GenericAttributeDto | GenericAttribute | | `StockQuantityHistoryDtos` | StockQuantityHistoryDto | StockQuantityHistory | | `Customers` | Customer | Customer | | `CustomerRoles` | CustomerRole | CustomerRole | **Key methods:** | Method | Purpose | |---|---| | `AddShippingItemAsync()` | Creates item, copies `IsMeasurable` from ProductDto | | `UpdateShippingItemAsync()` | Complex: updates measuring values, handles stock/weight changes, manages product transitions | | `SetupShippingItemMeasuringValues()` | Recalculates totals from child pallets | | `AddShippingItemPalletAsync()` / `UpdateShippingItemPalletAsync()` | Pallet CRUD with cascade to parent ShippingItem | | `StartMeasuringAsync()` | Sets MeasurementOwnerId GenericAttribute, updates order status | | `SetOrderStatusToCompleteAsync()` | Validates pallets, updates stock, sets RevisorId, publishes event | | `DeleteOrderItemConstraintsAsync()` | Cleanup when order items deleted | | `UpdateStockQuantityAndWeightAsync()` | Updates Product.StockQuantity + NetWeight, creates history entry | | `DeleteShippingSafeAsync()` | Transactional cascade delete of shipping + children | | `DeleteShippingDocumentSafeAsync()` | Transactional cascade delete of document + children | | `GetCustomersBySystemRoleName()` | LINQ query for role-based customer lookup | All state-changing operations wrapped in `TransactionSafeAsync()` for ACID compliance. ### StockTakingDbContext Inherits `MgDbContextBase`. Dedicated context for inventory sessions. **DbSet properties:** | DbSet | Entity | |---|---| | `ProductDtos` | ProductDto | | `OrderItemDtos` | OrderItemDto | | `StockTakings` | StockTaking | | `StockTakingItems` | StockTakingItem | | `StockTakingItemPallets` | StockTakingItemPallet | | `StockQuantityHistories` | StockQuantityHistory | | `GenericAttributes` | GenericAttribute | **Key methods:** | Method | Purpose | |---|---| | `CloseStockTaking()` | Validates all measured, updates stock/weight per item, marks closed | | `AddStockTakingItemPallet()` / `UpdateStockTakingItemPallet()` | Pallet CRUD with measured values refresh | | `RefreshStockTakingItemMeasuredValuesFromPallets()` | Sums quantities/weights from pallets | ## DbTable Repositories Each entity has a `*DbTable` class inheriting `MgDbTableBase` (from `Mango.Nop.Data`). Located in `Domains/DataLayer/`. ### ShippingDbTable | Method | Purpose | |---|---| | `GetAll()` | All shippings ordered by ShippingDate | | `GetAll(bool loadRelations)` | Eager loads full graph: Documents → Items → Pallets + Pallet type + ProductDto + GenericAttributes | | `GetAllNotMeasured()` | Filters for incomplete/recent shipments | | `GetByIdAsync(bool loadRelations)` | Single with optional eager loading | | `OnUpdate()` | Hook: sets `MeasuredDate` when `IsAllMeasured` becomes true | ### OrderDtoDbTable | Method | Purpose | |---|---| | `GetAll(bool loadRelations)` | Eager loads: GenericAttributes, Customer, OrderNotes, OrderItemDtos (with ProductDto, GenericAttributes, OrderItemPallets) | | `GetByIdAsync(bool loadRelations)` | Single order with optional relations | | `GetAllByOrderStatus()` | Status-filtered query | | `GetAllForMeasuring(DateTime fromDate)` | Unpaid, not cancelled, with DateOfReceipt >= fromDate | | `GetAllByProductId()` / `GetAllByProductIds()` | Product-filtered queries | | `GetAllByIds()` | Batch retrieval | ### Other DbTable classes | Class | Entity | Key behaviors | |---|---|---| | `ProductDtoDbTable` | ProductDto | GetAll with GenericAttributes | | `PartnerDbTable` | Partner | Standard CRUD | | `ShippingDocumentDbTable` | ShippingDocument | GetAll with items, files, partner | | `ShippingItemDbTable` | ShippingItem | GetAll with pallets | | `ShippingItemPalletDbTable` | ShippingItemPallet | Standard CRUD | | `OrderItemDtoDbTable` | OrderItemDto | GetAll with pallets, product | | `OrderItemPalletDbTable` | OrderItemPallet | Standard CRUD | | `FilesDbTable` | Files | Standard CRUD | | `ShippingDocumentToFilesDbTable` | ShippingDocumentToFiles | Standard CRUD | | `StockTakingDbTable` | StockTaking | GetAll with items, pallets | | `StockTakingItemDbTable` | StockTakingItem | GetAll with pallets | | `StockTakingItemPalletDbTable` | StockTakingItemPallet | Standard CRUD | | `StockQuantityHistoryDtoDbTable` | StockQuantityHistoryDto | Filtered by date range | ## Repository Interfaces Located in `Domains/DataLayer/Interfaces/`. Each interface (e.g., `IShippingDbSet`, `IOrderDtoDbSet`) declares the DbSet property that the DbContext must implement. This allows DbTable classes to accept any context that provides the required DbSet. ## Entity Mapping - `Mapping/Builders/PluginBuilder.cs` — EF Core Fluent API entity configuration for plugin-owned tables (fb* prefix) - `Mapping/NameCompatibility.cs` — Table name mapping for nopCommerce compatibility - `Migrations/SchemaMigration.cs` — FluentMigrator schema setup for `CustomTable` base entity ## FruitBankEventConsumer Located in `Domains/EventConsumers/`. Extends `MgEventConsumerBase` (from `Mango.Nop.Services`). Handles entity lifecycle events for cascading updates. **Product events:** | Event | Action | |---|---| | `EntityInsertedEvent` | Saves custom attributes (IsMeasurable, NetWeight, Tare, AverageWeight, AverageWeightTreshold, IncomingQuantity) | | `EntityUpdatedEvent` | Updates custom attributes, syncs IsMeasurable/Tare changes to existing ShippingItems | **Shipping cascade events:** | Event | Action | |---|---| | `EntityDeletedEvent` | Refreshes parent ShippingItem measuring values | | `EntityInsertedEvent/UpdatedEvent` | Rechecks ShippingDocument.IsAllMeasured | | `EntityInsertedEvent/UpdatedEvent` | Rechecks Shipping.IsAllMeasured | | `EntityDeletedEvent` | Cascade deletes child documents | | `EntityDeletedEvent` | Cascade deletes child items | | `EntityDeletedEvent` | Cascade deletes child pallets | **Order events:** | Event | Action | |---|---| | `EntityDeletedEvent` | Cleanup via `MeasurementService.DeleteOrderItemConstraintsAsync()` | | `EntityInsertedEvent/UpdatedEvent` | Post-process via `MeasurementService.OrderItemInsertedOrUpdatedPostProcess()` | ## Eager Loading Pattern DbTable `GetAll(bool loadRelations = true)` uses `LoadWith()` chains to eagerly load entity graphs. Example for OrderDtoDbTable: ``` OrderDto → GenericAttributes → Customer → OrderNotes → OrderItemDtos → ProductDto → GenericAttributes → GenericAttributes → OrderItemPallets ``` This avoids N+1 queries when transferring full entity graphs to FruitBankHybridApp via SignalR.