Inventory Counts & Adjustments
Physical inventory counts and stock adjustments to reconcile actual vs. system quantities.
Purpose
- Perform physical inventory counts to verify actual stock levels against the system
- Automatically calculate differences between counted and system quantities
- Auto-create draft adjustments for variances found during counts
- Create manual adjustments for damage, expiry, corrections, or other reasons
- Maintain an audit trail with approval and cancellation workflows
Inventory Counts
Entity Attributes
| Attribute | Type | Description |
|---|---|---|
id | integer | Auto-incremented primary key |
company_id | integer | Company that owns this count |
warehouse_id | integer | Warehouse being counted |
count_number | string | Auto-generated (e.g., CNT-000001) |
date | date | Count date |
status | enum | draft, approved, cancelled |
notes | text | Optional notes |
created_by | integer | User who created the count |
approved_by | integer | User who finalized the count |
approved_at | datetime | When the count was finalized |
Count Item Attributes
| Attribute | Type | Description |
|---|---|---|
id | integer | Auto-incremented primary key |
inventory_count_id | integer | Parent count |
product_id | integer | Product being counted |
product_variant_id | integer | Optional variant |
unit_id | integer | Unit of measure |
system_quantity | decimal(15,3) | System quantity at time of count |
counted_quantity | decimal(15,3) | Physically counted quantity |
difference | decimal(15,3) | counted_quantity - system_quantity |
unit_cost | decimal(15,3) | Average cost from stock balance |
Count Workflow
- Draft — can be edited, updated, or deleted freely
- Finalize — refreshes system quantities from current stock balances, computes differences, marks as approved, and auto-creates a draft adjustment for items with non-zero differences
Stock Adjustments
Entity Attributes
| Attribute | Type | Description |
|---|---|---|
id | integer | Auto-incremented primary key |
company_id | integer | Company that owns this adjustment |
warehouse_id | integer | Warehouse being adjusted |
adjustment_number | string | Auto-generated (e.g., ADJ-000001) |
date | date | Adjustment date |
status | enum | draft, approved, cancelled |
reason | enum | count, damage, expiry, correction, other |
inventory_count_id | integer | Source count (if created from a count) |
notes | text | Optional notes |
total_quantity_increase | decimal(15,3) | Sum of increase items |
total_quantity_decrease | decimal(15,3) | Sum of decrease items |
total_value_increase | decimal(15,3) | Total cost of increases |
total_value_decrease | decimal(15,3) | Total cost of decreases |
Adjustment Item Attributes
| Attribute | Type | Description |
|---|---|---|
id | integer | Auto-incremented primary key |
inventory_adjustment_id | integer | Parent adjustment |
product_id | integer | Product being adjusted |
product_variant_id | integer | Optional variant |
unit_id | integer | Unit of measure |
adjustment_type | enum | increase or decrease |
quantity | decimal(15,3) | Quantity to adjust |
unit_cost | decimal(15,3) | Cost per unit |
total_cost | decimal(15,3) | quantity * unit_cost |
Adjustment Workflow
ER Diagram
API Endpoints
Inventory Counts
| Method | Endpoint | Description | Permission |
|---|---|---|---|
GET | /api/inventory/counts | List counts (paginated) | inventory.counts.view |
POST | /api/inventory/counts | Create a count | inventory.counts.create |
GET | /api/inventory/counts/{id} | Get count details | inventory.counts.view |
PUT | /api/inventory/counts/{id} | Update a draft count | inventory.counts.create |
DELETE | /api/inventory/counts/{id} | Delete a draft count | inventory.counts.create |
POST | /api/inventory/counts/{id}/finalize | Finalize a count | inventory.counts.finalize |
Stock Adjustments
| Method | Endpoint | Description | Permission |
|---|---|---|---|
GET | /api/inventory/adjustments | List adjustments (paginated) | inventory.adjustments.view |
POST | /api/inventory/adjustments | Create an adjustment | inventory.adjustments.create |
GET | /api/inventory/adjustments/{id} | Get adjustment details | inventory.adjustments.view |
POST | /api/inventory/adjustments/{id}/approve | Approve an adjustment | inventory.adjustments.approve |
POST | /api/inventory/adjustments/{id}/cancel | Cancel an adjustment | inventory.adjustments.cancel |
Query Parameters — Counts
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status (draft, approved, cancelled) |
warehouse_id | integer | Filter by warehouse |
date_from | date | Filter from date |
date_to | date | Filter to date |
search | string | Search by count number |
Query Parameters — Adjustments
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status (draft, approved, cancelled) |
warehouse_id | integer | Filter by warehouse |
reason | string | Filter by reason (count, damage, expiry, correction, other) |
date_from | date | Filter from date |
date_to | date | Filter to date |
search | string | Search by adjustment number |
Examples
Create an Inventory Count
bash
curl -X POST https://moon-erp.elbaset.com/api/inventory/counts \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"warehouse_id": 1,
"date": "2026-02-23",
"notes": "Monthly count",
"items": [
{
"product_id": 1,
"unit_id": 1,
"counted_quantity": 95
}
]
}'dart
final response = await http.post(
Uri.parse('$baseUrl/api/inventory/counts'),
headers: {'Authorization': 'Bearer $token', 'Content-Type': 'application/json'},
body: jsonEncode({
'warehouse_id': 1,
'date': '2026-02-23',
'notes': 'Monthly count',
'items': [
{'product_id': 1, 'unit_id': 1, 'counted_quantity': 95},
],
}),
);Finalize a Count
bash
curl -X POST https://moon-erp.elbaset.com/api/inventory/counts/1/finalize \
-H "Authorization: Bearer {token}"dart
final response = await http.post(
Uri.parse('$baseUrl/api/inventory/counts/1/finalize'),
headers: {'Authorization': 'Bearer $token'},
);Create a Manual Adjustment
bash
curl -X POST https://moon-erp.elbaset.com/api/inventory/adjustments \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"warehouse_id": 1,
"date": "2026-02-23",
"reason": "damage",
"notes": "Water damage to stock",
"items": [
{
"product_id": 1,
"unit_id": 1,
"adjustment_type": "decrease",
"quantity": 5,
"unit_cost": 10
}
]
}'dart
final response = await http.post(
Uri.parse('$baseUrl/api/inventory/adjustments'),
headers: {'Authorization': 'Bearer $token', 'Content-Type': 'application/json'},
body: jsonEncode({
'warehouse_id': 1,
'date': '2026-02-23',
'reason': 'damage',
'notes': 'Water damage to stock',
'items': [
{
'product_id': 1,
'unit_id': 1,
'adjustment_type': 'decrease',
'quantity': 5,
'unit_cost': 10,
},
],
}),
);Approve an Adjustment
bash
curl -X POST https://moon-erp.elbaset.com/api/inventory/adjustments/1/approve \
-H "Authorization: Bearer {token}"dart
final response = await http.post(
Uri.parse('$baseUrl/api/inventory/adjustments/1/approve'),
headers: {'Authorization': 'Bearer $token'},
);Business Rules
- Counts can only be finalized from
draftstatus - Finalizing a count refreshes system quantities, computes differences, and auto-creates a draft adjustment if any items have non-zero differences
- Adjustments can be created manually or auto-created from counts
- Approving an adjustment updates stock balances (increase adds stock, decrease removes stock)
- Cancelling an approved adjustment reverses all stock changes
- Cancelling a draft adjustment has no stock effect
- Only draft counts can be edited or deleted
- Count and adjustment numbers are auto-generated via the Sequence system
- All operations are company-scoped (tenant-isolated)