# Domain Model — Behavioral Documentation

> Part of `Nop.Plugin.Misc.FruitBankPlugin`. See `README.md` for project overview.
> For entity definitions, properties, and relationships see `docs/SCHEMA.md` (authoritative TOON schema).
> For measurement workflows see `docs/MEASUREMENT.md`.
> For the customer pre-order workflow see `docs/PREORDER/README.md`.
> For data layer details see `docs/DATA_LAYER.md`.
> For core measurement system rules and common domain traps, see: `../../../../../FruitBankHybridApp/FruitBank.Common/docs/GLOSSARY.md`

This document explains **how the domain model behaves** in the plugin. For **what the entities are** (properties, types, relationships), see `docs/SCHEMA.md`.

## Weight Formula

All three pallet types (ShippingItemPallet, OrderItemPallet, StockTakingItemPallet) use the same formula:

```
NetWeight = GrossWeight - PalletWeight - (TrayQuantity * TareWeight)
```

| Field | Meaning |
|---|---|
| `GrossWeight` | Scale reading (total weight). 0.0 if product is not measurable |
| `PalletWeight` | Weight of the physical pallet. 0.0 if goods arrive without pallet |
| `TareWeight` | Weight of one tray/crate (per unit) |
| `TrayQuantity` | Number of trays/crates. Always recorded, even for non-measurable products |
| `NetWeight` | Computed, read-only, not-mapped (setter exists for shared interface but throws Exception) |

## MeasuringStatus Lifecycle

```
NotStarted (0)  →  Started (10)  →  Finnished (20)  →  Audited (30)
   [no record]      [record created]   [IsMeasured=true]   [RevisorId > 0]
```

- **NotStarted**: No pallet record exists yet
- **Started**: Pallet record created (Id > 0) but `IsMeasured = false`
- **Finnished**: `IsMeasured = true` — weights recorded
- **Audited**: `RevisorId > 0` — quality auditor confirmed (OrderItemPallet only)

## IsMeasurable Flag

`ProductDto.IsMeasurable` is the master toggle (stored as GenericAttribute). When `false`:
- Weight validation is bypassed
- One pallet record is still created with `GrossWeight = 0.0`
- Only `TrayQuantity` is recorded
- Product is not included in average weight checks

## GenericAttribute Keys

Custom product/order properties are stored via nopCommerce's GenericAttribute table. The DTO classes expose these as computed, not-mapped properties.

### Product attributes (saved by FruitBankEventConsumer)

| Key | Type | Purpose |
|---|---|---|
| `IsMeasurable` | bool | Master flag for weight validation |
| `NetWeight` | double | Product net weight |
| `Tare` | double | Tare weight per tray |
| `AverageWeight` | double | Expected average weight per tray |
| `AverageWeightTreshold` | double | Max allowed deviation % from AverageWeight |
| `IncomingQuantity` | int | Incoming stock not yet received |

### Order attributes

| Key | Type | Purpose |
|---|---|---|
| `DateOfReceipt` | DateTime | Scheduled delivery date |
| `MeasurementOwnerId` | int | User who started measuring |
| `RevisorId` | int | Quality auditor user ID |

## Entity Hierarchy Overview

### Inbound Delivery (Shipping)

```
Shipping (fbShipping)                         ← truck arrival
  └─ ShippingDocument (fbShippingDocument)    ← supplier delivery note  [1:N]
       ├─ ShippingItem (fbShippingItem)       ← product line            [1:N]
       │    └─ ShippingItemPallet             ← measurement event       [1:N]
       ├─ ShippingDocumentToFiles             ← uploaded PDFs           [1:N]
       │    └─ Files (fbFiles)                                          [N:1]
       └─ Partner (fbPartner)                 ← supplier                [N:1]
```

### Outbound Order

```
OrderDto (Order)                              ← nopCommerce order
  ├─ OrderItemDto (OrderItem)                 ← line item               [1:N]
  │    ├─ OrderItemPallet (fbOrderItemPallet) ← measurement record      [1:N]
  │    └─ ProductDto (Product)                                          [N:1]
  ├─ Customer                                                           [N:1]
  └─ GenericAttributeDto                      ← custom attributes       [1:N]
```

### Inventory (StockTaking)

```
StockTaking (fbStockTaking)                   ← inventory session
  └─ StockTakingItem (fbStockTakingItem)      ← product line            [1:N]
       ├─ StockTakingItemPallet               ← measurement record      [1:N]
       └─ ProductDto (Product)                                          [N:1]
```

## FullProcessModel

Container for bulk data sync between server and FruitBankHybridApp via SignalR:

| Property | Type | Purpose |
|---|---|---|
| `Orders` | `List<OrderDto>` | All active orders with full graph |
| `Shippings` | `List<Shipping>` | All active shipments with full graph |
| `StockTakings` | `List<StockTaking>` | All active inventory sessions |

## Note on "Pallet" Naming

The term "Pallet" in `OrderItemPallet`, `ShippingItemPallet`, and `StockTakingItemPallet` is a legacy naming convention. These are **general measurement records** that are always created for every item, regardless of whether goods arrive on a physical pallet. For non-measurable products, weight fields are 0.0 and only `TrayQuantity` is tracked.
