# TaxCore Current Architecture - Data Flow Analysis

## Overview

This document describes the **current architecture** of the TaxCore integration after the migration from PHP to Node.js service. It clarifies how data flows from the browser to TaxCore and back, and where data is persisted.

## Architecture Summary

```
┌─────────────────────────────────────────────────────────────────────────┐
│                           Browser (POS.js)                               │
│                                                                          │
│  1. User completes sale                                                  │
│  2. Compiles sale data (items, payments, customer, cashier)             │
│  3. Generates UUIDv7 for transaction                                     │
│                                                                          │
└────┬─────────────────────────────────────────────────────┬──────────────┘
     │                                                      │
     │ sendTaxCoreData(saleData)                           │ sendSaleData(saleData)
     │ (TaxCore receipt generation)                        │ (Backend save)
     │                                                      │
     ▼                                                      ▼
┌─────────────────────────────────────┐    ┌──────────────────────────────┐
│   Mode Selection (localStorage)     │    │   PHP Backend                │
│   taxcore_direct = true/false       │    │   /api/POS/sale              │
│                                      │    │                              │
├────────────┬────────────────────────┤    │   PosController::sale()      │
│  Direct    │  Proxy                 │    └──────────────────────────────┘
│  Mode      │  Mode                  │                   │
└──────┬─────┴────────┬───────────────┘                   │
       │              │                                    │
       │              ▼                                    ▼
       │    ┌───────────────────────┐          ┌──────────────────────────┐
       │    │   PHP Proxy           │          │  PostgreSQL Database     │
       │    │   /api/POS/taxcore    │          │                          │
       │    │                       │          │  lineitems_payments:     │
       │    │   PosController       │          │  - sales_receipt_id      │
       │    │   ::taxcore()         │          │  - line_items (JSON)     │
       │    │   ::callTaxCoreService│          │  - payments (JSON)       │
       │    └───────────┬───────────┘          │  - customer (optional)   │
       │                │                       │  - customer_id (opt)     │
       │                │                       │                          │
       ▼                ▼                       │  Saved by:               │
┌───────────────────────────────────────┐     │  PosController::sale()   │
│   Node.js TaxCore Service             │     └──────────────────────────┘
│   Port 3001                            │
│   websocket-server/taxcore-service.js │
│                                        │
│   • Receives sale payload              │
│   • Injects defaults (date, type)     │
│   • Maps payment types                 │
│   • Enriches with cashier info         │
└───────────┬───────────────────────────┘
            │
            │ HTTP POST with mTLS
            │ (PFX certificate)
            │
            ▼
┌───────────────────────────────────────────────────────────────┐
│              FRCS TaxCore API (V-SDC)                         │
│              http://devesdc.sandbox.vms.frcs.org.fj:8888/...  │
│                                                                │
│              POST /v3/invoices                                 │
│                                                                │
│              Returns:                                          │
│              - invoiceNumber                                   │
│              - journal (receipt text)                          │
│              - verificationQRCode                              │
│              - verificationUrl                                 │
│              - signature                                       │
│              - sdcDateTime                                     │
│              - taxItems                                        │
│              - totalAmount                                     │
└────────────┬──────────────────────────────────────────────────┘
             │
             │ TaxCore Response
             │
             ▼
┌──────────────────────────────────────────────────────────────┐
│  Node.js Service Returns to Browser                          │
│  {                                                            │
│    success: true,                                             │
│    receipt: journal,                                          │
│    taxcore: { ...full TaxCore response... }                  │
│  }                                                            │
└────────────┬─────────────────────────────────────────────────┘
             │
             │
             ▼
┌────────────────────────────────────────────────────────────────────┐
│  Browser Receives TaxCore Response                                  │
│                                                                      │
│  1. saveTaxCoreDataToDB(uuid, taxCoreResult)                        │
│     → IndexedDB: sales_receipts store                               │
│     → Syncs to: PostgreSQL sales_receipts table                     │
│                                                                      │
│  2. sendSaleData(saleData)                                          │
│     → PHP: /api/POS/sale                                            │
│     → Saves to: PostgreSQL lineitems_payments table                 │
│                                                                      │
│  3. showReceiptModal(taxCoreResult)                                 │
│     → Display receipt to user                                       │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
```

