Goods Received Notes (أذونات استلام البضائع)
Record goods received from suppliers. Supports 3 modes controlled by the purchases.grn_mode setting.
GRN Modes
| Mode | Description | Flow |
|---|---|---|
direct | GRN endpoints disabled | Stock added on Purchase Bill approval |
grn | Standard receiving | Draft → Approved (stock added) |
grn_quality | Receiving + quality check | Draft → Pending Quality → Quality Approved/Rejected → Approved |
Status Workflow
grn Mode
grn_quality Mode
| Status | Editable | Can Submit Quality | Can Quality Check | Can Approve | Can Cancel |
|---|---|---|---|---|---|
draft | Yes | Yes* | No | Yes** | Yes |
pending_quality | No | No | Yes | No | Yes |
quality_approved | No | No | No | Yes | Yes |
quality_rejected | No | No | No | No | Yes |
approved | No | No | No | No | No |
cancelled | No | No | No | No | No |
*Submit Quality only in grn_quality mode. **Approve from draft only in grn mode.
Entity Attributes
PurchaseGrn
| Field | Type | Description |
|---|---|---|
id | integer | Primary key |
company_id | integer | Tenant company |
branch_id | integer | Branch (nullable) |
grn_number | string | Auto-generated (e.g., GRN-2026-00001) |
date | date | Receipt date |
purchase_order_id | integer | Source PO (nullable) |
warehouse_id | integer | Receiving warehouse |
supplier_id | integer | Supplier business partner ID |
supplier_name | string | Cached supplier name |
status | enum | See workflow above (6 states) |
reference | string | External reference |
notes / notes_ar | text | Additional notes |
quality_notes | text | Quality check notes |
total_quantity | decimal(15,3) | Sum of item quantities |
total_cost | decimal(15,3) | Sum of item costs |
inventory_receipt_id | integer | Created inventory receipt (after approve) |
created_by | integer | Creator user ID |
approved_by | integer | Approver user ID |
approved_at | datetime | Approval timestamp |
quality_checked_by | integer | Quality checker user ID |
quality_checked_at | datetime | Quality check timestamp |
cancelled_by | integer | Canceller user ID |
cancelled_at | datetime | Cancellation timestamp |
cancellation_reason | text | Cancellation reason |
PurchaseGrnItem
| Field | Type | Description |
|---|---|---|
id | integer | Primary key |
purchase_grn_id | integer | FK to GRN |
purchase_order_item_id | integer | FK to PO item (nullable) |
product_id | integer | Product |
product_variant_id | integer | Optional variant |
unit_id | integer | Unit of measure |
description / description_ar | string | Item description |
quantity | decimal(15,3) | Received quantity |
accepted_quantity | decimal(15,3) | Accepted after quality (nullable) |
rejected_quantity | decimal(15,3) | Rejected after quality (nullable) |
unit_cost | decimal(15,3) | Unit cost |
total_cost | decimal(15,3) | Calculated total cost |
batch_number | string | Batch tracking |
expiry_date | date | Expiry date |
serial_numbers | json | Serial numbers array |
rejection_reason | text | Reason for rejection |
notes | text | Item-level notes |
sort_order | integer | Display order |
API Endpoints
| Method | Endpoint | Permission | Description |
|---|---|---|---|
GET | /api/purchases/grns | purchases.grns.view | List GRNs |
POST | /api/purchases/grns | purchases.grns.create | Create GRN |
GET | /api/purchases/grns/{id} | purchases.grns.view | Show GRN |
PUT | /api/purchases/grns/{id} | purchases.grns.update | Update GRN |
DELETE | /api/purchases/grns/{id} | purchases.grns.delete | Delete draft GRN |
POST | /api/purchases/grns/{id}/submit-quality | purchases.grns.approve | Submit for quality check |
POST | /api/purchases/grns/{id}/quality-check | purchases.grns.approve | Record quality results |
POST | /api/purchases/grns/{id}/approve | purchases.grns.approve | Approve GRN |
POST | /api/purchases/grns/{id}/cancel | purchases.grns.cancel | Cancel GRN |
Query Parameters (List)
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status |
purchase_order_id | integer | Filter by purchase order |
supplier_id | integer | Filter by supplier |
warehouse_id | integer | Filter by warehouse |
date_from | date | Start date filter |
date_to | date | End date filter |
search | string | Search in grn_number, reference |
Examples
Create GRN from Purchase Order
bash
curl -X POST /api/purchases/grns \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"date": "2026-02-25",
"purchase_order_id": 1,
"warehouse_id": 1,
"supplier_id": 1,
"items": [
{
"purchase_order_item_id": 1,
"product_id": 1,
"unit_id": 1,
"quantity": 50,
"unit_cost": 25.000
}
]
}'dart
final response = await dio.post('/api/purchases/grns', data: {
'date': '2026-02-25',
'purchase_order_id': 1,
'warehouse_id': 1,
'supplier_id': 1,
'items': [
{
'purchase_order_item_id': 1,
'product_id': 1,
'unit_id': 1,
'quantity': 50,
'unit_cost': 25.000,
}
],
});Quality Check (Accept/Reject Items)
bash
curl -X POST /api/purchases/grns/1/quality-check \
-H "Authorization: Bearer {token}" \
-d '{
"approved": true,
"quality_notes": "5 items had damaged packaging",
"items": [
{
"id": 1,
"accepted_quantity": 45,
"rejected_quantity": 5,
"rejection_reason": "Damaged packaging"
}
]
}'dart
await dio.post('/api/purchases/grns/1/quality-check', data: {
'approved': true,
'quality_notes': '5 items had damaged packaging',
'items': [
{
'id': 1,
'accepted_quantity': 45,
'rejected_quantity': 5,
'rejection_reason': 'Damaged packaging',
}
],
});Approve GRN (Stock Integration)
bash
curl -X POST /api/purchases/grns/1/approve \
-H "Authorization: Bearer {token}"dart
await dio.post('/api/purchases/grns/1/approve');Stock Integration
When a GRN is approved:
- Inventory Receipt is created with
reference_type = 'purchase' - The receipt is auto-approved via the Inventory module's
ApproveReceiptaction - Stock balances are increased, cost layers created, inventory movements recorded
- If linked to a PO, the PO item's
received_quantityis updated - The PO's
receive_statusis recalculated (pending/partial/complete)
In quality mode (grn_quality), only the accepted_quantity goes to stock. Rejected items are recorded but do not create stock entries.
Business Rules
- GRN endpoints return 403 when
purchases.grn_modeis set todirect - GRNs are auto-numbered using the
GRNsequence (e.g.,GRN-2026-00001) - Only draft GRNs can be edited or deleted
- Submit for quality is only available in
grn_qualitymode - Quality check records
accepted_quantityandrejected_quantityper item with optional rejection reasons - Approve can be called from
draft(ingrnmode) orquality_approved(ingrn_qualitymode) - Approved GRNs cannot be cancelled (stock has already been added)
- The
inventory_receipt_idfield links to the created inventory receipt for traceability