Show current service usage #193

Open
opened 2025-09-11 13:56:49 +00:00 by tobru · 2 comments
Owner

Stories

As a user, I want to see my current service usage which will lead into an invoice

Implementation Notes

This means we should display analytic lines from Odoo in a user friendly way.

## Stories _As a user, I want to see my current service usage which will lead into an invoice_ ## Implementation Notes This means we should display analytic lines from Odoo in a user friendly way.
tobru added this to the Roadmap project 2025-09-11 13:56:49 +00:00
Author
Owner

Design ready

Spec: docs/plans/2026-10-19-service-usage-view-design.md (commit c5f381de)

What the customer gets

A new page /{org}/usage/ with an entry "Usage" in the Organization menu, below
"Invoices". One row per Odoo sale order line: instance, service, quantity, money
accrued this month, money expected at month end. A CSS bar shows accrued against
expected per row, and a total row closes the table.

Where the numbers come from

  • The daily rows are account.analytic.line with instance_event = true. The
    vshn_event_billing cron writes one row per day per order line, from
    instance_start_time to today, refreshed hourly. Trial hours carry quantity 0.
  • The month quantity is the sum of those rows. One Instance Month is 730 hours.
  • Money is quantity times the Odoo line price minus discount, from the #698 price
    rule: open line price first, then pricelist, then the catalog fallback.
  • Expected is accrued plus a full day of usage for each remaining billable day. A
    full day is (24/730) x instance_size, so the forecast does not depend on the
    time of day.
  • The join is a prefix match of the order line instance_id against
    ServiceInstance.name. The longest name wins. A tie leaves the row unlinked
    with the product label.

Decisions

  • Shared sale orders: page hidden. One order carries several tenants, so a read
    by order_id would show one customer the money of another. No Odoo read happens.
  • Trial: forecast only the days after the trial end, and print that date on the
    page. The accrued column is already correct, because Odoo zeroes trial hours.
  • Every member sees the page, not only admins. New flag
    Feature.SERVICE_USAGE, default off.
  • The month is a UTC calendar month, because Odoo cuts days at 00:00 UTC. The
    header clock stays Zurich time.
  • VAT is out of scope, as everywhere else in the portal.
  • Rows are per order line, not per instance. One instance shows up to three rows,
    which matches how the invoice is built.

First step of the plan: verify on prod

Preprod has 8'086 order lines with an instance_id but zero usage analytic
rows. Every analytic row there is hours, and billable_metered_usage is false on
all of them. So three facts are unverified:

  1. A managed service line uses a UUID as instance_id. Is that UUID a
    ServiceInstance.name in the portal?
  2. Does a real usage row hold the order line instance_id in name, and does
    unit_amount sum to qty_invoiced + qty_to_invoice for the same window?
  3. Confirm the currency is the sale order currency, as in #698.

If item 1 fails, the compute and storage rows still join, and the managed service
rows stay unlinked with the product label. That is acceptable, not a blocker.

Next step: the implementation plan.

## Design ready Spec: `docs/plans/2026-10-19-service-usage-view-design.md` (commit `c5f381de`) ### What the customer gets A new page `/{org}/usage/` with an entry "Usage" in the Organization menu, below "Invoices". One row per Odoo sale order line: instance, service, quantity, money accrued this month, money expected at month end. A CSS bar shows accrued against expected per row, and a total row closes the table. ### Where the numbers come from - The daily rows are `account.analytic.line` with `instance_event = true`. The `vshn_event_billing` cron writes one row per day per order line, from `instance_start_time` to today, refreshed hourly. Trial hours carry quantity 0. - The month quantity is the sum of those rows. One Instance Month is 730 hours. - Money is quantity times the Odoo line price minus discount, from the #698 price rule: open line price first, then pricelist, then the catalog fallback. - Expected is accrued plus a full day of usage for each remaining billable day. A full day is `(24/730) x instance_size`, so the forecast does not depend on the time of day. - The join is a prefix match of the order line `instance_id` against `ServiceInstance.name`. The longest name wins. A tie leaves the row unlinked with the product label. ### Decisions - **Shared sale orders: page hidden.** One order carries several tenants, so a read by `order_id` would show one customer the money of another. No Odoo read happens. - **Trial: forecast only the days after the trial end**, and print that date on the page. The accrued column is already correct, because Odoo zeroes trial hours. - **Every member sees the page**, not only admins. New flag `Feature.SERVICE_USAGE`, default off. - **The month is a UTC calendar month**, because Odoo cuts days at 00:00 UTC. The header clock stays Zurich time. - VAT is out of scope, as everywhere else in the portal. - Rows are per order line, not per instance. One instance shows up to three rows, which matches how the invoice is built. ### First step of the plan: verify on prod Preprod has 8'086 order lines with an `instance_id` but **zero** usage analytic rows. Every analytic row there is hours, and `billable_metered_usage` is false on all of them. So three facts are unverified: 1. A managed service line uses a UUID as `instance_id`. Is that UUID a `ServiceInstance.name` in the portal? 2. Does a real usage row hold the order line `instance_id` in `name`, and does `unit_amount` sum to `qty_invoiced + qty_to_invoice` for the same window? 3. Confirm the currency is the sale order currency, as in #698. If item 1 fails, the compute and storage rows still join, and the managed service rows stay unlinked with the product label. That is acceptable, not a blocker. Next step: the implementation plan.
Author
Owner

