Files
bakery-ia/docs/03-features/notifications/whatsapp/shared-account-guide.md
Urtzi Alfaro 3c3d3ce042 Fix Purchase Order modal and reorganize documentation
Frontend Changes:
- Fix runtime error: Remove undefined handleModify reference from ActionQueueCard in DashboardPage
- Migrate PurchaseOrderDetailsModal to use correct PurchaseOrderItem type from purchase_orders service
- Fix item display: Parse unit_price as string (Decimal) instead of number
- Use correct field names: item_notes instead of notes
- Remove deprecated PurchaseOrder types from suppliers.ts to prevent type conflicts
- Update CreatePurchaseOrderModal to use unified types
- Clean up API exports: Remove old PO hooks re-exported from suppliers
- Add comprehensive translations for PO modal (en, es, eu)

Documentation Reorganization:
- Move WhatsApp implementation docs to docs/03-features/notifications/whatsapp/
- Move forecast validation docs to docs/03-features/forecasting/
- Move specification docs to docs/03-features/specifications/
- Move deployment docs (Colima, K8s, VPS sizing) to docs/05-deployment/
- Archive completed implementation summaries to docs/archive/implementation-summaries/
- Delete obsolete FRONTEND_CHANGES_NEEDED.md
- Standardize filenames to lowercase with hyphens

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 11:59:23 +01:00