## Critical Architecture Points

### 1. **Node.js Cannot Save to Database**
- The Node.js TaxCore service is **stateless** and **database-agnostic**
- It only communicates with TaxCore API using mTLS certificates
- **No database credentials** or connections in Node.js service
- This is by design for security and separation of concerns

### 2. **Browser Compiles Complete Sale Data**
The browser (POS.js) is responsible for:
- Compiling sale items with tax labels
- Collecting payment methods and amounts
- Gathering customer/buyer information
- Fetching cashier details
- Generating transaction UUID (UUIDv7)

**Sale Data Structure:**
```javascript
const saleData = {
  sales_receipt_id: "01924a5b-1234-7890-abcd-ef0123456789", // UUIDv7
  items: [
    {
      product_id: "product-uuid",
      name: "Product Name",
      qty: 2,
      price: 10.50,
      unit: "each",
      labels: ["A", "G"] // Tax labels (A=15% VAT, G=0%)
    }
  ],
  payments: [
    { method: "Cash", amount: 21.00 }
  ],
  total: 21.00,
  pay: 21.00,
  type: "Normal", // or Proforma, Advance, Training, Copy
  customer: {
    name: "John Doe",
    tin: "123456789"
  },
  customer_id: "customer-uuid"
};
```

### 3. **Dual Save Operations**

#### A. TaxCore Receipt Save (via Browser → IndexedDB → Sync)
```javascript
// In POS.js
async function saveTaxCoreDataToDB(uuid, taxCoreResult) {
  const syncManager = await getManager({
    tables: { sales_receipts: '/api/sales_receipts' }
  });
  
  const record = {
    id: uuid,
    type: 'taxcore',
    data: taxCoreResult,
    customer: taxCoreResult?.input?.customer,
    customer_id: taxCoreResult?.input?.customer_id,
    timestamp: Date.now()
  };
  
  await syncManager.add('sales_receipts', record);
  // This syncs to PostgreSQL sales_receipts table
}
```

**PostgreSQL: `sales_receipts` Table**
```sql
CREATE TABLE sales_receipts (
  sales_receipt_id UUID PRIMARY KEY,
  type VARCHAR,              -- Normal, Proforma, etc.
  invoiceNumber VARCHAR,
  journal TEXT,              -- Receipt text
  verificationQRCode TEXT,
  verificationUrl TEXT,
  signature TEXT,
  sdcDateTime TIMESTAMP,
  data JSONB,                -- Full TaxCore response + customer info
  -- ... other TaxCore fields
);
```

#### B. Sale Data Save (via Browser → PHP Backend)
```javascript
// In POS.js
async function sendSaleData(saleData) {
  const res = await fetch('../api/POS/sale', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(saleData)
  });
  return await res.json();
}

async function saveSaleDataToDB(uuid, saleResult) {
  const syncManager = await getManager({
    tables: { lineitems_payments: '/api/lineitems_payments' }
  });
  
  const record = {
    id: uuid,
    org_id: window.currentOrgId,
    line_items: saleResult.items,
    payments: saleResult.payments,
    customer: saleResult.customer,
    customer_id: saleResult.customer_id,
    isDeleted: false,
    updatedAt: new Date().toISOString()
  };
  
  await syncManager.add('lineitems_payments', record);
}
```

**PostgreSQL: `lineitems_payments` Table**
```sql
CREATE TABLE lineitems_payments (
  sales_receipt_id UUID PRIMARY KEY,
  org_id UUID,
  line_items JSONB,          -- Array of items sold
  payments JSONB,            -- Array of payment methods
  customer JSONB,            -- Customer details
  customer_id UUID,
  isDeleted BOOLEAN,
  updatedAt TIMESTAMP
);
```

