Personel Bildirimi (notify.staff) ⏳ Yakında

Eklentiniz, tenant'ın bağladığı bildirim sağlayıcısı eklenti (push/Telegram/dahili ekran) üzerinden PERSONELE bildirim gönderebilir — sağlayıcının kimliğini bilmeden. messaging.send ile aynı eklentiler-arası capability altyapısını kullanır; fark yalnızca hedef ({role|userId}, müşteri değil) ve payload (title/body).

1 · Model

Tüketici bildirim ister, sağlayıcıyı tenant seçer (Android chooser modeli). Hedef { role | userId } bir personel referansıdır (PII değil — opak); sağlayıcı bunu kendi users:read'iyle kullanıcıya çözer. Scope'lar: capability:notify.staff:consume (tüketici) · capability:notify.staff:provide (sağlayıcı).

Ortak model + sağlayıcı olma rehberi (dedupe, imza, async durum): Yetenekler — Genel Bakış. notify.staff, messaging.send ve invoice.issue ile aynı platform altyapısını kullanır (sağlayıcı bağlama, idempotency, imzalı relay, breaker, kota, kill-switch, per-tüketici blok). Manifest'te provides:[{capability:"notify.staff"}] (sağlayıcı) veya consumes:[{capability:"notify.staff"}] (tüketici) beyan edin; türetilmiş scope otomatik eklenir.

2 · Tüketici — bildirim gönder

client.capabilities.invoke('notify.staff', …)
import { RestomenumClient } from '@restomenum/plugin-sdk';   // v1.10.0+

// TÜKETİCİ (scope: capability:notify.staff:consume) — jenerik capability yolu.
// Sonuç tipi (NotifyStaffResult) capability id'den OTOMATİK türer; elle <> tip argümanı GEREKMEZ.
const r = await client.capabilities.invoke('notify.staff', {
  to: { role: 'manager' },              // { role: 'manager'|'staff' } VEYA { userId } (en az biri)
  title: 'Stok uyarısı',
  body: 'Domates kritik seviyede (2 kg).',
  idempotencyKey: `stock-alert-${date}`,  // ZORUNLU — çift bildirim koruması
});
// r = { requestId, status: 'accepted'|'sent'|'failed', providerMessageId?, idempotentReplay? }
// Sağlayıcı bağlı değilse ApiError('plugin.notify.noProvider', 424) → özelliği gizle.

3 · Sağlayıcı — bildirimleri işle

verifyAndParseCapability<NotifyStaffPayload>
import { verifyAndParseCapability, capabilityResponse } from '@restomenum/plugin-sdk';
import type { NotifyStaffPayload } from '@restomenum/plugin-sdk';

// SAĞLAYICI (scope: capability:notify.staff:provide) — actionUrl'e gelen imzalı type:"capability" POST:
const req = await verifyAndParseCapability<NotifyStaffPayload>(rawBody, headers['x-restomenum-signature'], {
  getSecret: (tenantId) => installStore.find(tenantId)?.webhookSecret,   // webhook ile AYNI imza şeması
});
if (!req) return res.status(401).json({ error: 'invalid_signature' });
// ZORUNLU dedupe: aynı req.requestId tekrar gelirse YENİDEN GÖNDERME (önceki yanıtı dön).

// Hedefi ÇÖZ: { role|userId } — personel listesini KENDİ users:read'inle çek (rol→kullanıcı):
const { to, title, body } = req.payload;
// const users = await client.users.get();  // {role:'manager'} → authority filtrele; {userId} → doğrudan
// … push/Telegram/dahili ekran ile personele ilet …
res.json(capabilityResponse('accepted', { providerMessageId }));
title/body serbest metindir — gereksiz kişisel/müşteri verisi koymayın; UI'da text olarak basın (HTML/XSS yok). Hedefi çözmek için users:read+ (personel adı için) PII consent gerekir; userId/role'ün kendisi PII değildir.

4 · Hata kodları

KodHTTPAnlamı
plugin.notify.noProvider424Tenant bildirim sağlayıcısı bağlamamış — önkoşul eksik. Özelliği gizle; retry etme.
plugin.notify.providerUnavailable503Sağlayıcı inaktif/askıda/breaker-open/ulaşılamaz.
plugin.notify.timeout504Sağlayıcı 10 sn içinde yanıtlamadı. AYNI idempotencyKey ile retry.
plugin.notify.invalidTarget400to yalnız {role: manager|staff, userId} alır; ham personel PII (name/email) yasak.
plugin.notify.idempotencyKeyRequired400idempotencyKey eksik/geçersiz (zorunlu, ≤64).
plugin.notify.titleTooLong400title 200 karakteri aşıyor.
plugin.notify.consumerBlocked403Tenant, eklentinizin bildirim göndermesini engellemiş.
plugin.notify.suspended403Eklentiniz kill-switch ile askıya alınmış — bildirim gönderemez.
plugin.notify.duplicateInProgress409Aynı idempotencyKey eşzamanlı işleniyor.
plugin.notify.idempotencyKeyReused409Aynı key FARKLI içerikle kullanıldı.
plugin.notify.selfTarget409Tüketici == bağlı sağlayıcı (kendine yönlendirme; binding çakışması).
plugin.notify.providerChanged409Belirsiz sonuçtan (timeout) sonra tenant sağlayıcıyı değiştirdi — çift bildirime karşı bu key ile retry engellendi.
plugin.capability.notFound404Bilinmeyen capability (jenerik /capabilities/{cap}/* ucunda).

notify.staff fire-and-forget'tir — messaging'in aksine asenkron teslim durumu (DLR) yoktur; accepted sağlayıcının kuyruğa aldığını gösterir.