Files
brandize_bi/README.md
T
2026-10-27 12:30:28 -03:00

140 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BI Dashboard
A flat, server-free dashboard for the Brandize BI webhook
(`Summe je Abgangsland`). It shows the gross revenue (Brutto inkl. USt) per
departure country for a selected period, with KPI cards, a bar chart and a
detail table.
## Run
Just open `index.html` in a browser (double-click it). No server, no build step.
The page calls the webhook directly:
```
https://brandize.app.n8n.cloud/webhook/bi-dashboard
```
via a **POST** request with a JSON body:
```json
{ "query": "Summe je Abgangsland", "month": "2026-SEP" }
```
The webhook sends CORS headers (it reflects the request `Origin`, including
`null` for `file://`), so a direct browser call works.
## Navigation
The left sidebar groups reports by topic:
- **Amazon** → *Umsatz je Abgangsland* (Brutto inkl. USt je Abgangsland)
- **Amazon** → *VAT Summary* (USt-Übersicht je Land)
- **Amazon** → *Cross Boarder Inv. Movement* (In Bearbeitung)
- **Amazon** → *Auswertung Buchhaltung* (In Bearbeitung)
- **Amazon** → *ESL/ZM-Check* (In Bearbeitung)
- **Amazon** → *Inventory Summary* (In Bearbeitung)
- **ODOO** → *Rechnungen pro Monat* (Pivot über Odoo-Rechnungen)
- **ODOO** → *FIFO Lager Bewertung* (Lagerwert FIFO)
- **ODOO** → *Produkt Materialkosten* (Materialkosten je Produkt)
- **Gesamt** → *Alle Rechnungen pro Monat* (PivotTable.js-Experiment)
- **Gesamt** → *Quartal Schweiz* (AMAZON + ODOO, Lieferung in die Schweiz)
All groups start collapsed, so no submenu is visible when the page opens.
Clicking a topic expands its submenu; clicking a submenu item switches the
view.
## Authentication
The webhooks are protected with HTTP Basic Auth. The dashboard asks for the
credentials when they are needed: the first request is sent without them, and
when the webhook answers `401`, a login prompt appears. The entered
credentials are sent as an `Authorization: Basic ...` header on every request
and kept in `sessionStorage` for the rest of the browser session (so a reload
does not prompt again). If the webhook rejects them (`401`), the prompt
reappears. Storage is best-effort: on `file://` it may be unavailable, in which
case credentials are kept in memory only.
## CSV export
The header has a **CSV** button that downloads the data of the currently
displayed dashboard, as a semicolon-delimited UTF-8 file (with BOM) including a
Gesamt row:
- *Umsatz je Abgangsland* → `umsatz-je-abgangsland_YYYY-MMM.csv`
- *VAT Summary* → `vat-summary_YYYY-MMM.csv`
- *Rechnungen pro Monat* → `odoo-rechnungen_YYYY-MMM.csv`
- *Alle Rechnungen pro Monat* → `gesamt-rechnungen-pivot_YYYY-MMM.csv`
- *Quartal Schweiz* → `schweiz-quartal_YYYY-QN.csv`
- *FIFO Lager Bewertung* → `fifo-lager_YYYY-MMM.csv`
- *Produkt Materialkosten* → `materialkosten_YYYY-MMM.csv`
The button is disabled while no dataset is loaded for the current dashboard.
## Filter
A shared filter bar sits above every dashboard (labelled **Filter** with a
funnel icon and the hint *gilt für alle Dashboards*). It holds the **Zeitraum**
month + year selector (e.g. `2026-SEP`) and the reload button, and stays visible
across all views. The previous month is preselected. The selected period is
sent to the webhook in the POST body as `month` (e.g. `2026-SEP`), matching the
`%(month)s` parameter used in `../sql/*.sql`.
The invoice dashboards (*ODOO Rechnungen pro Monat*, *Alle Rechnungen pro Monat*
and *Quartal Schweiz*) have an additional, dashboard-specific filter row. *ODOO
Rechnungen pro Monat* filters by **Tag**, **Bestellnummer**, **Lieferland** and
**Rechnungsnummer**; *Alle Rechnungen pro Monat* and *Quartal Schweiz* add a
**Quelle** field. Filtering re-renders the KPIs, chart and pivot live.
For *Quartal Schweiz* the shared **Zeitraum** control shows a **quarter**
selector (Q1–Q4) instead of the month selector; the year selector is shared.
## Pivot (Experiment)
The three invoice dashboards (*ODOO Rechnungen pro Monat*, *Alle Rechnungen pro
Monat* and *Quartal Schweiz*) render their data as an interactive
[PivotTable.js](https://pivottable.js.org/) pivot instead of a detail table.
The pivot's rows, columns, values and aggregator are freely configurable in the
pivotUI control panel; the renderer is fixed to *Table*. Each dashboard has its
own filter row and **Vorlage** dropdown with presets:
- *ODOO Rechnungen pro Monat*: *Standard* (**Rechnungsland** rows, no column
group, **Sum(Gesamtpreis brutto)**) and *Zahlart*.
- *Alle Rechnungen pro Monat* and *Quartal Schweiz*: *Standard* (**Quelle**
rows, no column group, **Sum(Gesamtpreis brutto)**) and *Rechnungsland
(Spalten)* (adds **Rechnungsland** as the column group).
*Quartal Schweiz* is filtered to invoices with a **delivery into Switzerland**.
Applying a preset also resets the filters. The chosen layout is kept while you
type in the filters.
It loads jQuery, jQuery UI and PivotTable.js from public CDNs, so — unlike the
rest of the app — it needs an **internet connection** to render; offline it shows
a short message instead. To vendor the libraries locally, download them next to
`index.html` and change the `<link>`/`<script>` URLs in the `<head>`.
## Data sources
Because the datasets are large, the dashboard requests **only the dataset of
the currently displayed dashboard** — one POST per dashboard, sent only when
that dashboard is opened or reloaded. Nothing is prefetched. The dataset is
selected with the `query` field:
- *Umsatz je Abgangsland* → `query: "Summe je Abgangsland"`
- *VAT Summary* → `query: "VAT Summary"`
- *Rechnungen pro Monat* → `query: "ODOO Rechnungen pro Monat"`
- *Alle Rechnungen pro Monat* → `query: "Alle Rechnungen pro Monat"`
- *Quartal Schweiz* → `query: "Quartal Schweiz"` (sends `quarter` instead of `month`)
- *FIFO Lager Bewertung* → `query: "FIFO Lager Bewertung"` (response is `[{ id, query, row_count, data: [...] }]`)
- *Produkt Materialkosten* → `query: "Produkt Materialkosten"` (one row per product; `remaining_qty` = remaining of that cost bracket, not the product total)
## Files
| File | Purpose |
| --- | --- |
| `index.html` | Dashboard markup |
| `styles.css` | Styling |
| `app.js` | Data loading, parsing and rendering |