# Flutter — Receipt No WebSocket Integration

## Flow (IMPORTANT — prevents number gaps)

```
1. Screen opens  →  receipt_no:next  →  peek "065"
2. User taps Save  →  POST /receipt.php with receipt_no="065"
3. PHP saves to DB  →  calls internal endpoint  →  broadcasts "066" to all
4. All devices see  "066"
```

**NEVER call `receipt_no:request` before PHP save.** This causes number gaps.

---

## Connection

```dart
import 'package:socket_io_client/socket_io_client.dart' as IO;

final socket = IO.io(
  'https://dayloan.agniplay.com/admin',
  OptionBuilder()
      .setPath('/ws')
      .setAuth({'token': yourJwtToken})
      .setTransports(['websocket'])
      .disableAutoConnect()
      .build(),
);

socket.connect();
```

## Screen Open — Peek Next Receipt No

```dart
socket.emit('receipt_no:next', { 'requestId': 'peek_1' });

socket.on('ack', (data) {
  if (data['requestId'] == 'peek_1') {
    final nextNo = data['data']['receipt_no']; // "065"
    // Update UI
  }
});
```

## User Taps Save — Save via PHP First

```dart
void _onSave() {
  // Use the peeked receipt_no to save via PHP
  _saveToApi(_nextReceiptNo!);
}

Future<void> _saveToApi(String receiptNo) async {
  final response = await http.post(
    Uri.parse('https://dayloanphp.agniplay.com/receipt.php'),
    headers: { 'Authorization': 'Bearer $token' },
    body: {
      'loan_id': selectedLoanId,
      'receipt_no': receiptNo,       // the peeked number
      'receipt_date': '2026-07-17',
      'cust_id': customerId,
      'cus_name': customerName,
      'no_of_dues': '1',
      'due_amt': '600',
      'paid_amt': '600',
      'paid_dues': '1',
      'balance_dues': '2400',
      'pending_dues': '4',
      'ledger_name': 'Cash A/c',
      'principal_per_due': '400',
      'interest_per_due': '200',
    },
  );

  final result = jsonDecode(response.body);
  if (result['success'] == true) {
    // PHP auto-broadcasts next receipt_no to all devices
    // No need to call receipt_no:request!
    Navigator.pop(context);
  }
}
```

## Listen for Broadcasts (Auto-Updated After PHP Save)

```dart
// When ANY terminal saves via PHP, server broadcasts the next number
socket.on('receipt_no:next', (data) {
  final nextNo = data['data']['receipt_no']; // "066"
  setState(() => _nextReceiptNo = nextNo);
});
```

## Full Screen Example

```dart
class ReceiptScreen extends StatefulWidget {
  final String token;
  const ReceiptScreen({required this.token, Key? key}) : super(key: key);
  @override
  _ReceiptScreenState createState() => _ReceiptScreenState();
}

class _ReceiptScreenState extends State<ReceiptScreen> {
  IO.Socket? _socket;
  String? _nextReceiptNo;
  bool _saved = false;

  @override
  void initState() {
    super.initState();

    _socket = IO.io(
      'https://dayloan.agniplay.com/admin',
      OptionBuilder()
          .setPath('/ws')
          .setAuth({'token': widget.token})
          .setTransports(['websocket'])
          .disableAutoConnect()
          .build(),
    );

    _socket!.connect();

    // Listen for ack (peek response)
    _socket!.on('ack', (data) {
      final rid = data['requestId'] as String;
      final d = data['data'] as Map<String, dynamic>?;
      if (rid.startsWith('peek_') && d != null) {
        setState(() => _nextReceiptNo = d['receipt_no']);
      }
    });

    // Listen for broadcasts (other terminals saved)
    _socket!.on('receipt_no:next', (data) {
      final nextNo = data['data']?['receipt_no'];
      if (nextNo != null) setState(() => _nextReceiptNo = nextNo);
    });

    // Peek on screen open
    Future.delayed(Duration(seconds: 1), () {
      _socket!.emit('receipt_no:next', {
        'requestId': 'peek_${DateTime.now().millisecondsSinceEpoch}',
      });
    });
  }

  void _onSave() {
    if (_nextReceiptNo == null) return;
    _saveToApi(_nextReceiptNo!);
  }

  Future<void> _saveToApi(String receiptNo) async {
    // POST to PHP receipt.php with receiptNo
    // On success, PHP broadcasts next to all devices
    setState(() => _saved = true);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Receipt')),
      body: Column(
        children: [
          if (_saved)
            Text('Saved!', style: TextStyle(fontSize: 20, color: Colors.green)),
          if (!_saved)
            Text('Next Receipt: $_nextReceiptNo',
                style: TextStyle(fontSize: 20)),
          Expanded(child: Container()),
          ElevatedButton(
            onPressed: _saved ? null : _onSave,
            child: Text('Save'),
          ),
        ],
      ),
    );
  }
}
```

## Events Reference

| Event | Direction | Purpose |
|-------|-----------|---------|
| `receipt_no:next` | Client → Server | Peek (read-only) |
| `receipt_no:next` | Server → Client | Broadcast next after PHP save |
| `receipt_no:request` | Client → Server | **DO NOT USE** — causes gaps |

## Ack Response

```json
// Peek only
{ "type": "receipt_no:next", "requestId": "...", "data": { "receipt_no": "065" } }
```

## Broadcast

```json
// After PHP saves and calls internal endpoint
{ "version": 1, "type": "receipt_no:next", "timestamp": 1234567890, "data": { "receipt_no": "066" } }
```

## Why NOT to Use `receipt_no:request`

```
receipt_no:request → consumes number (counter advances)
  ↓
PHP save might FAIL
  ↓
Number wasted → GAP in sequence
```

```
receipt_no:next (peek) → no consumption
  ↓
PHP save → DB insert → internal endpoint → broadcast
  ↓
Number only consumed when actually saved → NO GAPS
```
