# Store Keeper Module - Production API Issues Analysis

## Summary
Found multiple critical issues with API endpoint calls that could cause 400 errors and silent failures on production.

---

## 1. CRITICAL: Missing Response Status Check in Dashboard Fetch Calls

### Issue
Dashboard.php fetch calls do NOT check `response.ok` before parsing JSON. If the API returns an error (401, 400, 500), the HTML error page will be parsed as JSON, causing silent failures.

### Affected Files & Lines
- **[store_keeper/dashboard.php](store_keeper/dashboard.php#L296)** - Line 296: `loadMetrics()`
- **[store_keeper/dashboard.php](store_keeper/dashboard.php#L312)** - Line 312: `loadLowStockItems()`
- **[store_keeper/dashboard.php](store_keeper/dashboard.php#L342)** - Line 342: `loadExpiringItems()`
- **[store_keeper/dashboard.php](store_keeper/dashboard.php#L370)** - Line 370: `loadRecentActivities()`

### Current Code Pattern
```javascript
const response = await fetch('../backend/store_keeper_api.php?action=get_metrics');
const data = await response.json();  // ⚠️ No check if response succeeded!

if (data.success) {
    // Process data
}
```

### Problem
- If API returns 401 (Unauthorized - session expired on production), response body is HTML error page
- `response.json()` tries to parse HTML as JSON and throws SyntaxError
- Error is caught silently in catch block, nothing displayed to user
- User sees empty dashboard with no feedback

### Comparison with Other Files
[store_keeper/stock_receiving.php](store_keeper/stock_receiving.php#L432) does this CORRECTLY:
```javascript
const response = await fetch(url);
const rawText = await response.text();
let data;
try {
    data = JSON.parse(rawText);
} catch (parseError) {
    throw new Error(`Invalid JSON response (HTTP ${response.status})`);
}

if (!response.ok) {
    throw new Error(data?.message || `Request failed with status ${response.status}`);
}
```

---

## 2. Missing Credentials in Fetch Requests

### Issue
Dashboard.php fetch calls don't include `credentials: 'include'` option, which can cause session cookies to NOT be sent on production if:
- API is on different port/domain
- CORS is configured
- Server uses strict cookie policies

### Affected Files & Lines
- **[store_keeper/dashboard.php](store_keeper/dashboard.php#L296)** - Line 296: `loadMetrics()`
- **[store_keeper/dashboard.php](store_keeper/dashboard.php#L312)** - Line 312: `loadLowStockItems()`
- **[store_keeper/dashboard.php](store_keeper/dashboard.php#L342)** - Line 342: `loadExpiringItems()`
- **[store_keeper/dashboard.php](store_keeper/dashboard.php#L370)** - Line 370: `loadRecentActivities()`

### Current Code
```javascript
const response = await fetch('../backend/store_keeper_api.php?action=get_metrics');
// ⚠️ No credentials option - cookies might not be sent
```

### Expected Code
```javascript
const response = await fetch('../backend/store_keeper_api.php?action=get_metrics', {
    credentials: 'include'  // ✓ Ensure cookies are sent
});
```

### Consequence
[backend/store_keeper_api.php](backend/store_keeper_api.php#L15) checks:
```php
$is_authenticated = !empty($_SESSION['store_keeper_id']);
if (!$is_authenticated) {
    http_response_code(401);  // Returns 401 error
    sendResponse(false, 'Unauthorized - Please login first', null);
}
```
Returns 401 error instead of data, dashboard fails.

---

## 3. Session Initialization Issues - Potential Header Conflicts

### Issue
[backend/store_keeper_api.php](backend/store_keeper_api.php#L10) sets Content-Type header BEFORE calling `session_start()`:

```php
header('Content-Type: application/json');  // Line 10
session_start();                            // Line 11
```

### Risk
- On some server configurations, if output is sent before session_start(), session won't initialize properly
- This can result in 400 Bad Request errors on production but work fine locally

### Recommendation
Move `session_start()` before any headers:
```php
session_start();                            // FIRST
header('Content-Type: application/json');  // THEN
```

---

## 4. Inconsistent API Action Parameter Passing

### Issue
Action parameter is passed in QUERY STRING for both GET and POST requests:

### GET Requests (dashboard.php)
```javascript
fetch('../backend/store_keeper_api.php?action=get_metrics')  // GET in query string ✓
```

### POST Requests (inventory.php, stock_receiving.php)
```javascript
fetch('../backend/store_keeper_api.php?action=adjust_inventory', {  // POST in query string
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({...})  // Data in body
})
```

### Files with POST calls
- **[store_keeper/inventory.php](store_keeper/inventory.php#L288)** - Line 288: `adjust_inventory`
- **[store_keeper/stock_receiving.php](store_keeper/stock_receiving.php#L358)** - Line 358: `add_stock_receiving`
- **[store_keeper/stock_receiving.php](store_keeper/stock_receiving.php#L393)** - Line 393: `log_wastage`
- **[store_keeper/stock_receiving.php](store_keeper/stock_receiving.php#L688)** - Line 688: `close_remaining_shipment`

### Problem
- Violates REST conventions (data should be in body for POST)
- Query strings can have length limits on some servers, causing 414 errors
- Some proxies/WAF might filter these as suspicious
- Less portable across different server configurations

### Recommendation
Pass action in POST body instead:
```javascript
fetch('../backend/store_keeper_api.php', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        action: 'add_stock_receiving',  // ✓ In body
        ingredient_id: ingredientId,
        ...
    })
})
```

---

## 5. URL Encoding Issues - Potential 400 Errors

### Issue
Some endpoints use query parameters with user input that may not be properly encoded:

### Files with URL encoding issues
- **[store_keeper/stock_receiving.php](store_keeper/stock_receiving.php#L432)** - Line 432:
```javascript
const url = filterDate ? 
    `../backend/store_keeper_api.php?action=get_stock_receiving&date=${filterDate}` :  // ⚠️ Not encoded!
    '../backend/store_keeper_api.php?action=get_stock_receiving';
```

- **[store_keeper/stock_receiving.php](store_keeper/stock_receiving.php#L543)** - Line 543:
```javascript
fetch(`../backend/store_keeper_api.php?action=get_receipt&id=${id}`)  // ⚠️ Not encoded!
```

- **[store_keeper/stock_receiving.php](store_keeper/stock_receiving.php#L591)** - Line 591:
```javascript
fetch(`../backend/store_keeper_api.php?action=get_po_for_receiving&po_id=${poId}`)  // ⚠️ Not encoded!
```

### Comparison with Correct Implementation
Other files (about.php, blog.php) correctly encode parameters:
```javascript
fetch(`backend/meal_slots_api.php?action=get_slot_availability&date=${encodeURIComponent(selectedDate)}&guests=${encodeURIComponent(guestsCount)}`);
```

### Consequence
Special characters in parameters (dates with '/', ids with special chars) cause:
- 400 Bad Request errors
- Query string parsing failures
- Potential security issues

---

## 6. POST Request Error Handling Issues

### Issue
POST requests in inventory.php don't check response status:

### Files with incomplete error handling
- **[store_keeper/inventory.php](store_keeper/inventory.php#L288)** - Line 288-308:
```javascript
const response = await fetch('../backend/store_keeper_api.php?action=adjust_inventory', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({...})
});

const data = await response.json();  // ⚠️ No response.ok check!

if (data.success) {
    // Success handling
} else {
    Swal.fire('Error', data.message || 'Failed to adjust inventory', 'error');
}
```

### Comparison
**Best Practice** from stock_receiving.php shows proper error handling:
```javascript
try {
    const response = await fetch(url);
    const rawText = await response.text();
    let data;
    try {
        data = JSON.parse(rawText);
    } catch (parseError) {
        throw new Error(`Invalid JSON response (HTTP ${response.status})`);
    }

    if (!response.ok) {
        throw new Error(data?.message || `Request failed with status ${response.status}`);
    }
    // Rest of handling...
} catch (error) {
    console.error('Error:', error);
    Swal.fire('Error', error.message, 'error');
}
```

---

## 7. Session-Related Parameter Issues

### Issue
Some API calls pass parameters that might not be properly sanitized or validated on production:

### Affected File
- **[backend/store_keeper_api.php](backend/store_keeper_api.php#L23)**:
```php
$action = isset($_GET['action']) ? $_GET['action'] : (isset($_POST['action']) ? $_POST['action'] : null);
```

This accepts action from BOTH GET and POST, but doesn't validate it's a legitimate action.

### Files affected
- Anywhere action parameter comes from user input (almost all fetch calls)

### Risk
If production server has strict input validation, unrecognized action values might:
- Return 400 Bad Request
- Trigger WAF rules
- Be rejected by request filters

---

## Summary of Required Fixes

| Priority | Issue | Files | Severity |
|----------|-------|-------|----------|
| 🔴 CRITICAL | Missing response.ok check in dashboard fetch | dashboard.php (4 locations) | 4 endpoints broken on 401 |
| 🔴 CRITICAL | Missing credentials option | dashboard.php (4 locations) | Session lost on CORS domains |
| 🟠 HIGH | Session init before header | store_keeper_api.php | Intermittent 400 errors |
| 🟠 HIGH | URL not encoded | stock_receiving.php (3 locations) | 400 errors with special characters |
| 🟡 MEDIUM | Action in query string for POST | inventory.php, stock_receiving.php | Non-standard, query string limits |
| 🟡 MEDIUM | Missing error handling in POST | inventory.php | Silent failures on errors |

---

## Quick Fix Priority Order

1. **First**: Add `response.ok` check in dashboard.php (prevents 400 error cascade)
2. **Second**: Add `credentials: 'include'` to fetch calls (fixes session issues)
3. **Third**: Move `session_start()` before headers in store_keeper_api.php
4. **Fourth**: Add `encodeURIComponent()` to URL parameters
5. **Fifth**: Add proper error handling to POST requests
