# Odoo-Based Approval Sync

## Overview
The approval status is synced from Odoo's `active` field. When the sales team changes a customer's `active` status in Odoo, the portal automatically detects and updates the approval status.

---

## How It Works

### Odoo Active Field Mapping

```
Odoo active = true  → Portal approval_status = 'approved'
Odoo active = false → Portal approval_status = 'pending' or 'rejected'
```

### Sync Mechanisms

There are TWO ways the approval status is synced:

#### 1. Scheduled Sync (Every 10 Minutes)
**Command**: `php artisan customers:sync-approval-status`

Automatically runs every 10 minutes via Laravel scheduler.

**What it does:**
- Checks all wholesale customers with `approval_status = 'pending'`
- Fetches their data from Odoo
- If `active = true` in Odoo → Approves customer in portal
- If `active = false` and comment contains "reject" → Rejects customer
- Logs all changes

**Usage:**
```bash
# Sync all pending customers (default)
php artisan customers:sync-approval-status

# Sync all wholesale customers
php artisan customers:sync-approval-status --all

# Sync specific customer
php artisan customers:sync-approval-status --customer-id=123
```

#### 2. Real-Time Sync on Login
**Location**: `AuthController::login()`

When a wholesale customer tries to login:
1. Checks portal `approval_status`
2. If not approved, queries Odoo in real-time
3. If Odoo `active = true`, auto-approves and allows login
4. If Odoo `active = false`, blocks login

**Benefits:**
- Immediate approval without waiting for scheduled sync
- Customer can login as soon as admin approves in Odoo
- Fallback if scheduled sync hasn't run yet

---

## Approval Workflow in Odoo

### Step 1: Customer Registers
- Portal creates customer in Odoo with `active = false`
- Customer is "archived" in Odoo (not visible in normal views)
- Admin receives email with POTRAZ license

### Step 2: Admin Reviews in Odoo
1. Go to Contacts in Odoo
2. Click Filters → Archived
3. Find the customer (e.g., "Test ISP Ltd")
4. Review POTRAZ license (attached to email)
5. Verify company credentials

### Step 3: Admin Approves
**Option A: Unarchive**
1. Open customer record
2. Click "Unarchive" button
3. Customer `active` becomes `true`

**Option B: Set Active Field**
1. Open customer record
2. Edit form
3. Check "Active" checkbox
4. Save

### Step 4: Portal Syncs
**Automatic (within 10 minutes):**
- Scheduled task detects `active = true`
- Updates portal: `approval_status = 'approved'`
- Customer can now login

**Immediate (on login attempt):**
- Customer tries to login
- Portal checks Odoo real-time
- Detects `active = true`
- Auto-approves and allows login

### Step 5: Customer Logs In
- Customer can now access portal
- Full functionality available

---

## Rejection Workflow

### Option 1: Keep Archived with Comment
1. Keep customer `active = false`
2. Add rejection reason to `comment` field
3. Include words like "rejected" or "denied"
4. Scheduled sync detects and marks as rejected

### Option 2: Manual Portal Update
```sql
UPDATE customers 
SET approval_status = 'rejected',
    rejection_reason = 'Invalid POTRAZ license'
WHERE id = 123;
```

---

## Scheduled Task Setup

### Laravel Scheduler
The sync command is scheduled in `routes/console.php`:

```php
Schedule::command('customers:sync-approval-status')
    ->everyTenMinutes()
    ->withoutOverlapping();
```

### Cron Job (Production)
Add to server crontab:

```bash
* * * * * cd /path/to/afinet-portal-backend && php artisan schedule:run >> /dev/null 2>&1
```

This runs Laravel's scheduler every minute, which then runs the sync command every 10 minutes.

### Manual Testing
```bash
# Run sync manually
php artisan customers:sync-approval-status

# Run with verbose output
php artisan customers:sync-approval-status -v

# Sync specific customer
php artisan customers:sync-approval-status --customer-id=5

# Sync all wholesale customers (not just pending)
php artisan customers:sync-approval-status --all
```

---

## Sync Command Output

### Example Output:
```
Starting customer approval status sync...
Found 3 customer(s) to sync.

Syncing customer: Test ISP Ltd (ID: 5)
  Current status: pending
  Odoo active: true
  ✓ Customer APPROVED
  → Approval email sent (TODO: implement email)

Syncing customer: Another ISP (ID: 6)
  Current status: pending
  Odoo active: false
  - Still pending (active=false in Odoo)

Syncing customer: Rejected ISP (ID: 7)
  Current status: pending
  Odoo active: false
  ✗ Customer REJECTED
  → Rejection email sent (TODO: implement email)

Sync completed!
+------------+-------+
| Status     | Count |
+------------+-------+
| Approved   | 1     |
| Rejected   | 1     |
| Unchanged  | 1     |
| Errors     | 0     |
+------------+-------+
```

---

## Real-Time Login Check

### Flow:
```
Customer Login Attempt
         ↓
Check portal approval_status
         ↓
    Not approved?
         ↓
Query Odoo for active status
         ↓
    active = true?
         ↓
Auto-approve in portal
         ↓
Allow login immediately
```

### Benefits:
- No waiting for scheduled sync
- Instant approval on login
- Better user experience
- Fallback mechanism

