How to Send Slack Notifications for Material Issue, Material Transfer, and Purchase Receipt in ERPNext v15 (With Error Fix)

If you’re using ERPNext v15 and want Slack notifications when stock movements happen, this guide walks you through:

  • Sending Slack alerts for:
    • Material Issue
    • Material Transfer
    • Purchase Receipt
  • Avoiding the common Jinja error: TypeError: 'builtin_function_or_method' object is not iterable

This method uses ERPNext’s built-in Webhook DocType and Slack Incoming Webhooks.


Step 1 — Create a Slack Incoming Webhook

  1. Go to https://api.slack.com/apps
  2. Create a new app (or use an existing one).
  3. Enable Incoming Webhooks.
  4. Click Add New Webhook to Workspace.
  5. Choose your channel.
  6. Copy the generated Webhook URL.

Important:
Treat this URL like a password. Do not publish or share it publicly.


Step 2 — Create Webhook for Stock Entry (Material Issue + Transfer)

In ERPNext:

Go to:

Search → Webhook → New

Configure:

Document Type:

Stock Entry

Doc Event:

on_submit

Request Method:

POST

Request URL:
Paste your Slack webhook URL

Enabled:
Checked


Add Condition (Important)

Only trigger for Material Issue and Material Transfer:

doc.stock_entry_type in ("Material Issue", "Material Transfer")

Why this matters:
Stock Entry is one DocType, but it has multiple types. This prevents Slack spam from other stock movements.


Add Header

Add one row:

KeyValue
Content-Typeapplication/json

Step 3 — Correct JSON Payload (Avoid the Common Error)

Many users encounter this error:

TypeError: 'builtin_function_or_method' object is not iterable

This happens because in Webhooks, doc is treated as a dictionary.
Using doc.items conflicts with Python’s dict.items() method.

❌ Wrong (causes error)

{% for i in doc.items %}

✅ Correct

{% for i in doc.get('items', []) %}

Use This Working JSON Payload (Stock Entry)

{
  "text": "📦 *Stock Entry Submitted*\n*Type:* {{ doc.get('stock_entry_type', '—') }}\n*ID:* {{ doc.get('name', '—') }}\n*Company:* {{ doc.get('company', '—') }}\n*Posting:* {{ doc.get('posting_date', '—') }} {{ doc.get('posting_time', '') }}\n*From WH:* {{ doc.get('from_warehouse') or '—' }}\n*To WH:* {{ doc.get('to_warehouse') or '—' }}\n\n*Items:*\n{% for i in doc.get('items', []) %}• {{ i.get('item_code', '—') }} — Qty: {{ i.get('qty', 0) }} {{ i.get('uom', '') }} ({{ i.get('s_warehouse') or i.get('t_warehouse') or '—' }})\n{% endfor %}"
}

Step 4 — Create Separate Webhook for Purchase Receipt

Important:
Purchase Receipt is NOT a Stock Entry Type.
It is a separate DocType.

So you must create a second webhook.


Create New Webhook

Document Type:

Purchase Receipt

Doc Event:

on_submit

Request Method:

POST

Request URL:
Slack webhook URL

Header:

Content-Type: application/json

No condition required unless you want to filter by supplier or company.


Working JSON Payload (Purchase Receipt)

{
  "text": "🧾 *Purchase Receipt Submitted*\n*ID:* {{ doc.get('name', '—') }}\n*Supplier:* {{ doc.get('supplier', '—') }}\n*Company:* {{ doc.get('company', '—') }}\n*Posting:* {{ doc.get('posting_date', '—') }} {{ doc.get('posting_time', '') }}\n\n*Items Received:*\n{% for i in doc.get('items', []) %}• {{ i.get('item_code', '—') }} — Qty: {{ i.get('qty', 0) }} {{ i.get('uom', '') }} ({{ i.get('warehouse') or '—' }})\n{% endfor %}"
}

Step 5 — Testing

  1. Create a Material Transfer.
  2. Submit it.
  3. Check Slack channel.

Then test:

  • Material Issue
  • Purchase Receipt

If nothing appears:

Check Logs (Docker)

docker compose logs -f backend

Or inside container:

bench --site yoursite tail

Optional Enhancement — Add Clickable ERP Link

If your ERP URL is:

https://erp.yourdomain.com

You can add this inside your Slack message:

https://erp.yourdomain.com/app/stock-entry/{{ doc.get('name') }}

Slack will auto-link it.


Why This Fix Works

In Webhooks:

  • doc behaves as a Python dictionary.
  • doc.items conflicts with dictionary method .items().
  • Using doc.get('items', []) safely retrieves the child table list.

This prevents:

TypeError: 'builtin_function_or_method' object is not iterable

Final Result

You now have Slack alerts for:

  • Material Issue
  • Material Transfer
  • Purchase Receipt

Triggered automatically on submit, with item-level detail

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Secret Link