Purchase Requests (طلبات الشراء)
Internal purchase requisitions — departments request items before a formal Purchase Order is created.
Status Workflow
| Status | Editable | Can Submit | Can Approve | Can Reject | Can Cancel |
|---|---|---|---|---|---|
draft | Yes | Yes | No | No | Yes |
pending_approval | No | No | Yes | Yes | Yes |
approved | No | No | No | No | Yes |
rejected | Yes | Yes | No | No | No |
converted | No | No | No | No | No |
cancelled | No | No | No | No | No |
Entity Attributes
PurchaseRequest
| Field | Type | Description |
|---|---|---|
id | integer | Primary key |
company_id | integer | Tenant company |
branch_id | integer | Branch (nullable) |
request_number | string | Auto-generated (e.g., PR-2026-00001) |
date | date | Request date |
requested_by | integer | User ID of requester |
department | string | Department name (EN) |
department_ar | string | Department name (AR) |
needed_by | date | Required-by date |
priority | enum | low, normal, high, urgent |
reason / reason_ar | string | Justification |
notes / notes_ar | string | Additional notes |
cost_center_id | integer | Optional cost center |
status | enum | See workflow above |
approved_by | integer | Approver user ID |
approved_at | datetime | Approval timestamp |
rejection_reason | string | Reason for rejection |
subtotal | decimal(15,3) | Sum of item estimated totals |
PurchaseRequestItem
| Field | Type | Description |
|---|---|---|
id | integer | Primary key |
purchase_request_id | integer | FK to purchase request |
sort_order | integer | Display order |
product_id | integer | Requested product |
product_variant_id | integer | Optional variant |
unit_id | integer | Unit of measure |
description / description_ar | string | Item description |
quantity | decimal(15,3) | Requested quantity |
estimated_price | decimal(15,3) | Estimated unit price |
estimated_total | decimal(15,3) | quantity x estimated_price |
preferred_supplier_id | integer | Optional preferred supplier |
notes | string | Item-level notes |
API Endpoints
| Method | Endpoint | Permission | Description |
|---|---|---|---|
GET | /api/purchases/requests | purchases.requests.view | List purchase requests |
POST | /api/purchases/requests | purchases.requests.create | Create purchase request |
GET | /api/purchases/requests/{id} | purchases.requests.view | Show purchase request |
PUT | /api/purchases/requests/{id} | purchases.requests.update | Update purchase request |
DELETE | /api/purchases/requests/{id} | purchases.requests.delete | Delete draft request |
POST | /api/purchases/requests/{id}/submit-approval | purchases.requests.approve | Submit for approval |
POST | /api/purchases/requests/{id}/approve | purchases.requests.approve | Approve request |
POST | /api/purchases/requests/{id}/reject | purchases.requests.approve | Reject request |
POST | /api/purchases/requests/{id}/cancel | purchases.requests.approve | Cancel request |
Query Parameters (List)
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status |
requested_by | integer | Filter by requester user ID |
priority | string | Filter by priority |
branch_id | integer | Filter by branch |
date_from | date | Start date filter |
date_to | date | End date filter |
search | string | Search in request_number, department, reason |
Examples
Create Purchase Request
bash
curl -X POST /api/purchases/requests \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"date": "2026-02-25",
"department": "IT",
"department_ar": "تقنية المعلومات",
"needed_by": "2026-03-15",
"priority": "high",
"reason": "Equipment replacement",
"items": [
{
"product_id": 1,
"unit_id": 1,
"quantity": 10,
"estimated_price": 25.000
}
]
}'dart
final response = await dio.post('/api/purchases/requests', data: {
'date': '2026-02-25',
'department': 'IT',
'department_ar': 'تقنية المعلومات',
'needed_by': '2026-03-15',
'priority': 'high',
'reason': 'Equipment replacement',
'items': [
{
'product_id': 1,
'unit_id': 1,
'quantity': 10,
'estimated_price': 25.000,
}
],
});Reject Request
bash
curl -X POST /api/purchases/requests/1/reject \
-H "Authorization: Bearer {token}" \
-d '{"reason": "Budget exceeded"}'dart
await dio.post('/api/purchases/requests/1/reject', data: {
'reason': 'Budget exceeded',
});Business Rules
- Purchase requests are auto-numbered using the
PRsequence (e.g.,PR-2026-00001) - Only draft and rejected requests can be edited or resubmitted
- Only draft requests can be deleted
- Submitting for approval requires at least one item
- Rejection requires a reason string
- The
subtotalis automatically recalculated when items change