Skip to content

Delivery Notes (أذونات التسليم)

The Delivery Notes API manages the shipping and delivery lifecycle — creating delivery notes from confirmed orders, confirming them (with optional stock deduction), tracking shipments, and recording delivery.

Entity Attributes

SalesDeliveryNote (Header)

FieldTypeRequiredDescription
idintegerautoPrimary key
delivery_numberstringautoAuto-generated via SequenceService (e.g., DN-00001)
datedateyesDelivery note date
customer_idFKautoCopied from the source order
order_idFKyesSource confirmed order
invoice_idFKnoOptional linked invoice
warehouse_idFKyesSource warehouse for stock deduction
branch_idFKautoCopied from the source order
statusenumautodraft, confirmed, shipped, delivered, cancelled
shipping_addresstextnoDelivery address
carrier_namestringnoShipping carrier (e.g., DHL, FedEx)
tracking_numberstringnoShipment tracking number
shipping_methodstringnoShipping method (Express, Standard, etc.)
shipping_costdecimal(15,3)noShipping cost
estimated_deliverydatenoEstimated delivery date
shipped_atdatetimeautoWhen the shipment was dispatched
delivered_atdatetimeautoWhen delivery was completed
received_bystringnoName of the person who received delivery
notes / notes_artextnoNotes in English / Arabic
confirmed_byFKautoUser who confirmed
confirmed_atdatetimeautoConfirmation timestamp
cancelled_byFKautoUser who cancelled
cancelled_atdatetimeautoCancellation timestamp
cancellation_reasontextnoReason for cancellation

SalesDeliveryNoteItem (Line Item)

FieldTypeRequiredDescription
idintegerautoPrimary key
order_item_idFKyesSource order line item
product_idFKautoCopied from order item
product_variant_idFKautoCopied from order item
unit_idFKautoCopied from order item
quantitydecimal(15,3)yesDelivery quantity (max = remaining delivery qty)
batch_numberstringnoBatch/lot number
serial_numbersJSONnoArray of serial numbers
notestextnoLine item notes
sort_orderintegernoDisplay order

ER Diagram

Delivery Note Lifecycle

Confirmation Flow (Critical)

When a delivery note is confirmed (POST /api/sales/delivery-notes/{id}/confirm), the following happens atomically:

Step 1: Stock Deduction (conditional)

If sales.stock_deduction_point = 'delivery':

  • For each line item with product.track_inventory = true
  • Decreases stock via StockService::decreaseStock() with movement type Issue
  • Records inventory movement for audit trail

If sales.stock_deduction_point = 'invoice', stock is not deducted on delivery confirmation — it happens when the invoice is posted instead.

Step 2: Update Order Delivery Quantities

  • For each line item linked to an order item:
    • Increments order_item.delivered_quantity by the delivery quantity
  • Calls order.recalculateDeliveryStatus() to update the order's delivery_status (pending → partial → complete)

Cancellation Flow

When a confirmed/shipped delivery note is cancelled:

  1. Stock restored — if sales.stock_deduction_point = 'delivery', stock is increased back via StockService::increaseStock()
  2. Order quantities reversed — each order item's delivered_quantity is decremented
  3. Delivery status recalculated — order's delivery_status is recalculated

Partial Delivery Support

Multiple delivery notes can be created for a single order. The system tracks:

remaining_delivery_qty = order_item.quantity - order_item.delivered_quantity

Each new delivery note validates that the requested quantity does not exceed the remaining deliverable amount.

API Endpoints

