# Mobile Close Reports API Specification

**Base URL**: `https://dev2.buyerkiosk.com/api/mobile/close-reports`
**Authentication**: JWT Bearer Token (same as other mobile APIs)
**Content-Type**: `application/json`

---

## Authentication

All endpoints require a valid JWT token in the Authorization header:

```
Authorization: Bearer <jwt_token>
```

The user must have access to the requested store (validated via `StoreContextMiddleware`).

---

## Endpoints

### 1. List Recent Reports

**POST** `/:typeNum/list`

Returns paginated list of close report summaries for the store.

#### Request Body

```json
{
  "limit": 30,   // Optional, default 30, max 100
  "offset": 0    // Optional, default 0
}
```

#### Response

```json
{
  "success": true,
  "reports": [
    {
      "id": 243,
      "reportDate": "2026-01-07",
      "postedAt": "2026-01-26T23:39:48+00:00",
      "netSalesRetail": 25206100,
      "salesVsGoalPercent": 0.0,
      "buysCount": 31,
      "buysTotal": 13090800,
      "hasDiscrepancy": true,
      "laborPercent": null
    }
  ],
  "pagination": {
    "total": 243,
    "limit": 30,
    "offset": 0,
    "hasMore": true
  }
}
```

---

### 2. Get Report Detail

**POST** `/:typeNum/detail`

Returns the full close report for a specific date.

#### Request Body

```json
{
  "date": "2026-01-07"  // Required, YYYY-MM-DD format
}
```

#### Response

```json
{
  "success": true,
  "report": {
    "id": 243,
    "metadata": {
      "reportDate": "2026-01-07",
      "postedAt": "2026-01-26T23:39:48+00:00",
      "storeNumber": "",
      "storeName": "",
      "typeNum": "ou00"
    },
    "salesSummary": {
      "grossSalesRetail": 25326100,
      "grossSalesCost": 8781100,
      "grossSalesGM": 653000,
      "netSalesRetail": 25206100,
      "netSalesCost": 8745100,
      "netSalesGM": 653000,
      "salesCount": 69,
      "averageRetail": 365300
    },
    "buys": {
      "buysCost": 13090800,
      "buysRetail": 39140000,
      "buysGM": 666000,
      "buysCount": 31,
      "buysGoal": 0,
      "buysOutstanding": 0
    },
    "returns": {
      "returnsCost": -36000,
      "returnsRetail": -120000,
      "returnsNumber": 1,
      "returnsGM": 700000
    },
    "goals": {
      "salesGoal": 0,
      "buysGoal": 0,
      "salesVsGoalPercent": 0,
      "buysVsGoalPercent": 0
    },
    "cash": {
      "cashPaidIn": 0,
      "cashPaidOut": 0,
      "cashVariance": -140000,
      "safeBalance": 0,
      "alerts": [],
      "hasDiscrepancy": true
    },
    "checklists": {
      "openingCheckListSubmitter": "",
      "openingCheckListComments": "",
      "closingCheckListSubmitter": "",
      "closingCheckListComments": "",
      "varianceExplanation": "$15.00 given to student Alyssa from register 1",
      "dayCloseComments": "",
      "dayCloseSubmitter": "A Weaver",
      "tasks": [],
      "hasOpeningChecklist": false,
      "hasClosingChecklist": false,
      "completedTaskCount": 0,
      "totalTaskCount": 0
    },
    "dailyResults": {
      "openingCheckListSubmitter": "",
      "closingCheckListSubmitter": "",
      "varianceExplanation": "$15.00 given to student Alyssa from register 1",
      "dayCloseComments": "",
      "dayCloseSubmitter": "A Weaver"
    },
    "labor": null,
    "comparisons": null,
    "isLegacyMigrated": true
  }
}
```

---

### 3. Get Latest Report

**POST** `/:typeNum/latest`

Convenience endpoint - returns the most recent close report for the store.

#### Request Body

None required.

#### Response

Same format as `/detail` endpoint.

#### Error Response (no reports exist)

```json
{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "No close reports found for this store",
    "status": 404
  }
}
```

---

### 4. Get Calendar Dates

**POST** `/:typeNum/calendar`

Returns dates that have close reports available (for calendar UI).

#### Request Body

```json
{
  "year": 2026,    // Required
  "month": 1       // Optional (1-12), omit for full year
}
```

#### Response

```json
{
  "success": true,
  "dates": [
    "2026-01-01",
    "2026-01-02",
    "2026-01-03",
    "2026-01-07"
  ],
  "year": 2026,
  "month": 1
}
```

---

### 5. Compare Two Reports

**POST** `/:typeNum/compare`

Returns two reports with calculated deltas and variance highlights.

#### Request Body

```json
{
  "primaryDate": "2026-01-07",      // Required, YYYY-MM-DD (newer date)
  "comparisonDate": "2026-01-06"    // Required, YYYY-MM-DD (older date)
}
```

#### Response

