# Frontend API Guide — Latest Backend Changes

Base URL: `/api/v1` (aliases also exist under `/api/`)

All routes below require `Authorization: Bearer <token>` unless noted.

---

## 1. Low-stock products (full details)

### Analytics — stock endpoint
`GET /analytics/stock?timeRange=7d`

### Analytics — low-stock only (easiest to test)
`GET /analytics/low-stock-products?lowStockThreshold=10`

Both return the same **full** product shape in `data.lowStockProducts` (and `data.products`).

`GET /analytics/stock?timeRange=7d`

`data.lowStockProducts[]` now returns **full product objects**, not just a name:

```json
{
  "_id": "...",
  "productId": "...",
  "productName": "Dell Latitude 7420",
  "name": "Dell Latitude 7420",
  "brand": "Dell",
  "modelNumber": "7420",
  "barcode": "1234567890123",
  "serialNumber": null,
  "stock": 3,
  "minStock": 10,
  "costPrice": 800,
  "sellingPrice": 1100,
  "stockValue": 2400,
  "category": "categoryObjectId",
  "categoryLabel": "Laptops",
  "status": true,
  "description": "...",
  "image": "/uploads/...",
  "warrantyExpiry": null,
  "specs": {
    "productionBatch": "11th",
    "requiresColdChain": false,
    "activeIngredient": "i7",
    "strength": "16GB",
    "packSize": "512GB SSD",
    "manufacturer": null,
    "routeOfAdministration": "65W"
  }
}
```

### Analytics — consolidated dashboard
`GET /analytics/consolidated?timeRange=7d&lowStockThreshold=10`

`data.tables.lowStockProducts[]` uses the same shape.

### Reports
Analytics PDF/data reports now include:
- `stockSnapshot[]` — all active products (full detail shape above)
- `lowStockProducts[]` — only products where `stock <= 10`

**Frontend:** bind table cards directly to these fields; no extra product lookup needed.

---

## 2. Email optional (Accounts Receivable & Payable)

### Accounts Receivable
`POST /accounts-receivable` and `PATCH /accounts-receivable/:id`

- `email` is **optional**
- Send `""` or omit — both are accepted
- If provided, must be a valid email

### Accounts Payable
`POST /accounts-payable` and `PATCH /accounts-payable/:id`

- Same rules as receivable (already supported; receivable now matches)

---

## 3. Invoice optional dates

### Create / update invoice
`POST /invoices` · `PATCH /invoices/:id`

| Field | Required | Notes |
|-------|----------|-------|
| `invoiceDate` | Yes | |
| `dueDate` | No | Omit or send `""` |
| `deliveryDate` | No | New optional field |

```json
{
  "invoiceNumber": "invoice-2026-3",
  "invoiceDate": "2026-06-24",
  "accountId": "...",
  "items": [{ "item": "productId", "quantity": 1, "unitprice": 1100 }]
}
```

---

## 4. Invoice edit, refund & financial impact

### Edit invoice
`PATCH /invoices/:id`

- Allowed on **unpaid / partial / paid / overdue** invoices
- **Blocked** when `paymentStatus === "refunded"`
- When `totalAmount` or item prices change:
  - Linked **loan `principalAmount`** is updated automatically
  - **`totalGrossProfit`** is recalculated from batch cost allocations
  - If `amountPaid > new totalAmount`, excess moves to `overpaymentAmount`
  - Net profit in analytics decreases when gross profit decreases

**Response includes `financialImpact`:**

```json
{
  "status": "success",
  "data": {
    "invoice": { "...": "full invoice with itemName on each line" },
    "financialImpact": {
      "previousTotalAmount": 1100,
      "newTotalAmount": 900,
      "totalAmountDelta": -200,
      "previousGrossProfit": 100,
      "newGrossProfit": 80,
      "grossProfitDelta": -20,
      "loanPrincipalUpdated": true,
      "netProfitImpact": -20
    }
  }
}
```

**Frontend implementation:**
1. Open edit form with current invoice payload (`GET /invoices/:id`)
2. On save, `PATCH` changed fields (items, discount, tax, prices)
3. Show `financialImpact` toast/summary after save
4. Refresh loan/receivable summary for the account if linked

### Full invoice refund
`POST /invoices/:id/refund`

- Restocks all items (products + medicines)
- Sets invoice totals to `0`, `paymentStatus: "refunded"`
- Closes linked loan (`principalAmount: 0`)
- Reduces gross profit / net profit by refunded profit

```json
{
  "status": "success",
  "data": {
    "invoice": { "...": "refunded invoice" },
    "refundSummary": {
      "totalRefundAmount": 1100,
      "totalRefundedGrossProfit": 100,
      "previousTotalAmount": 1100,
      "previousGrossProfit": 100,
      "netProfitImpact": -100,
      "loanPrincipalUpdated": true
    }
  }
}
```

### Refund single line item
`POST /invoices/:id/refund-item`

```json
{
  "itemId": "invoiceLineItemObjectId",
  "quantity": 1
}
```

- `itemId` = the `_id` on the invoice line item (not the product id)
- Omit `quantity` to refund the full line quantity
- Partial refunds reduce totals and gross profit proportionally
- Full line removal may set invoice to `refunded` if nothing remains

