WMSSeptember 2026 · 4 min read

Inventory Database Schema

By go2ismail · Published · .NET 10

At a glance

The inventory model uses string identifiers, decimal prices, nullable double quantities, and an InventoryTransaction ledger. Stock is a signed contribution per row, not a running balance.

Read the entity and its configuration together

An inventory database schema is more than a list of table names. To understand this implementation, read the C# entities alongside AppDbContext and the EF entity configurations. The Blazor WMS Source Code package contains those layers. The tables below describe the inspected model; they do not claim to be a schema export from the demo database.

Start with inherited identity and audit fields

Product, Warehouse, and InventoryTransaction inherit BaseEntity. Its Id is a C# string initialized by a sequential identifier utility. AppDbContext configures the Id with maximum length 36 and fixed length. That is not a declaration of a native SQL Server uniqueidentifier column.

Inherited propertyC# typeRole
IdstringPrimary key; configured length 36.
CreatedAt / UpdatedAtDateTimeOffset?Creation and latest-update timestamps.
CreatedBy / UpdatedBystring?Actor identifiers; configured maximum length 500.
IsDeletedboolSoft-delete flag, initially false.

Product describes an item, not its warehouse balance

Product propertyC# typeMeaning
Name / AutoNumberstring?Display name and generated reference; maximum length 500.
Descriptionstring?Description; maximum length 1000.
UnitPricedecimal?Price value.
Physicalbool?Physical-item flag; initialized to true.
UnitMeasureId / ProductGroupIdstring?Foreign keys to unit and group; maximum length 36.

ProductConfiguration declares a unique index on AutoNumber and indexes on Name, UnitMeasureId, and ProductGroupId. The unit and group relationships use NoAction delete behavior. A unit price’s C# decimal type should not be used to invent a precision and scale absent from the inspected configuration; inspect the generated schema for the chosen provider.

Warehouse supplies the stock location and special movement roles

Warehouse propertyC# typeMeaning
Namestring?Warehouse name.
Descriptionstring?Warehouse description.
SystemWarehousebool?Special warehouse-role flag; initialized to false.

These are the entity’s own fields in addition to BaseEntity. InventoryTransaction separately holds the references that connect product movement to Warehouse. The same parent type is used for Warehouse, WarehouseFrom, and WarehouseTo navigation properties, so the EF configuration explicitly maps all three.

InventoryTransaction stores movement facts

Property groupC# typeInterpretation
ModuleId / ModuleName / ModuleCode / ModuleNumberstring?Source-document metadata; ModuleId is a generic application reference.
MovementDateDateTime?Operational movement date.
StatusInventoryTransactionStatusDraft = 0, Cancelled = 1, Confirmed = 2, Archived = 3.
AutoNumberstring?Generated transaction reference.
WarehouseId / ProductIdstring?Stock warehouse and product foreign keys.
WarehouseFromId / WarehouseToIdstring?Movement-origin and destination foreign keys.
Movement / Stockdouble?Movement magnitude and signed stock contribution.
TransTypeInventoryTransType?In = 1 or Out = −1.
QtySCSys / QtySCCount / QtySCDeltadouble?System quantity, counted quantity, and count difference.

A nullable type describes what the model can represent; it is not a complete statement of what a particular workflow accepts. For example, the calculation helper checks quantity rules, and a form may require fields that are nullable on the entity. Read the operation’s request and handler before constructing a write payload.

Interpret Stock correctly

The helper multiplies movement magnitude by direction and stores the result in Stock. The stock-report query filters confirmed records and sums Stock grouped by WarehouseId and ProductId. A row containing −5 means that movement contributes a reduction of five; it does not mean the warehouse balance is negative five.

This also distinguishes operational dates from audit timestamps. MovementDate belongs to the warehouse event, while CreatedAt records creation of the database entity. A date-range report must intentionally choose which one it uses. The workflow guide demonstrates the calculation with receipts and deliveries.

The current stock-report handler also requires Product.Physical == true and Warehouse.SystemWarehouse == false. Its displayed balances therefore concern physical products in ordinary warehouses, rather than every record with a confirmed status.

Inspect indexes without assuming a tuning strategy

InventoryTransactionConfiguration declares a unique AutoNumber index and separate indexes on MovementDate, WarehouseId, and ProductId. It maps the Product and three Warehouse relationships with NoAction delete behavior. These declarations are concrete evidence; they do not establish a composite warehouse-product-status index or prove performance at a particular transaction volume.

For a larger installation, inspect the real report query and database execution plan before changing indexes. Quantity precision is another deliberate extension decision: the inspected quantity fields use nullable double, whereas UnitPrice uses nullable decimal. If a domain requires exact decimal quantity arithmetic, that change must cover entities, DTOs, calculations, and persisted data.

Account for soft deletion and schema evolution

AppDbContext applies a query filter for IsDeleted and fills audit fields during saves. This affects what normal reports see. Its startup uses EnsureCreated rather than a demonstrated migration sequence. When adapting the schema, preserve the distinction between the C# model, generated relational schema, and any existing database that already contains data.

Continue with warehouse database design for the relationship overview or the complete WMS source-code product to work with the full application.