```json
{
  "success": true,
  "primary": {
    "id": 243,
    "reportDate": "2026-01-07",
    "netSalesRetail": 25206100,
    "salesVsGoalPercent": 0,
    "buysCount": 31,
    "buysTotal": 13090800,
    "hasDiscrepancy": true,
    "laborPercent": null
  },
  "comparison": {
    "id": 242,
    "reportDate": "2026-01-06",
    "netSalesRetail": 22000000,
    "salesVsGoalPercent": 0,
    "buysCount": 28,
    "buysTotal": 11000000,
    "hasDiscrepancy": false,
    "laborPercent": null
  },
  "deltas": {
    "netSalesRetail": 3206100,
    "netSalesRetailPercent": 14.57,
    "buysCount": 3,
    "buysCountPercent": 10.71,
    "buysTotal": 2090800,
    "buysTotalPercent": 19.0,
    "salesVsGoalPercent": 0,
    "laborPercentage": 0
  },
  "highlights": [
    {
      "field": "netSalesRetail",
      "label": "Net Sales",
      "direction": "up",
      "percentChange": 14.6,
      "significance": "warning"
    },
    {
      "field": "buysCount",
      "label": "Buys Count",
      "direction": "up",
      "percentChange": 10.7,
      "significance": "warning"
    }
  ]
}
```

**Significance Levels**:
- `warning`: 10-25% variance
- `critical`: >25% variance

---

### 6. Get User Preferences

**GET** `/preferences`

Get current user's close report notification preferences. No store context required.

#### Response

```json
{
  "userId": 123,
  "closeReportEmailEnabled": true,
  "closeReportPushEnabled": false
}
```

---

### 7. Update User Preferences

**POST** `/preferences`

Update notification preferences.

#### Request Body

```json
{
  "closeReportEmailEnabled": true,    // Optional
  "closeReportPushEnabled": true      // Optional (at least one required)
}
```

#### Response

```json
{
  "userId": 123,
  "closeReportEmailEnabled": true,
  "closeReportPushEnabled": true,
  "updated": true
}
```

---

## Data Types Reference

### Monetary Values

**All monetary values are in CENTS (integers)**, not dollars. This avoids floating-point precision issues.

| Field | Type | Description |
|-------|------|-------------|
| `netSalesRetail` | int | Net sales in cents (25206100 = $252,061.00) |
| `grossSalesRetail` | int | Gross sales in cents |
| `buysCost` | int | Total buys cost in cents |
| `cashVariance` | int | Cash variance in cents (negative = short) |

### Percentage Values

| Field | Type | Description |
|-------|------|-------------|
| `salesVsGoalPercent` | float | Sales vs goal (100.5 = 100.5%) |
| `laborPercent` | float | Labor percentage (nullable if WiW disabled) |
| `grossSalesGM` | int | Gross margin in basis points (653000 = 65.3%) |

### Date/Time Formats

| Field | Format | Example |
|-------|--------|---------|
| `reportDate` | YYYY-MM-DD | `"2026-01-07"` |
| `postedAt` | ISO 8601 with TZ | `"2026-01-26T23:39:48+00:00"` |

---

## Error Responses

All errors follow this structure:

```json
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable message",
    "status": 400
  }
}
```

### Error Codes

| Code | Status | Description |
|------|--------|-------------|
| `INVALID_DATE` | 400 | Date format invalid or not a real date |
| `MISSING_DATE` | 400 | Required date parameter not provided |
| `MISSING_YEAR` | 400 | Year parameter required for calendar |
| `INVALID_MONTH` | 400 | Month must be 1-12 |
| `MISSING_DATES` | 400 | Compare requires both dates |
| `NOT_FOUND` | 404 | Report not found for specified date |
| `UNAUTHORIZED` | 401 | Invalid or expired JWT token |
| `FORBIDDEN` | 403 | User doesn't have access to this store |

---

## Optional Sections

The following sections are **only included** in the response when data is available:

| Section | When Included |
|---------|---------------|
| `labor` | Store has WhenIWork integration enabled |
| `cash` | Cash variance data was recorded |
| `checklists` | Opening/closing checklist data exists |
| `dailyResults` | Daily results comments exist |
| `comparisons` | WTD/MTD comparison data calculated |
| `buyerStats` | Buyer performance stats available |
| `sorterStats` | Sorter performance stats available |

---

## Example Usage (Dart/Flutter)

```dart
// Get latest close report
final response = await dio.post(
  '/api/mobile/close-reports/ou00/latest',
  options: Options(headers: {'Authorization': 'Bearer $token'}),
);

if (response.data['success']) {
  final report = response.data['report'];

  // Access sales data (remember: values in cents!)
  final netSales = report['salesSummary']['netSalesRetail'] / 100;
  final buysCount = report['buys']['buysCount'];

  // Check for cash discrepancy
  final hasDiscrepancy = report['cash']?['hasDiscrepancy'] ?? false;

  // Get variance explanation if exists
  final varianceNote = report['checklists']?['varianceExplanation'] ?? '';
}
```

---

## Changelog

| Date | Change |
|------|--------|
| 2026-01-26 | Initial API spec created |
| 2026-01-26 | Full DRS payload migration completed for ou00, pc00 |
