319 lines
8.0 KiB
Markdown
319 lines
8.0 KiB
Markdown
|
|
# Quick Start Guide
|
|||
|
|
## Gadai Mulia Collection Broadcast Integration
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ⚡ 5-Minute Setup
|
|||
|
|
|
|||
|
|
### Prerequisites
|
|||
|
|
- PostgreSQL database access
|
|||
|
|
- Sopiga API credentials (token)
|
|||
|
|
- Go 1.16+ (for worker deployment)
|
|||
|
|
|
|||
|
|
### Step 1: Create Database Schema (2 minutes)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Run SQL schema
|
|||
|
|
psql -U postgres -h localhost -d gadai_mulia < gadai_collection_broadcast_simplified_schema.sql
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Verify schema created:
|
|||
|
|
```sql
|
|||
|
|
\dt collection_broadcasts.*
|
|||
|
|
-- Should show: sopiga_template_config, template_variable_mapping, sopiga_collar_config, broadcast_staging, etc
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 2: Setup Sopiga Template (2 minutes)
|
|||
|
|
|
|||
|
|
**In Sopiga UI or API:**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Create template
|
|||
|
|
curl -X POST https://omnix.promas.site/api/client/template \
|
|||
|
|
-H "Authorization: Bearer YOUR_TOKEN" \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{
|
|||
|
|
"template_name": "Collection Invoice",
|
|||
|
|
"channel": "waba",
|
|||
|
|
"variables": [
|
|||
|
|
{"name": "Nama", "type": "string"},
|
|||
|
|
{"name": "TotalTagihan", "type": "integer"},
|
|||
|
|
{"name": "TanggalJatuhTempo", "type": "date"}
|
|||
|
|
]
|
|||
|
|
}'
|
|||
|
|
# Response: {"data": {"id": 2}}
|
|||
|
|
|
|||
|
|
# Create collar
|
|||
|
|
curl -X POST https://omnix.promas.site/api/client/collar \
|
|||
|
|
-H "Authorization: Bearer YOUR_TOKEN" \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{
|
|||
|
|
"judul_broadcast": "Invoice May 2026",
|
|||
|
|
"template_id": 2
|
|||
|
|
}'
|
|||
|
|
# Response: {"data": {"id": 70}}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Note down:
|
|||
|
|
- `template_id = 2`
|
|||
|
|
- `collar_id = 70`
|
|||
|
|
|
|||
|
|
### Step 3: Register in Database (1 minute)
|
|||
|
|
|
|||
|
|
**Connect to PostgreSQL:**
|
|||
|
|
```bash
|
|||
|
|
psql -U postgres -h localhost -d gadai_mulia
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Insert template:**
|
|||
|
|
```sql
|
|||
|
|
INSERT INTO collection_broadcasts.sopiga_template_config
|
|||
|
|
(template_name, sopiga_template_id, channel, template_type, description)
|
|||
|
|
VALUES
|
|||
|
|
('Collection Invoice May 2026', 2, 'waba', 'utility', 'Invoice bulanan untuk collection');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Insert variable mappings (URUTAN PENTING!):**
|
|||
|
|
```sql
|
|||
|
|
INSERT INTO collection_broadcasts.template_variable_mapping
|
|||
|
|
(sopiga_template_id, variable_order, sopiga_variable_name, variable_type, db_field_source, is_required, example_value)
|
|||
|
|
VALUES
|
|||
|
|
(2, 1, 'Nama', 'string', 'nasabah_nama', TRUE, 'Budi Santoso'),
|
|||
|
|
(2, 2, 'TotalTagihan', 'integer', 'nominal_tagihan', TRUE, '1500000'),
|
|||
|
|
(2, 3, 'TanggalJatuhTempo', 'date', 'tanggal_tempo', TRUE, '2026-06-30');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Insert collar config:**
|
|||
|
|
```sql
|
|||
|
|
INSERT INTO collection_broadcasts.sopiga_collar_config
|
|||
|
|
(broadcast_name, sopiga_collar_id, sopiga_template_id, status, description)
|
|||
|
|
VALUES
|
|||
|
|
('Collection Invoices May 2026', 70, 2, 'open', 'Broadcast collar untuk collection invoice bulanan Mei 2026');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Verify setup:**
|
|||
|
|
```sql
|
|||
|
|
SELECT * FROM collection_broadcasts.v_template_variables_ordered
|
|||
|
|
WHERE sopiga_template_id = 2;
|
|||
|
|
-- Should show 3 rows with order 1, 2, 3
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 4: Deploy Worker (1 minute)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Build
|
|||
|
|
go build -o collection_broadcast_worker collection_broadcast_worker_simplified.go
|
|||
|
|
|
|||
|
|
# Run with environment variables
|
|||
|
|
export DB_HOST=localhost
|
|||
|
|
export DB_PORT=5432
|
|||
|
|
export DB_NAME=gadai_mulia
|
|||
|
|
export DB_USER=postgres
|
|||
|
|
export DB_PASSWORD=xxx
|
|||
|
|
export SOPIGA_BASE_URL=https://omnix.promas.site
|
|||
|
|
export SOPIGA_TOKEN=your_api_token
|
|||
|
|
|
|||
|
|
./collection_broadcast_worker
|
|||
|
|
# Output: Collection Broadcast Worker started
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🧪 Test It
|
|||
|
|
|
|||
|
|
### Insert Test Record
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- Insert to broadcast_staging
|
|||
|
|
INSERT INTO collection_broadcasts.broadcast_staging
|
|||
|
|
(sopiga_collar_id, sopiga_template_id, message_payload, status)
|
|||
|
|
VALUES
|
|||
|
|
(
|
|||
|
|
70,
|
|||
|
|
2,
|
|||
|
|
'{"nasabah_nama": "Budi Santoso", "nasabah_phone": "6281234567890", "nominal_tagihan": 1500000, "tanggal_tempo": "2026-06-30", "invoice_pdf_url": "https://storage.gadai.com/invoices/inv-001.pdf"}',
|
|||
|
|
'pending'
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
-- Check it's pending
|
|||
|
|
SELECT id, status, created_at FROM collection_broadcasts.broadcast_staging WHERE status='pending';
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Wait 30 seconds
|
|||
|
|
|
|||
|
|
Worker polls every 30 seconds. Check status:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
SELECT id, status, sopiga_recipient_detail_id, dispatched_at, error_message
|
|||
|
|
FROM collection_broadcasts.broadcast_staging
|
|||
|
|
WHERE id = 1;
|
|||
|
|
|
|||
|
|
-- Should show: status='dispatched', sopiga_recipient_detail_id=512
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Check WhatsApp Delivery
|
|||
|
|
|
|||
|
|
After 5 minutes, check sync job:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
SELECT id, status, last_status_from_sopiga, delivered_at
|
|||
|
|
FROM collection_broadcasts.broadcast_staging
|
|||
|
|
WHERE id = 1;
|
|||
|
|
|
|||
|
|
-- Should show: status='delivered', delivered_at=<timestamp>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 Monitor
|
|||
|
|
|
|||
|
|
### Check Delivery Rate
|
|||
|
|
```sql
|
|||
|
|
SELECT * FROM collection_broadcasts.v_delivery_rate_24h;
|
|||
|
|
-- delivery_rate_percent should be > 95%
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Check Failed Records
|
|||
|
|
```sql
|
|||
|
|
SELECT * FROM collection_broadcasts.v_failed_records_24h;
|
|||
|
|
-- Should be empty or < 5%
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Check Collar Summary
|
|||
|
|
```sql
|
|||
|
|
SELECT * FROM collection_broadcasts.v_collar_summary;
|
|||
|
|
-- See totals per collar, delivery rates
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ➕ Add New Template (5 minutes)
|
|||
|
|
|
|||
|
|
When Sopiga template changes (e.g., add `NoKontrak` field):
|
|||
|
|
|
|||
|
|
### 1. Create in Sopiga
|
|||
|
|
```bash
|
|||
|
|
# template_id = 3, collar_id = 71
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. Register in Database
|
|||
|
|
```sql
|
|||
|
|
-- Template
|
|||
|
|
INSERT INTO sopiga_template_config (template_name, sopiga_template_id, ...)
|
|||
|
|
VALUES ('Collection Invoice Extended', 3, ...);
|
|||
|
|
|
|||
|
|
-- Variables (URUTAN PENTING!)
|
|||
|
|
INSERT INTO template_variable_mapping
|
|||
|
|
(sopiga_template_id, variable_order, sopiga_variable_name, variable_type, db_field_source, is_required)
|
|||
|
|
VALUES
|
|||
|
|
(3, 1, 'Nama', 'string', 'nasabah_nama', TRUE),
|
|||
|
|
(3, 2, 'NoKontrak', 'string', 'contract_no', TRUE), ← NEW
|
|||
|
|
(3, 3, 'TotalTagihan', 'integer', 'nominal_tagihan', TRUE),
|
|||
|
|
(3, 4, 'TanggalJatuhTempo', 'date', 'tanggal_tempo', TRUE);
|
|||
|
|
|
|||
|
|
-- Collar
|
|||
|
|
INSERT INTO sopiga_collar_config (broadcast_name, sopiga_collar_id, sopiga_template_id, status)
|
|||
|
|
VALUES ('Collection Invoices June 2026', 71, 3, 'open');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. NO code redeploy needed ✅
|
|||
|
|
|
|||
|
|
Worker automatically picks up new template on next poll.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🚀 Gadai Service Integration
|
|||
|
|
|
|||
|
|
**How Gadai calls this:**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
import "database/sql"
|
|||
|
|
import "encoding/json"
|
|||
|
|
|
|||
|
|
db, _ := sql.Open("postgres", "postgres://user:pass@localhost/gadai_mulia")
|
|||
|
|
|
|||
|
|
payload := map[string]interface{}{
|
|||
|
|
"nasabah_nama": "Budi Santoso",
|
|||
|
|
"nasabah_phone": "6281234567890",
|
|||
|
|
"nominal_tagihan": 1500000.0,
|
|||
|
|
"tanggal_tempo": "2026-06-30",
|
|||
|
|
"invoice_pdf_url": "https://storage.gadai.com/invoices/inv-001.pdf",
|
|||
|
|
"contract_no": "GAD-2026-001",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
payloadJSON, _ := json.Marshal(payload)
|
|||
|
|
|
|||
|
|
db.Exec(`
|
|||
|
|
INSERT INTO collection_broadcasts.broadcast_staging
|
|||
|
|
(sopiga_collar_id, sopiga_template_id, message_payload, status)
|
|||
|
|
VALUES ($1, $2, $3, 'pending')
|
|||
|
|
`, 70, 2, payloadJSON)
|
|||
|
|
|
|||
|
|
// Done! Worker picks it up automatically
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🐛 Troubleshooting
|
|||
|
|
|
|||
|
|
### "Message format wrong"
|
|||
|
|
→ Check template variable order matches Sopiga:
|
|||
|
|
```sql
|
|||
|
|
SELECT variable_order, sopiga_variable_name FROM template_variable_mapping
|
|||
|
|
WHERE sopiga_template_id=2 ORDER BY variable_order;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### "Record stuck in pending"
|
|||
|
|
→ Check worker logs:
|
|||
|
|
```bash
|
|||
|
|
# Look for errors in stdout/stderr
|
|||
|
|
# Check DB connection
|
|||
|
|
psql -U postgres -h localhost -d gadai_mulia -c "SELECT 1;"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### "Sopiga API errors"
|
|||
|
|
→ Check token and URL:
|
|||
|
|
```bash
|
|||
|
|
curl -X GET https://omnix.promas.site/api/client/collar/list \
|
|||
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ Checklist
|
|||
|
|
|
|||
|
|
- [ ] Schema created
|
|||
|
|
- [ ] Sopiga template created (note template_id)
|
|||
|
|
- [ ] Sopiga collar created (note collar_id)
|
|||
|
|
- [ ] Database populated with template_id, collar_id
|
|||
|
|
- [ ] Variable mappings inserted (check order!)
|
|||
|
|
- [ ] Worker deployed and running
|
|||
|
|
- [ ] Test record inserted
|
|||
|
|
- [ ] Record moved to 'dispatched' after 30s
|
|||
|
|
- [ ] Record moved to 'delivered' after 5 min
|
|||
|
|
- [ ] WhatsApp message received on test phone
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📞 Quick Reference
|
|||
|
|
|
|||
|
|
**Database:**
|
|||
|
|
- Schema: `collection_broadcasts`
|
|||
|
|
- Main table: `broadcast_staging`
|
|||
|
|
- Config tables: `sopiga_template_config`, `template_variable_mapping`, `sopiga_collar_config`
|
|||
|
|
|
|||
|
|
**Sopiga URLs:**
|
|||
|
|
- Base: `https://omnix.promas.site`
|
|||
|
|
- Create template: POST `/api/client/template`
|
|||
|
|
- Create collar: POST `/api/client/collar`
|
|||
|
|
- Add recipient: POST `/api/client/collar/add-recipient`
|
|||
|
|
- Get recipient detail: GET `/api/client/collar/add-recipient/{id}/detail`
|
|||
|
|
|
|||
|
|
**Worker:**
|
|||
|
|
- Poll interval: 30 seconds (pending records)
|
|||
|
|
- Status sync: 5 minutes (delivery status)
|
|||
|
|
- Max retries: 3 (exponential backoff: 1s, 2s, 4s)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**Ready to go! 🚀**
|