Layout decided

We mocked the page in a browser and settled the shape. Spec updated:
docs/plans/2026-10-19-service-usage-view-design.md (commit c151e21c).

The page

It reads like a bill, not like a table.

  1. Title and month card. "October 2026", "as of 19.10.2026 16:12, 12 days left".
    On the right, right aligned, Accrued so far and Expected at month end in
    the success color. A footnote explains the forecast and carries a
    "How this is calculated" toggle.
  2. One block per instance, ordered by expected money, biggest first. Each block
    has its own border, the instance name linked to the instance page, its size tag,
    its instance id, and its own expected subtotal.
  3. A composition bar per block, one segment per billed item. It answers "why is
    this instance expensive", for example storage being half of it.
  4. Item rows with the quantity, the accrued and the expected money. This is the
    line the customer can hold against the invoice.

Small rules that keep it honest

  • The bar is a composition, not a quota. The portal already uses bars for disk quota,
    so a "time left in the month" bar would be misread.
  • Colors are fixed per item kind: compute, managed service, sizing, storage. The same
    item keeps its color in every block.
  • Every item row carries a dot in its segment color, so the row is the legend and
    there is no separate legend line.
  • A block with only one billed item gets no bar. One segment says nothing.
  • The accrued figure is never hidden, also not on a phone, because the gap between
    accrued and expected is the point of the page.
  • No color-only facts. Every share is also readable as money.
  • The bar is aria-hidden decoration, and the footnote toggle is a native
    <details>, so it works without JavaScript.

States

  • Trial: amber banner, "Your trial ends 20.10.2026. Usage is free until then, the
    forecast covers 20.10. to 31.10." Zero accrued plus a real forecast would otherwise
    look like a bug.
  • No usage yet: "No usage recorded yet this month", with the reason.
  • Billing system down: "Usage is not available right now", a retry link, and no
    numbers. A zero would be a lie that the customer would believe.

Mobile

Two labelled columns per item: name and share on top, accrued on the left and expected
on the right below.

Next step: the implementation plan.

## Layout decided We mocked the page in a browser and settled the shape. Spec updated: `docs/plans/2026-10-19-service-usage-view-design.md` (commit `c151e21c`). ### The page It reads like a bill, not like a table. 1. **Title and month card.** "October 2026", "as of 19.10.2026 16:12, 12 days left". On the right, right aligned, **Accrued so far** and **Expected at month end** in the success color. A footnote explains the forecast and carries a "How this is calculated" toggle. 2. **One block per instance**, ordered by expected money, biggest first. Each block has its own border, the instance name linked to the instance page, its size tag, its instance id, and its own expected subtotal. 3. **A composition bar per block**, one segment per billed item. It answers "why is this instance expensive", for example storage being half of it. 4. **Item rows** with the quantity, the accrued and the expected money. This is the line the customer can hold against the invoice. ### Small rules that keep it honest - The bar is a composition, not a quota. The portal already uses bars for disk quota, so a "time left in the month" bar would be misread. - Colors are fixed per item kind: compute, managed service, sizing, storage. The same item keeps its color in every block. - Every item row carries a dot in its segment color, so the row is the legend and there is no separate legend line. - A block with only one billed item gets no bar. One segment says nothing. - The accrued figure is never hidden, also not on a phone, because the gap between accrued and expected is the point of the page. - No color-only facts. Every share is also readable as money. - The bar is `aria-hidden` decoration, and the footnote toggle is a native `<details>`, so it works without JavaScript. ### States - **Trial:** amber banner, "Your trial ends 20.10.2026. Usage is free until then, the forecast covers 20.10. to 31.10." Zero accrued plus a real forecast would otherwise look like a bug. - **No usage yet:** "No usage recorded yet this month", with the reason. - **Billing system down:** "Usage is not available right now", a retry link, and no numbers. A zero would be a lie that the customer would believe. ### Mobile Two labelled columns per item: name and share on top, accrued on the left and expected on the right below. Next step: the implementation plan.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
servala/servala-portal#193
No description provided.