Daily Balances API

This page describes the implemented JSON REST API for exposing the data currently shown on the /history page.

Status:

  • Implemented in shift-planner-app.

Endpoint

GET /api/v1/warehouses/daily-balances?from=2026-03-01&to=2026-03-31&include_empty_days=false

Goal

The endpoint returns daily balance records across all warehouses for a requested date range.

The response is grouped by warehouse so clients do not need to de-duplicate warehouse metadata on each row.

Query Parameters

Name Type Required Description
from string yes Start date in YYYY-MM-DD format, inclusive.
to string no End date in YYYY-MM-DD format, inclusive.
include_empty_days boolean no If true, the API also returns missing days inside the requested range with zero-valued metrics. Default is false.

Request Rules

  • from must be a valid ISO date.
  • to must be a valid ISO date when provided.
  • If to is missing, the API defaults it to from + 1 calendar month - 1 day.
  • The API rejects requests where to < from.
  • The API rejects requests where the requested range is longer than 31 days.
  • Warehouses are sorted by warehouse.id.
  • Records inside each warehouse are sorted by business_date ascending.

Response Design

The response contains only business-facing fields.

It intentionally does not expose:

  • HTML-specific labels used by the /history page
  • internal editing links
  • internal audit field updated_by

When include_empty_days=false, the API returns only warehouses and dates that have stored records.

When include_empty_days=true, the API also fills missing dates with zero-valued metrics. For generated empty days, updated_at is null.

Response Shape

{
  "data": [
    {
      "warehouse": {
        "id": 1,
        "name": "Jablonec"
      },
      "records": [
        {
          "business_date": "2026-03-03",
          "metrics": {
            "full_pallets": 1,
            "full_roll_cages": 0,
            "empty_pallets": 2,
            "empty_roll_cages": 3,
            "pallets_to_be_stocked": 3,
            "dispatched_yesterday_pallets": 0,
            "dispatched_yesterday_roll_cages": 20
          },
          "updated_at": "2026-03-04T13:40:09Z"
        }
      ]
    }
  ],
  "meta": {
    "filters": {
      "from": "2026-03-01",
      "to": "2026-03-31",
      "include_empty_days": false
    },
    "warehouse_count": 1,
    "record_count": 1
  }
}

Field Definitions

Warehouse

Field Type Description
id integer Internal warehouse identifier used by Shift Planner.
name string Warehouse display name.

Record

Field Type Description
business_date string Business date in YYYY-MM-DD format.
updated_at string \| null Last update timestamp in ISO 8601 UTC format. Generated empty days use null.
metrics.full_pallets integer Number of full pallets.
metrics.full_roll_cages integer Number of full roll cages.
metrics.empty_pallets integer Number of empty pallets.
metrics.empty_roll_cages integer Number of empty roll cages.
metrics.pallets_to_be_stocked integer Number of pallets waiting to be stocked.
metrics.dispatched_yesterday_pallets integer Number of pallets dispatched on the previous day.
metrics.dispatched_yesterday_roll_cages integer Number of roll cages dispatched on the previous day.

Example Request

GET /api/v1/warehouses/daily-balances?from=2026-03-01&to=2026-03-31&include_empty_days=false
Accept: application/json

Example Response

{
  "data": [
    {
      "warehouse": {
        "id": 1,
        "name": "Jablonec"
      },
      "records": [
        {
          "business_date": "2026-03-03",
          "metrics": {
            "full_pallets": 1,
            "full_roll_cages": 0,
            "empty_pallets": 2,
            "empty_roll_cages": 3,
            "pallets_to_be_stocked": 3,
            "dispatched_yesterday_pallets": 0,
            "dispatched_yesterday_roll_cages": 20
          },
          "updated_at": "2026-03-04T13:40:09Z"
        }
      ]
    },
    {
      "warehouse": {
        "id": 4,
        "name": "Ceska Lipa"
      },
      "records": [
        {
          "business_date": "2026-03-04",
          "metrics": {
            "full_pallets": 3,
            "full_roll_cages": 0,
            "empty_pallets": 0,
            "empty_roll_cages": 3,
            "pallets_to_be_stocked": 0,
            "dispatched_yesterday_pallets": 3,
            "dispatched_yesterday_roll_cages": 3
          },
          "updated_at": "2026-03-04T13:54:56Z"
        }
      ]
    }
  ],
  "meta": {
    "filters": {
      "from": "2026-03-01",
      "to": "2026-03-31",
      "include_empty_days": false
    },
    "warehouse_count": 2,
    "record_count": 2
  }
}

Example Response With Empty Days

{
  "data": [
    {
      "warehouse": {
        "id": 4,
        "name": "Ceska Lipa"
      },
      "records": [
        {
          "business_date": "2026-03-03",
          "metrics": {
            "full_pallets": 7,
            "full_roll_cages": 1,
            "empty_pallets": 2,
            "empty_roll_cages": 3,
            "pallets_to_be_stocked": 4,
            "dispatched_yesterday_pallets": 5,
            "dispatched_yesterday_roll_cages": 6
          },
          "updated_at": "2026-03-03T06:00:00Z"
        },
        {
          "business_date": "2026-03-04",
          "metrics": {
            "full_pallets": 0,
            "full_roll_cages": 0,
            "empty_pallets": 0,
            "empty_roll_cages": 0,
            "pallets_to_be_stocked": 0,
            "dispatched_yesterday_pallets": 0,
            "dispatched_yesterday_roll_cages": 0
          },
          "updated_at": null
        }
      ]
    }
  ],
  "meta": {
    "filters": {
      "from": "2026-03-03",
      "to": "2026-03-04",
      "include_empty_days": true
    },
    "warehouse_count": 1,
    "record_count": 2
  }
}
  • 400 Bad Request for invalid date format
  • 400 Bad Request for missing from
  • 400 Bad Request when to is earlier than from
  • 400 Bad Request for invalid boolean format in include_empty_days
  • 400 Bad Request when the requested range exceeds 31 days

Notes

  • This API is based on the current /history data model in the application.
  • The API uses English field names even though the current HTML page is Czech.
  • The API is designed for integrations first, not for rendering the existing HTML table directly.