### Performance:
- Only queries Odoo if customer is not approved
- Approved customers skip Odoo check
- Cached in portal database after first check

---

## Monitoring & Logs

### Laravel Logs
Location: `storage/logs/laravel.log`

**Approval Events:**
```
[2026-02-25 10:30:00] local.INFO: Customer auto-approved during login via Odoo sync
{"customer_id":5,"odoo_partner_id":3582}
```

**Sync Errors:**
```
[2026-02-25 10:30:00] local.ERROR: Failed to sync customer approval status
{"customer_id":5,"error":"Odoo API timeout"}
```

### Database Queries
```sql
-- Check pending customers
SELECT id, name, email, approval_status, odoo_partner_id, created_at
FROM customers
WHERE type = 'wholesale' AND approval_status = 'pending';

-- Check recently approved
SELECT id, name, email, approved_at, approved_by
FROM customers
WHERE approval_status = 'approved'
ORDER BY approved_at DESC
LIMIT 10;

-- Check sync activity
SELECT id, name, approval_status, approved_at, approved_by
FROM customers
WHERE approved_by LIKE '%Odoo Sync%';
```

---

## Troubleshooting

### Issue: Customer approved in Odoo but still can't login

**Check 1: Verify Odoo Status**
```bash
php artisan tinker
$customer = Customer::find(5);
$odoo = app(\App\Services\OdooService::class);
$odooCustomer = $odoo->getCustomerById($customer->odoo_partner_id);
dd($odooCustomer['active']);
```

**Check 2: Run Manual Sync**
```bash
php artisan customers:sync-approval-status --customer-id=5
```

**Check 3: Check Portal Status**
```sql
SELECT id, name, approval_status, approved_at, approved_by
FROM customers WHERE id = 5;
```

**Fix: Manual Approval**
```sql
UPDATE customers 
SET approval_status = 'approved',
    approved_at = NOW(),
    approved_by = 'Manual Fix'
WHERE id = 5;
```

### Issue: Scheduled sync not running

**Check 1: Verify Cron Job**
```bash
crontab -l
# Should see: * * * * * cd /path/to/project && php artisan schedule:run
```

**Check 2: Test Scheduler**
```bash
php artisan schedule:list
# Should show: customers:sync-approval-status
```

**Check 3: Run Manually**
```bash
php artisan schedule:run
```

### Issue: Odoo API timeout

**Check 1: Test Odoo Connection**
```bash
php artisan tinker
$odoo = app(\App\Services\OdooService::class);
$odoo->authenticate();
```

**Check 2: Check Odoo URL**
```bash
# In .env
ODOO_URL=https://dfaz.ipos.co.zw
```

**Fix: Increase Timeout**
In `OdooService.php`, increase timeout in Guzzle client.

---

## Email Notifications (TODO)

### Approval Email
**To**: Customer email
**Subject**: "Your AFINET Account Has Been Approved"
**Content**:
- Congratulations message
- Login instructions
- Portal URL
- Support contact

### Rejection Email
**To**: Customer email
**Subject**: "AFINET Account Registration Update"
**Content**:
- Rejection notification
- Reason (if provided)
- Next steps
- Support contact

**Implementation:**
```php
// In SyncCustomerApprovalStatus command
Mail::to($customer->email)->send(new WholesaleApprovedEmail($customer));
Mail::to($customer->email)->send(new WholesaleRejectedEmail($customer));
```

---

## Testing Checklist

- [ ] Register wholesale customer
- [ ] Verify customer created in Odoo with `active = false`
- [ ] Try to login (should be blocked)
- [ ] Approve in Odoo (set `active = true`)
- [ ] Wait 10 minutes or run manual sync
- [ ] Try to login again (should work)
- [ ] Check portal `approval_status = 'approved'`
- [ ] Test real-time sync (approve in Odoo, login immediately)
- [ ] Test rejection (keep `active = false`, add rejection comment)
- [ ] Verify scheduled task runs automatically

---

## Configuration

### Environment Variables
```env
# Odoo connection (already configured)
ODOO_URL=https://dfaz.ipos.co.zw
ODOO_DATABASE=odoo_dfaz_300126
ODOO_USERNAME=quatrohaus.dev@dfafrica.co.zw
ODOO_PASSWORD=ecb4ab887985d57355272fc10302c2158f536572

# Email for admin notifications
COMPANY_MAIL=tawona@quatrohaus.com
```

### Sync Frequency
To change sync frequency, edit `routes/console.php`:

```php
// Every 5 minutes
Schedule::command('customers:sync-approval-status')->everyFiveMinutes();

// Every 15 minutes
Schedule::command('customers:sync-approval-status')->everyFifteenMinutes();

// Every hour
Schedule::command('customers:sync-approval-status')->hourly();
```

---

## Summary

✅ **Approval is controlled by Odoo `active` field**
✅ **Two sync mechanisms: scheduled (10 min) + real-time (on login)**
✅ **No admin portal needed - all done in Odoo**
✅ **Automatic approval when admin sets `active = true`**
✅ **Customer can login immediately after approval**
✅ **Fallback mechanisms for reliability**

**Admin Workflow:**
1. Receive email with POTRAZ license
2. Review in Odoo
3. Click "Unarchive" or set `active = true`
4. Done! Customer can login within 10 minutes (or immediately on next login attempt)