**Frontend implementation:**
1. Add “Refund item” action on each invoice line (use line `_id` as `itemId`)
2. Add “Refund entire invoice” on invoice detail
3. Confirm dialog showing amount + profit impact
4. After refund, refresh invoice list + analytics/KPI cards

---

## 5. Barcode optional (products)

`POST /product` · `PUT /product/:id`

- `barcode` is **optional** — omit or send `""`
- Multiple products can have **no barcode**
- Duplicate error only when two products share the **same non-empty** barcode
- If provided: max 32 characters (no minimum length)

---

## 6. Vendor multi-payment (FIFO) — like customer invoices

Mirrors accounts receivable `invoice-payment` flow.

### Get outstanding payable records for vendor
`GET /accounts-payable/:id/record-summary?page=1&limit=20`

```json
{
  "status": "success",
  "data": {
    "account": { "_id": "...", "id": "VND-001", "name": "Supplier A", "type": "supplier" },
    "totalPayable": 5000,
    "totalPrincipal": 8000,
    "totalPaid": 3000,
    "recordCount": 3,
    "page": 1,
    "limit": 20,
    "totalPages": 1,
    "records": [
      {
        "_id": "...",
        "recordNumber": "PAY-001",
        "date": "2026-06-01",
        "principalAmount": 3000,
        "totalPaid": 1000,
        "balance": 2000,
        "paymentStatus": "partial",
        "status": "partial",
        "direction": "outgoing"
      }
    ]
  }
}
```

### Pay vendor — FIFO across oldest records
`POST /accounts-payable/:id/record-payment`

```json
{
  "amount": 2500,
  "paymentDate": "2026-06-24",
  "paymentMethod": "Cash",
  "reference": "CHQ-123",
  "note": "Optional note"
}
```

`paymentMethod` must be one of: `Cash`, `Bank Transfer`, `Check`, `Mobile Payment`, `Other`

**Response (same pattern as receivable):**

```json
{
  "status": "success",
  "data": {
    "totalPayment": 2500,
    "totalApplied": 2500,
    "remainingUnallocated": 0,
    "recordsUpdated": 2,
    "appliedPayments": [
      {
        "payableRecordId": "...",
        "recordNumber": "PAY-001",
        "amountApplied": 2000,
        "newBalance": 0,
        "paymentStatus": "paid",
        "paymentRecordId": "..."
      }
    ]
  }
}
```

**Frontend implementation (mirror AR invoice payment UI):**
1. Vendor detail → “Pay outstanding” button
2. Load `GET /accounts-payable/:id/record-summary`
3. Show FIFO list of open records + total payable
4. Submit lump-sum payment to `POST /accounts-payable/:id/record-payment`
5. Display `appliedPayments` breakdown (which records were paid)

### Receivable reference (existing — for parity)

| AR (customer) | AP (vendor) |
|---------------|-------------|
| `GET /accounts-receivable/:id/invoice-summary` | `GET /accounts-payable/:id/record-summary` |
| `POST /accounts-receivable/:id/invoice-payment` | `POST /accounts-payable/:id/record-payment` |

---

## 7. Invoice line items (product names)

All invoice list/detail responses include populated items:

```json
{
  "itemId": "productOrPartId",
  "itemName": "Paracetamol 500mg",
  "itemType": "medicine",
  "item": {
    "_id": "...",
    "name": "Paracetamol 500mg",
    "model": "...",
    "sellingPrice": 1100
  },
  "quantity": 1,
  "unitprice": 1100,
  "amount": 1100
}
```

Use `itemName` for display; `itemId` for actions (refund uses line `_id`).

---

## 8. Direct net profit entries (from prior update)

| Method | Route |
|--------|-------|
| List | `GET /net-profit-entries` |
| Create | `POST /net-profit-entries` |
| Get | `GET /net-profit-entries/:id` |
| Update | `PUT /net-profit-entries/:id` |
| Delete | `DELETE /net-profit-entries/:id` |

Create body:
```json
{
  "title": "Manual adjustment",
  "amount": 500,
  "effect": "increase",
  "date": "2026-06-24",
  "description": "Optional"
}
```

Included in `GET /analytics/net-profit` as `directNetProfitAdjustments`.

---

## Quick route index

```
GET    /analytics/stock
GET    /analytics/low-stock-products
GET    /analytics/consolidated
GET    /analytics/product-performance
POST   /invoices
PATCH  /invoices/:id
POST   /invoices/:id/refund
POST   /invoices/:id/refund-item
GET    /accounts-payable/:id/record-summary
POST   /accounts-payable/:id/record-payment
GET    /accounts-receivable/:id/invoice-summary
POST   /accounts-receivable/:id/invoice-payment
POST   /net-profit-entries
GET    /net-profit-entries
POST   /drawer-sessions/open
GET    /drawer-sessions/current
GET    /drawer-sessions/:id/expected
GET    /drawer-sessions/:id/history
POST   /drawer-sessions/:id/close
```

**Drawer frontend guide:** `redmes/FRONTEND_DRAWER_INTEGRATION.md`