751 lines
23 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WhatsApp Shared Account Model - Implementation Guide
## Overview
This guide documents the **Shared WhatsApp Business Account** implementation for the bakery-ia pilot program. This model simplifies WhatsApp setup by using a single master WhatsApp Business Account with phone numbers assigned to each bakery tenant.
---
## Architecture
### Shared Account Model
```
┌─────────────────────────────────────────────┐
│ Master WhatsApp Business Account (WABA) │
│ - Centrally managed by platform admin │
│ - Single set of credentials │
│ - Multiple phone numbers (up to 120) │
└─────────────────────────────────────────────┘
┌─────────────┼─────────────┐
│ │ │
Phone #1 Phone #2 Phone #3
Bakery A Bakery B Bakery C
```
### Key Benefits
**Zero configuration for bakery users** - No Meta navigation required
**5-minute setup** - Admin assigns phone number via UI
**Lower support burden** - Centralized management
**Predictable costs** - One WABA subscription
**Perfect for pilot** - Quick deployment for 10 bakeries
---
## User Experience
### For Bakery Owners (Non-Technical Users)
**Before (Manual Setup):**
- Navigate Meta Business Suite ❌
- Create WhatsApp Business Account ❌
- Create message templates ❌
- Get credentials (3 different IDs) ❌
- Copy/paste into settings ❌
- **Time:** 1-2 hours, high error rate
**After (Shared Account):**
- Toggle WhatsApp ON ✓
- See assigned phone number ✓
- **Time:** 30 seconds, zero configuration
### For Platform Admin
**Admin Workflow:**
1. Access WhatsApp Admin page (`/app/admin/whatsapp`)
2. View list of tenants
3. Select tenant
4. Assign phone number from dropdown
5. Done!
---
## Technical Implementation
### Backend Changes
#### 1. Tenant Settings Model
**File:** `services/tenant/app/models/tenant_settings.py`
**Changed:**
```python
# OLD (Per-Tenant Credentials)
notification_settings = {
"whatsapp_enabled": False,
"whatsapp_phone_number_id": "",
"whatsapp_access_token": "", # REMOVED
"whatsapp_business_account_id": "", # REMOVED
"whatsapp_api_version": "v18.0", # REMOVED
"whatsapp_default_language": "es"
}
# NEW (Shared Account)
notification_settings = {
"whatsapp_enabled": False,
"whatsapp_phone_number_id": "", # Phone # from shared account
"whatsapp_display_phone_number": "", # Display format "+34 612 345 678"
"whatsapp_default_language": "es"
}
```
#### 2. WhatsApp Business Service
**File:** `services/notification/app/services/whatsapp_business_service.py`
**Changed `_get_whatsapp_credentials()` method:**
```python
async def _get_whatsapp_credentials(self, tenant_id: str) -> Dict[str, str]:
"""
Uses global master account credentials with tenant-specific phone number
"""
# Always use global master account
access_token = self.global_access_token
business_account_id = self.global_business_account_id
phone_number_id = self.global_phone_number_id # Default
# Fetch tenant's assigned phone number
if self.tenant_client:
notification_settings = await self.tenant_client.get_notification_settings(tenant_id)
if notification_settings and notification_settings.get('whatsapp_enabled'):
tenant_phone_id = notification_settings.get('whatsapp_phone_number_id', '')
if tenant_phone_id:
phone_number_id = tenant_phone_id # Use tenant's phone
return {
'access_token': access_token,
'phone_number_id': phone_number_id,
'business_account_id': business_account_id
}
```
**Key Change:** Always uses global credentials, but selects the phone number based on tenant assignment.
#### 3. Phone Number Management API
**New File:** `services/tenant/app/api/whatsapp_admin.py`
**Endpoints:**
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/v1/admin/whatsapp/phone-numbers` | List available phone numbers from master WABA |
| GET | `/api/v1/admin/whatsapp/tenants` | List all tenants with WhatsApp status |
| POST | `/api/v1/admin/whatsapp/tenants/{id}/assign-phone` | Assign phone to tenant |
| DELETE | `/api/v1/admin/whatsapp/tenants/{id}/unassign-phone` | Remove phone assignment |
**Example: Assign Phone Number**
```bash
curl -X POST http://localhost:8001/api/v1/admin/whatsapp/tenants/{tenant_id}/assign-phone \
-H "Content-Type: application/json" \
-d '{
"phone_number_id": "123456789012345",
"display_phone_number": "+34 612 345 678"
}'
```
**Response:**
```json
{
"success": true,
"message": "Phone number +34 612 345 678 assigned to tenant 'Panadería San Juan'",
"tenant_id": "uuid-here",
"phone_number_id": "123456789012345",
"display_phone_number": "+34 612 345 678"
}
```
### Frontend Changes
#### 1. Simplified Notification Settings Card
**File:** `frontend/src/pages/app/database/ajustes/cards/NotificationSettingsCard.tsx`
**Removed:**
- Access Token input field
- Business Account ID input field
- Phone Number ID input field
- API Version selector
- Setup wizard instructions
**Added:**
- Display-only phone number (green badge if configured)
- "Contact support" message if not configured
- Language selector only
**UI Before/After:**
```
BEFORE:
┌────────────────────────────────────────┐
│ WhatsApp Business API Configuration │
│ │
│ Phone Number ID: [____________] │
│ Access Token: [____________] │
│ Business Acct: [____________] │
│ API Version: [v18.0 ▼] │
│ Language: [Español ▼] │
│ │
Setup Instructions: │
│ 1. Create WhatsApp Business... │
│ 2. Create templates... │
│ 3. Get credentials... │
└────────────────────────────────────────┘
AFTER:
┌────────────────────────────────────────┐
│ WhatsApp Configuration │
│ │
│ ✅ WhatsApp Configured │
│ Phone: +34 612 345 678 │
│ │
│ Language: [Español ▼] │
│ │
WhatsApp Notifications Included │
│ WhatsApp messaging is included │
│ in your subscription. │
└────────────────────────────────────────┘
```
#### 2. Admin Interface
**New File:** `frontend/src/pages/app/admin/WhatsAppAdminPage.tsx`
**Features:**
- Lists all available phone numbers from master WABA
- Shows phone number quality rating (GREEN/YELLOW/RED)
- Lists all tenants with WhatsApp status
- Dropdown to assign phone numbers
- One-click unassign button
- Real-time status updates
**Screenshot Mockup:**
```
┌──────────────────────────────────────────────────────────────┐
│ WhatsApp Admin Management │
│ Assign WhatsApp phone numbers to bakery tenants │
├──────────────────────────────────────────────────────────────┤
│ 📞 Available Phone Numbers (3) │
├──────────────────────────────────────────────────────────────┤
│ +34 612 345 678 Bakery Platform [GREEN] │
│ +34 612 345 679 Bakery Support [GREEN] │
│ +34 612 345 680 Bakery Notifications [YELLOW] │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ 👥 Bakery Tenants (10) │
├──────────────────────────────────────────────────────────────┤
│ Panadería San Juan ✅ Active │
│ Phone: +34 612 345 678 [Unassign] │
├──────────────────────────────────────────────────────────────┤
│ Panadería Goiko ⚠️ Not Configured │
│ No phone number assigned [Assign phone number... ▼] │
└──────────────────────────────────────────────────────────────┘
```
---
## Setup Instructions
### Step 1: Create Master WhatsApp Business Account (One-Time)
**Prerequisites:**
- Meta/Facebook Business account
- Verified business
- Phone number(s) to register
**Instructions:**
1. **Create WhatsApp Business Account**
- Go to [Meta Business Suite](https://business.facebook.com)
- Add WhatsApp product
- Complete business verification (1-3 days)
2. **Add Phone Numbers**
- Add at least 10 phone numbers (one per pilot bakery)
- Verify each phone number
- Note: You can request up to 120 phone numbers per WABA
3. **Create Message Templates**
- Create `po_notification` template:
```
Category: UTILITY
Language: Spanish (es)
Message: "Hola {{1}}, has recibido una nueva orden de compra {{2}} por un total de {{3}}."
```
- Submit for approval (15 min - 24 hours)
4. **Get Master Credentials**
- Business Account ID: From WhatsApp Manager settings
- Access Token: Create System User or use temporary token
- Phone Number ID: Listed in phone numbers section
### Step 2: Configure Environment Variables
**File:** `services/notification/.env`
```bash
# Master WhatsApp Business Account Credentials
WHATSAPP_BUSINESS_ACCOUNT_ID=987654321098765
WHATSAPP_ACCESS_TOKEN=EAAxxxxxxxxxxxxxxxxxxxxxxxxxx
WHATSAPP_PHONE_NUMBER_ID=123456789012345 # Default/fallback phone
WHATSAPP_API_VERSION=v18.0
ENABLE_WHATSAPP_NOTIFICATIONS=true
WHATSAPP_WEBHOOK_VERIFY_TOKEN=random-secret-token-here
```
**Security Notes:**
- Store `WHATSAPP_ACCESS_TOKEN` securely (use secrets manager in production)
- Rotate token every 60 days
- Use System User token (not temporary token) for production
### Step 3: Assign Phone Numbers to Tenants
**Via Admin UI:**
1. Access admin page: `http://localhost:5173/app/admin/whatsapp`
2. See list of tenants
3. For each tenant:
- Select phone number from dropdown
- Click assign
- Verify green checkmark appears
**Via API:**
```bash
# Assign phone to tenant
curl -X POST http://localhost:8001/api/v1/admin/whatsapp/tenants/{tenant_id}/assign-phone \
-H "Content-Type: application/json" \
-d '{
"phone_number_id": "123456789012345",
"display_phone_number": "+34 612 345 678"
}'
```
### Step 4: Test Notifications
**Enable WhatsApp for a Tenant:**
1. Login as bakery owner
2. Go to Settings → Notifications
3. Toggle WhatsApp ON
4. Verify phone number is displayed
5. Save settings
**Trigger Test Notification:**
```bash
# Create a purchase order (will trigger WhatsApp notification)
curl -X POST http://localhost:8003/api/v1/orders/purchase-orders \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: {tenant_id}" \
-d '{
"supplier_id": "uuid",
"items": [...]
}'
```
**Verify:**
- Check notification service logs: `docker logs -f notification-service`
- Supplier should receive WhatsApp message from assigned phone number
- Message status tracked in `whatsapp_messages` table
---
## Monitoring & Operations
### Check Phone Number Usage
```bash
# List all tenants with assigned phone numbers
curl http://localhost:8001/api/v1/admin/whatsapp/tenants | jq
```
### View WhatsApp Message Logs
```sql
-- In notification database
SELECT
tenant_id,
recipient_phone,
template_name,
status,
created_at,
error_message
FROM whatsapp_messages
WHERE created_at > NOW() - INTERVAL '24 hours'
ORDER BY created_at DESC;
```
### Monitor Meta Rate Limits
WhatsApp Cloud API has the following limits:
| Metric | Limit |
|--------|-------|
| Messages per second | 80 |
| Messages per day (verified) | 100,000 |
| Messages per day (unverified) | 1,000 |
| Conversations per 24h | Unlimited (pay per conversation) |
**Check Quality Rating:**
```bash
curl -X GET "https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}" \
-H "Authorization: Bearer {ACCESS_TOKEN}" \
| jq '.quality_rating'
```
**Quality Ratings:**
- **GREEN** - No issues, full limits
- **YELLOW** - Warning, limits may be reduced
- **RED** - Quality issues, severely restricted
---
## Migration from Per-Tenant to Shared Account
If you have existing tenants with their own credentials:
### Automatic Migration Script
```python
# services/tenant/scripts/migrate_to_shared_account.py
"""
Migrate existing tenant WhatsApp credentials to shared account model
"""
import asyncio
from sqlalchemy import select
from app.core.database import database_manager
from app.models.tenant_settings import TenantSettings
async def migrate():
async with database_manager.get_session() as session:
# Get all tenant settings
result = await session.execute(select(TenantSettings))
all_settings = result.scalars().all()
for settings in all_settings:
notification_settings = settings.notification_settings
# If tenant has old credentials, preserve phone number ID
if notification_settings.get('whatsapp_access_token'):
phone_id = notification_settings.get('whatsapp_phone_number_id', '')
# Update to new schema
notification_settings['whatsapp_phone_number_id'] = phone_id
notification_settings['whatsapp_display_phone_number'] = '' # Admin will set
# Remove old fields
notification_settings.pop('whatsapp_access_token', None)
notification_settings.pop('whatsapp_business_account_id', None)
notification_settings.pop('whatsapp_api_version', None)
settings.notification_settings = notification_settings
print(f"Migrated tenant: {settings.tenant_id}")
await session.commit()
print("Migration complete!")
if __name__ == "__main__":
asyncio.run(migrate())
```
---
## Troubleshooting
### Issue: Tenant doesn't receive WhatsApp messages
**Checklist:**
1. ✅ WhatsApp enabled in tenant settings?
2. ✅ Phone number assigned to tenant?
3. ✅ Master credentials configured in environment?
4. ✅ Template approved by Meta?
5. ✅ Recipient phone number in E.164 format (+34612345678)?
**Check Logs:**
```bash
# Notification service logs
docker logs -f notification-service | grep whatsapp
# Look for:
# - "Using tenant-assigned WhatsApp phone number"
# - "WhatsApp template message sent successfully"
# - Any error messages
```
### Issue: Phone number assignment fails
**Error:** "Phone number already assigned to another tenant"
**Solution:**
```bash
# Find which tenant has the phone number
curl http://localhost:8001/api/v1/admin/whatsapp/tenants | \
jq '.[] | select(.phone_number_id == "123456789012345")'
# Unassign from old tenant first
curl -X DELETE http://localhost:8001/api/v1/admin/whatsapp/tenants/{old_tenant_id}/unassign-phone
```
### Issue: "WhatsApp master account not configured"
**Solution:**
Ensure environment variables are set:
```bash
# Check if variables exist
docker exec notification-service env | grep WHATSAPP
# Should show:
# WHATSAPP_BUSINESS_ACCOUNT_ID=...
# WHATSAPP_ACCESS_TOKEN=...
# WHATSAPP_PHONE_NUMBER_ID=...
```
### Issue: Template not found
**Error:** "Template po_notification not found"
**Solution:**
1. Create template in Meta Business Manager
2. Wait for approval (check status):
```bash
curl -X GET "https://graph.facebook.com/v18.0/{WABA_ID}/message_templates" \
-H "Authorization: Bearer {TOKEN}" \
| jq '.data[] | select(.name == "po_notification")'
```
3. Ensure template language matches tenant's `whatsapp_default_language`
---
## Cost Analysis
### WhatsApp Business API Pricing (as of 2024)
**Meta Pricing:**
- **Business-initiated conversations:** €0.0319 - €0.0699 per conversation (Spain)
- **User-initiated conversations:** Free (24-hour window)
- **Conversation window:** 24 hours
**Monthly Cost Estimate (10 Bakeries):**
- Assume 5 PO notifications per bakery per day
- 5 × 10 bakeries × 30 days = 1,500 messages/month
- Cost: 1,500 × €0.05 = **€75/month**
**Shared Account vs. Individual Accounts:**
| Model | Setup Time | Monthly Cost | Support Burden |
|-------|------------|--------------|----------------|
| Individual Accounts | 1-2 hrs/bakery | €75 total | High |
| Shared Account | 5 min/bakery | €75 total | Low |
**Savings:** Time savings = 10 hrs × €50/hr = **€500 in setup cost**
---
## Future Enhancements
### Option 1: Template Management API
Automate template creation for new tenants:
```python
async def create_po_template(waba_id: str, access_token: str):
"""Programmatically create PO notification template"""
url = f"https://graph.facebook.com/v18.0/{waba_id}/message_templates"
payload = {
"name": "po_notification",
"language": "es",
"category": "UTILITY",
"components": [{
"type": "BODY",
"text": "Hola {{1}}, has recibido una nueva orden de compra {{2}} por un total de {{3}}."
}]
}
response = await httpx.post(url, headers={"Authorization": f"Bearer {access_token}"}, json=payload)
return response.json()
```
### Option 2: WhatsApp Embedded Signup
For scaling beyond pilot:
- Apply for Meta Business Solution Provider program
- Implement OAuth-style signup flow
- Users click "Connect WhatsApp" → auto-configured
- Estimated implementation: 2-4 weeks
### Option 3: Tiered Pricing
```
Basic Tier (Free):
- Email notifications only
Standard Tier (€29/month):
- Shared WhatsApp account
- Pre-approved templates
- Up to 500 messages/month
Enterprise Tier (€99/month):
- Own WhatsApp Business Account
- Custom templates
- Unlimited messages
- White-label phone number
```
---
## Security & Compliance
### Data Privacy
**GDPR Compliance:**
- WhatsApp messages contain supplier contact info (phone numbers)
- Ensure GDPR consent for sending notifications
- Provide opt-out mechanism
- Data retention: Messages stored for 90 days (configurable)
**Encryption:**
- WhatsApp messages: End-to-end encrypted by Meta
- Access tokens: Stored in environment variables (use secrets manager in production)
- Database: Encrypt `notification_settings` JSON column
### Access Control
**Admin Access:**
- Only platform admins can assign/unassign phone numbers
- Implement role-based access control (RBAC)
- Audit log for phone number assignments
```python
# Example: Add admin check
@router.post("/admin/whatsapp/tenants/{tenant_id}/assign-phone")
async def assign_phone(tenant_id: UUID, current_user = Depends(require_admin_role)):
# Only admins can access
pass
```
---
## Support & Contacts
**Meta Support:**
- WhatsApp Business API Support: https://business.whatsapp.com/support
- Developer Docs: https://developers.facebook.com/docs/whatsapp
**Platform Admin:**
- Email: admin@bakery-platform.com
- Phone number assignment requests
- Template approval assistance
**Bakery Owner Help:**
- Settings → Notifications → Toggle WhatsApp ON
- If phone number not showing: Contact support
- Language preferences can be changed anytime
---
## Appendix
### A. Database Schema Changes
**Migration Script:**
```sql
-- Add new field, remove old fields
-- services/tenant/migrations/versions/00002_shared_whatsapp_account.py
ALTER TABLE tenant_settings
-- The notification_settings JSONB column now has:
-- + whatsapp_display_phone_number (new)
-- - whatsapp_access_token (removed)
-- - whatsapp_business_account_id (removed)
-- - whatsapp_api_version (removed)
;
-- No ALTER TABLE needed (JSONB is schema-less)
-- Migration handled by application code
```
### B. API Reference
**Phone Number Info Schema:**
```typescript
interface WhatsAppPhoneNumberInfo {
id: string; // Meta Phone Number ID
display_phone_number: string; // E.164 format: +34612345678
verified_name: string; // Business name verified by Meta
quality_rating: string; // GREEN, YELLOW, RED
}
```
**Tenant WhatsApp Status Schema:**
```typescript
interface TenantWhatsAppStatus {
tenant_id: string;
tenant_name: string;
whatsapp_enabled: boolean;
phone_number_id: string | null;
display_phone_number: string | null;
}
```
### C. Environment Variables Reference
```bash
# Notification Service (services/notification/.env)
WHATSAPP_BUSINESS_ACCOUNT_ID= # Meta WABA ID
WHATSAPP_ACCESS_TOKEN= # Meta System User Token
WHATSAPP_PHONE_NUMBER_ID= # Default phone (fallback)
WHATSAPP_API_VERSION=v18.0 # Meta API version
ENABLE_WHATSAPP_NOTIFICATIONS=true
WHATSAPP_WEBHOOK_VERIFY_TOKEN= # Random secret for webhook verification
```
### D. Useful Commands
```bash
# View all available phone numbers
curl http://localhost:8001/api/v1/admin/whatsapp/phone-numbers | jq
# View tenant WhatsApp status
curl http://localhost:8001/api/v1/admin/whatsapp/tenants | jq
# Assign phone to tenant
curl -X POST http://localhost:8001/api/v1/admin/whatsapp/tenants/{id}/assign-phone \
-H "Content-Type: application/json" \
-d '{"phone_number_id": "XXX", "display_phone_number": "+34 612 345 678"}'
# Unassign phone from tenant
curl -X DELETE http://localhost:8001/api/v1/admin/whatsapp/tenants/{id}/unassign-phone
# Test WhatsApp connectivity
curl -X GET "https://graph.facebook.com/v18.0/{PHONE_ID}" \
-H "Authorization: Bearer {TOKEN}"
# Check message template status
curl "https://graph.facebook.com/v18.0/{WABA_ID}/message_templates?fields=name,status,language" \
-H "Authorization: Bearer {TOKEN}" | jq
```
---
**Document Version:** 1.0
**Last Updated:** 2025-01-17
**Author:** Platform Engineering Team
**Status:** Production Ready for Pilot