Supplier Price Lists (قوائم أسعار الموردين)
Supplier price lists store supplier-specific pricing for products. Prices support validity periods, minimum order quantities, and lead time tracking. The comparison endpoint lets you compare prices across suppliers for a given product.
Feature Toggles
| Setting | Default | Description |
|---|---|---|
purchases.enable_supplier_price_lists | false | Enable CRUD endpoints (returns 403 when disabled) |
purchases.enable_supplier_comparison | false | Enable comparison endpoint (returns 403 when disabled) |
Entity Attributes
SupplierPriceList
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | auto | Primary key |
company_id | FK | auto | Company |
partner_id | FK | yes | Supplier (business partner) |
product_id | FK | yes | Product |
product_variant_id | FK | no | Product variant |
unit_id | FK | yes | Unit of measure |
price | decimal(15,3) | yes | Unit price |
currency_id | FK | yes | Currency |
min_quantity | decimal(15,3) | no | Minimum order quantity (MOQ) |
lead_time_days | integer | no | Supplier lead time in days |
valid_from | date | no | Price validity start |
valid_until | date | no | Price validity end |
notes | text | no | Notes |
is_active | boolean | auto | Default true |
last_updated_at | timestamp | auto | Last price update time |
Unique constraint: One price per (company_id, partner_id, product_id, product_variant_id, unit_id).
ER Diagram
API Endpoints
| Method | Endpoint | Permission | Description |
|---|---|---|---|
| GET | /api/purchases/supplier-prices | purchases.supplier-prices.view | List prices |
| POST | /api/purchases/supplier-prices | purchases.supplier-prices.create | Create price |
| GET | /api/purchases/supplier-prices/{id} | purchases.supplier-prices.view | Show price |
| PUT | /api/purchases/supplier-prices/{id} | purchases.supplier-prices.update | Update price |
| DELETE | /api/purchases/supplier-prices/{id} | purchases.supplier-prices.delete | Delete price |
| GET | /api/purchases/supplier-prices/compare | purchases.supplier-prices.view | Compare suppliers |
| POST | /api/purchases/supplier-prices/bulk-import | purchases.supplier-prices.create | CSV import |
Query Parameters (Index)
| Parameter | Type | Description |
|---|---|---|
partner_id | integer | Filter by supplier |
product_id | integer | Filter by product |
currency_id | integer | Filter by currency |
is_active | boolean | Filter by active status |
valid_only | boolean | Only currently valid prices (not expired, not future) |
search | string | Search supplier name/code or product name/SKU |
Create Request
bash
curl -X POST /api/purchases/supplier-prices \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"partner_id": 1,
"product_id": 5,
"unit_id": 2,
"price": 25.500,
"currency_id": 1,
"min_quantity": 10,
"lead_time_days": 7,
"valid_from": "2026-01-01",
"valid_until": "2026-12-31"
}'dart
final response = await dio.post(
'/api/purchases/supplier-prices',
data: {
'partner_id': 1,
'product_id': 5,
'unit_id': 2,
'price': 25.500,
'currency_id': 1,
'min_quantity': 10,
'lead_time_days': 7,
'valid_from': '2026-01-01',
'valid_until': '2026-12-31',
},
);Compare Request
bash
curl -X GET "/api/purchases/supplier-prices/compare?product_id=5" \
-H "Authorization: Bearer {token}"dart
final response = await dio.get(
'/api/purchases/supplier-prices/compare',
queryParameters: {'product_id': 5},
);Compare Response:
json
{
"data": [
{
"id": 3,
"partner_id": 2,
"partner_name": "Best Supplier Co.",
"price": "20.000",
"currency": { "id": 1, "code": "KWD" },
"min_quantity": "5.000",
"lead_time_days": 3,
"valid_until": "2026-12-31",
"is_best_price": true
},
{
"id": 1,
"partner_id": 1,
"partner_name": "Standard Supplier",
"price": "30.000",
"currency": { "id": 1, "code": "KWD" },
"min_quantity": null,
"lead_time_days": 7,
"valid_until": null,
"is_best_price": false
}
]
}CSV Bulk Import
Upload a CSV file with the following columns:
| Column | Required | Description |
|---|---|---|
supplier_code | yes | Business partner code |
product_sku | yes | Product SKU |
unit_code | yes | Unit symbol or name |
price | yes | Unit price |
currency_code | yes | Currency code (e.g., KWD) |
min_quantity | no | Minimum order quantity |
lead_time | no | Lead time in days |
valid_from | no | Validity start date |
valid_until | no | Validity end date |
Existing entries (same supplier+product+unit) are updated (upsert).
Business Rules
- Feature Toggle: All CRUD endpoints return 403 if
purchases.enable_supplier_price_listsis disabled. Compare returns 403 ifpurchases.enable_supplier_comparisonis disabled. - Uniqueness: One price per supplier+product+variant+unit combination per company.
- Validity Scopes:
active()filtersis_active = true.valid()filters entries wherevalid_from <= todayandvalid_until >= today(nulls treated as open-ended). - Compare: Returns only active and valid prices, sorted by price ascending. First entry is flagged
is_best_price: true. - PO Auto-fill: When creating purchase orders, if
enable_supplier_price_listsis enabled and an item hasunit_cost = 0, the system auto-fillsunit_costfrom the supplier's active/valid price for that product+variant+unit. - CSV Import: Uses upsert logic -- existing entries are updated, new ones created. Rows with unresolvable supplier/product/unit/currency codes are skipped with error messages.