### 4. **PHP Backend Role (PosController::sale)**

The PHP backend receives sale data and:
1. **Saves to `lineitems_payments` table**
2. **Updates `sales_receipts.data` with customer info** (if exists)
3. **Deducts inventory** via `InventoryService`
4. **Does NOT call TaxCore** (browser already did this)

```php
// PosController::sale()
public function sale() {
  $input = json_decode(file_get_contents('php://input'), true);
  $transactionUUID = $input['sales_receipt_id'];
  
  // Save to lineitems_payments
  $qb->table('lineitems_payments')->insert([
    'sales_receipt_id' => $transactionUUID,
    'line_items' => json_encode($items),
    'payments' => json_encode($payments),
    // ...
  ]);
  
  // Update sales_receipts with customer metadata
  $existing = $qb->table('sales_receipts')
    ->where('sales_receipt_id', $transactionUUID)
    ->first();
  if ($existing) {
    $qb->table('sales_receipts')->update([
      'data' => json_encode(['customer' => $customer, 'customer_id' => $customer_id])
    ]);
  }
  
  // Deduct inventory
  $inventoryService->removeInventory($inventoryItems);
  
  respond(200, ['success' => true]);
}
```

### 5. **PHP Proxy Mode (Optional)**

When `localStorage.taxcore_direct = false`:

```
Browser → PHP (/api/POS/taxcore) → Node.js (3001) → TaxCore API
```

**PosController::taxcore()**
```php
public function taxcore() {
  $input = json_decode(file_get_contents('php://input'), true);
  
  // Call Node.js service
  $sale = $this->callTaxCoreService($payload);
  
  // Save TaxCore receipt to sales_receipts
  $qb->table('sales_receipts')->insert([
    'sales_receipt_id' => $input['sales_receipt_id'],
    'journal' => $sale['journal'],
    'invoiceNumber' => $sale['invoiceNumber'],
    'data' => json_encode(['customer' => $input['customer']]),
    // ...
  ]);
  
  // Return receipt to browser
  respond(200, [
    'success' => true,
    'receipt' => $sale['journal'],
    'taxcore' => $sale
  ]);
}
```

## Data Flow Sequence Diagram

```
Browser          Node.js Service       TaxCore API       PostgreSQL
  │                    │                     │                │
  │ 1. Compile Sale    │                     │                │
  │    Data            │                     │                │
  │─────────────┐      │                     │                │
  │             │      │                     │                │
  │ 2. sendTaxCoreData()                     │                │
  ├──────────────────>│                      │                │
  │                    │                     │                │
  │                    │ 3. POST /v3/invoices with mTLS       │
  │                    ├──────────────────>  │                │
  │                    │                     │                │
  │                    │ 4. Receipt + Invoice#                │
  │                    │<──────────────────  │                │
  │                    │                     │                │
  │ 5. TaxCore Result  │                     │                │
  │<──────────────────┤                     │                │
  │                    │                     │                │
  │ 6. saveTaxCoreDataToDB()                 │                │
  │ (IndexedDB → Sync)                       │                │
  ├───────────────────────────────────────────────────────>  │
  │                    │                     │  sales_receipts│
  │                    │                     │                │
  │ 7. sendSaleData()  │                     │                │
  │ (to PHP backend)   │                     │                │
  ├──────────────────────────────────────────────────────>  │
  │                    │                     │ lineitems_     │
  │                    │                     │ payments       │
  │                    │                     │                │
  │ 8. showReceiptModal()                    │                │
  │                    │                     │                │
```

## Key Takeaways

1. ✅ **Browser compiles all sale data** (items, payments, customer, tax labels)
2. ✅ **Node.js ONLY talks to TaxCore** - no database access
3. ✅ **Browser saves TaxCore receipt** to `sales_receipts` via IndexedDB sync
4. ✅ **Browser sends sale data to PHP** which saves to `lineitems_payments`
5. ✅ **PHP backend deducts inventory** and updates metadata
6. ✅ **Two separate tables**: `sales_receipts` (TaxCore data) + `lineitems_payments` (sale data)
7. ✅ **Same UUID links both records**: `sales_receipt_id`