MethodEndpointDescriptionPermission
GET/api/sales/delivery-notesList delivery notes (paginated)sales.delivery_notes.view
POST/api/sales/delivery-notesCreate a delivery note from ordersales.delivery_notes.create
GET/api/sales/delivery-notes/{id}Get delivery note detailssales.delivery_notes.view
PUT/api/sales/delivery-notes/{id}Update a draft delivery notesales.delivery_notes.update
DELETE/api/sales/delivery-notes/{id}Delete a draft delivery notesales.delivery_notes.delete
POST/api/sales/delivery-notes/{id}/confirmConfirm delivery notesales.delivery_notes.confirm
POST/api/sales/delivery-notes/{id}/shipMark as shippedsales.delivery_notes.ship
POST/api/sales/delivery-notes/{id}/deliverMark as deliveredsales.delivery_notes.deliver
POST/api/sales/delivery-notes/{id}/cancelCancel delivery notesales.delivery_notes.cancel
POST/api/sales/orders/{id}/create-delivery-noteAuto-create DN from ordersales.delivery_notes.create

Query Parameters (List)

ParameterTypeDescription
statusstringFilter by delivery note status
customer_idintegerFilter by customer
order_idintegerFilter by source order
warehouse_idintegerFilter by warehouse
branch_idintegerFilter by branch
date_fromdateDelivery notes from this date
date_todateDelivery notes up to this date
searchstringSearch in delivery_number, tracking_number, carrier_name

Request / Response Examples

Create Delivery Note

bash
curl -X POST /api/sales/delivery-notes \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": 1,
    "warehouse_id": 1,
    "date": "2026-02-24",
    "shipping_address": "123 Main St, Kuwait City",
    "items": [
      {
        "order_item_id": 1,
        "quantity": 5,
        "batch_number": "LOT-2026-001"
      }
    ]
  }'
dart
final response = await dio.post('/api/sales/delivery-notes', data: {
  'order_id': 1,
  'warehouse_id': 1,
  'date': '2026-02-24',
  'shipping_address': '123 Main St, Kuwait City',
  'items': [
    {
      'order_item_id': 1,
      'quantity': 5,
      'batch_number': 'LOT-2026-001',
    }
  ],
});

Create From Order (auto-populate)

bash
curl -X POST /api/sales/orders/1/create-delivery-note \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"warehouse_id": 1}'
dart
final response = await dio.post('/api/sales/orders/1/create-delivery-note', data: {
  'warehouse_id': 1,
});

Ship Delivery Note

bash
curl -X POST /api/sales/delivery-notes/1/ship \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "carrier_name": "DHL Express",
    "tracking_number": "TRK-12345678",
    "shipping_method": "Express",
    "shipping_cost": 15.500,
    "estimated_delivery": "2026-02-28"
  }'
dart
final response = await dio.post('/api/sales/delivery-notes/1/ship', data: {
  'carrier_name': 'DHL Express',
  'tracking_number': 'TRK-12345678',
  'shipping_method': 'Express',
  'shipping_cost': 15.500,
  'estimated_delivery': '2026-02-28',
});

Deliver

bash
curl -X POST /api/sales/delivery-notes/1/deliver \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"received_by": "Ahmed Hassan"}'
dart
final response = await dio.post('/api/sales/delivery-notes/1/deliver', data: {
  'received_by': 'Ahmed Hassan',
});

Cancel Delivery Note

bash
curl -X POST /api/sales/delivery-notes/1/cancel \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"cancellation_reason": "Customer changed delivery address"}'
dart
final response = await dio.post('/api/sales/delivery-notes/1/cancel', data: {
  'cancellation_reason': 'Customer changed delivery address',
});

Business Rules

  1. Delivery notes can only be created from confirmed orders — draft or cancelled orders are rejected
  2. Delivery quantity cannot exceed remaining deliverable amount — tracked across all non-cancelled delivery notes for the same order item
  3. Product and unit are copied from the order item — cannot be manually adjusted
  4. Only draft delivery notes can be edited or deleted
  5. Stock deduction depends on sales.stock_deduction_point setting — either on delivery confirmation or invoice posting
  6. Cancellation restores everything — if confirmed, stock is restored and order quantities are reversed
  7. Customer and branch are inherited from the source order
  8. Multiple delivery notes per order — supports partial deliveries

Moon ERP API Documentation