# 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<T>` (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<Product>` | Saves custom attributes (IsMeasurable, NetWeight, Tare, AverageWeight, AverageWeightTreshold, IncomingQuantity) |
| `EntityUpdatedEvent<Product>` | Updates custom attributes, syncs IsMeasurable/Tare changes to existing ShippingItems |

**Shipping cascade events:**

| Event | Action |
|---|---|
| `EntityDeletedEvent<ShippingItemPallet>` | Refreshes parent ShippingItem measuring values |
| `EntityInsertedEvent/UpdatedEvent<ShippingItem>` | Rechecks ShippingDocument.IsAllMeasured |
| `EntityInsertedEvent/UpdatedEvent<ShippingDocument>` | Rechecks Shipping.IsAllMeasured |
| `EntityDeletedEvent<Shipping>` | Cascade deletes child documents |
| `EntityDeletedEvent<ShippingDocument>` | Cascade deletes child items |
| `EntityDeletedEvent<ShippingItem>` | Cascade deletes child pallets |

**Order events:**

| Event | Action |
|---|---|
| `EntityDeletedEvent<OrderItem>` | Cleanup via `MeasurementService.DeleteOrderItemConstraintsAsync()` |
| `EntityInsertedEvent/UpdatedEvent<OrderItem>` | 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.