## Database Schema Relationship

```
┌─────────────────────────────────────┐      ┌────────────────────────────────┐
│      sales_receipts                 │      │    lineitems_payments          │
├─────────────────────────────────────┤      ├────────────────────────────────┤
│ sales_receipt_id (PK) ◄─────────────┼──────┤ sales_receipt_id (PK, FK)      │
│ type                                │      │ org_id                         │
│ invoiceNumber                       │      │ line_items (JSONB)             │
│ journal (receipt)                   │      │ payments (JSONB)               │
│ verificationQRCode                  │      │ customer (JSONB)               │
│ verificationUrl                     │      │ customer_id (UUID)             │
│ signature                           │      │ isDeleted                      │
│ sdcDateTime                         │      │ updatedAt                      │
│ data (JSONB) - includes customer    │      │                                │
└─────────────────────────────────────┘      └────────────────────────────────┘
     ▲                                            ▲
     │                                            │
     └────────────── Same UUID ───────────────────┘
```

## Configuration Options

### Direct Mode (Recommended)
```javascript
localStorage.setItem('taxcore_direct', 'true');
localStorage.setItem('taxcore_service_url', 'http://localhost:3001');
```

**Pros:**
- Lower latency
- No PHP overhead
- Simpler flow

**Cons:**
- Browser must reach Node.js service directly
- Less centralized logging

### Proxy Mode
```javascript
localStorage.setItem('taxcore_direct', 'false');
```

**Pros:**
- Centralized through PHP
- Easier to add auth/logging
- Works with existing PHP infrastructure

**Cons:**
- Extra hop (latency)
- PHP must reach Node.js service

## Testing the Flow

1. **Start Node.js TaxCore Service:**
   ```bash
   cd websocket-server
   npm run taxcore
   ```

2. **Open Browser DevTools Console**

3. **Complete a Sale in POS**

4. **Check Logs:**
   - Browser Console: See sendTaxCoreData, sendSaleData calls
   - Node.js: `/usr/src/logs/taxcore-service.log`
   - PHP: Apache error log

5. **Verify Database:**
   ```sql
   -- Check TaxCore receipt
   SELECT sales_receipt_id, type, invoiceNumber, journal 
   FROM sales_receipts 
   ORDER BY "createdAt" DESC LIMIT 1;
   
   -- Check sale data
   SELECT sales_receipt_id, line_items, payments 
   FROM lineitems_payments 
   ORDER BY "updatedAt" DESC LIMIT 1;
   ```

## Troubleshooting

### Node.js Service Not Running
```bash
# Check if service is running
curl http://localhost:3001/health

# Should return: {"status":"ok","service":"TaxCore Service"}
```

### Certificate Issues
```bash
# Verify certificate exists
ls -la /var/www/html/TAF/FRCS_Certs_Install/*.pfx

# Check Node.js logs
tail -f /usr/src/logs/taxcore-service.log
```

### Database Not Saving
```javascript
// Check IndexedDB in DevTools → Application → Storage → IndexedDB
// Verify LocalSyncManager is configured:
await getManager({ 
  tables: { 
    sales_receipts: '/api/sales_receipts',
    lineitems_payments: '/api/lineitems_payments'
  }
});
```

## Future Improvements

1. **Single Save Endpoint**: Consolidate both saves into one API call
2. **Transaction Atomicity**: Ensure both tables save or both fail
3. **Retry Logic**: Handle network failures gracefully
4. **Offline Queue**: Queue sales when TaxCore is unreachable
5. **Real-time Sync**: WebSocket notifications for sale completion

---

**Last Updated:** October 2, 2025  
**Related Docs:**
- `websocket-server/TAXCORE_SERVICE.md`
- `docs/TaxCore_NodeJS_Migration.md`
- `docs/TaxCore_Architecture_Diagrams.md`
