Service instance annotations for billing purposes #263

Closed
opened 2025-10-30 10:36:10 +00:00 by tobru · 0 comments
Owner

Stories

As a user, I want to have a well-organized invoice.

Implementation Notes

Corresponds to internal ticket APPCAT-1195 and follows #96.

Context

The control plane automatically creates a custom resource for each service instance, which contains meta information to send to the event-based billing in Odoo:

apiVersion: vshn.appcat.vshn.io/v1
kind: BillingService
[...]
  labels:
    appcat.vshn.io/claim-name: beckdis
    appcat.vshn.io/claim-namespace: beck-test
    appcat.vshn.io/ownerapiversion: v1
    appcat.vshn.io/ownercomposite: beckdis-krst7
    appcat.vshn.io/ownergroup: vshn.appcat.vshn.io
    appcat.vshn.io/ownerkind: XVSHNRedis
    appcat.vshn.io/service-name: redis
  name: beckdis-krst7-billing-service
  namespace: syn-appcat
[...]
spec:
[...]
  odoo:
    instanceID: beckdis-krst7
    itemDescription: 'APPUiO Cloud - Cluster: c-appuio-lab-cloudscale-rma-0 / Namespace:
      beck-test'
    itemGroupDescription: beckdis
    organization: vshn
    productID: appcat-vshn-redis-besteffort
    salesOrderID: S10167
    size: "1"
    unitID: uom_uom_45_1e112771
[...]

The most important fields:

  • spec.odoo.instanceID: Generated and tracked by the control plane (per instance)
  • spec.odoo.itemDescription: Managed by Servala Portal (per instance)
  • spec.odoo.itemGroupDescription: Managed by Servala Portal (per instance or per organization origin)
  • spec.odoo.organization: Managed by Servala Portal Organization namespace annotation servala.com/organization
  • spec.odoo.productID: Managed by Servala Portal See in #264 (per service instance)
  • spec.odoo.salesOrderID: Managed by Servala Portal Organization namespace annotation servala.com/sales_order (per instance or per organization origin)
  • spec.odoo.size: Computed by the control plane (per instance)
  • spec.odoo.unitID: Managed by Servala Portal See in #264 (per instance)

Some of these fields are per organization, and some are per service instance.

Task

Part 1: Grouping per Organization Origin

Allow configuring the "grouping" behavior on the organization origin, called "Invoice Line Item Grouping":

  • By Service (default)
  • By Organization

Part 2: Annotations

Add the annotations per service instance (metadata.annotations), depending on the "Invoice Line Item Grouping" setting, inherited from the organization origin:

Case By Service:

  • servala.com/erp_item_group_description: "Servala Service: $Service.name"
  • servala.com/erp_item_description: "$ServiceInstance.name on $ServiceInstance.$ControlPlaneCRD.$ControlPlane.$CloudProvider.name $ServiceInstance.$ControlPlaneCRD.$ControlPlane.name"

Example:

  • servala.com/erp_item_group_description: Servala Service: Redis
  • servala.com/erp_item_description: MyProdRedis on Exoscale Geneva (CH-GVA-2).

Case By Organization:

  • servala.com/erp_item_group_description: Organization: $Organization.name ($Organization.osb_guid)
  • servala.com/erp_item_description = $ServiceInstance.name on $ServiceInstance.$ControlPlaneCRD.$ControlPlane.name [Org: $Organization.osb_guid]

Example:

  • servala.com/erp_item_group_description = Organization: ACME (01998651-dc86-7d43-9e49-cdb790fcc4f0)
  • servala.com/erp_item_description = MyProdRedis on Geneva (CH-GVA-2) [Org: 01998651-dc86-7d43-9e49-cdb790fcc4f0]

Only set osb_guid when the field is not empty.

Part 3: Admin Command

Create a Django admin command that can be used to sync all labels and annotations to all organization namespaces on all or selected control planes.

## Stories _As a user, I want to have a well-organized invoice._ ## Implementation Notes Corresponds to internal ticket [APPCAT-1195](https://vshnticket.atlassian.net/browse/APPCAT-1195) and follows #96. ### Context The control plane automatically creates a [custom resource for each service instance](https://kb.vshn.ch/app-catalog/adr/0033-event-based-billing-oddo.html), which contains meta information to send to the event-based billing in Odoo: ```yaml apiVersion: vshn.appcat.vshn.io/v1 kind: BillingService [...] labels: appcat.vshn.io/claim-name: beckdis appcat.vshn.io/claim-namespace: beck-test appcat.vshn.io/ownerapiversion: v1 appcat.vshn.io/ownercomposite: beckdis-krst7 appcat.vshn.io/ownergroup: vshn.appcat.vshn.io appcat.vshn.io/ownerkind: XVSHNRedis appcat.vshn.io/service-name: redis name: beckdis-krst7-billing-service namespace: syn-appcat [...] spec: [...] odoo: instanceID: beckdis-krst7 itemDescription: 'APPUiO Cloud - Cluster: c-appuio-lab-cloudscale-rma-0 / Namespace: beck-test' itemGroupDescription: beckdis organization: vshn productID: appcat-vshn-redis-besteffort salesOrderID: S10167 size: "1" unitID: uom_uom_45_1e112771 [...] ``` The most important fields: * `spec.odoo.instanceID`: Generated and tracked by the control plane (per instance) * `spec.odoo.itemDescription`: **Managed by Servala Portal** (per instance) * `spec.odoo.itemGroupDescription`: **Managed by Servala Portal** (per instance or per organization origin) * `spec.odoo.organization`: **Managed by Servala Portal** Organization namespace annotation `servala.com/organization` * `spec.odoo.productID`: **Managed by Servala Portal** See in #264 (per service instance) * `spec.odoo.salesOrderID`: **Managed by Servala Portal** Organization namespace annotation `servala.com/sales_order` (per instance or per organization origin) * `spec.odoo.size`: Computed by the control plane (per instance) * `spec.odoo.unitID`: **Managed by Servala Portal** See in #264 (per instance) Some of these fields are per organization, and some are per service instance. ### Task #### Part 1: Grouping per Organization Origin Allow configuring the "grouping" behavior on the organization origin, called "Invoice Line Item Grouping": * By Service (default) * By Organization #### Part 2: Annotations Add the annotations per service instance (`metadata.annotations`), depending on the "Invoice Line Item Grouping" setting, inherited from the organization origin: Case `By Service`: * `servala.com/erp_item_group_description`: "Servala Service: $Service.name" * `servala.com/erp_item_description`: "$ServiceInstance.name on $ServiceInstance.$ControlPlaneCRD.$ControlPlane.$CloudProvider.name $ServiceInstance.$ControlPlaneCRD.$ControlPlane.name" Example: * `servala.com/erp_item_group_description`: Servala Service: Redis * `servala.com/erp_item_description`: MyProdRedis on Exoscale Geneva (CH-GVA-2). Case `By Organization`: * `servala.com/erp_item_group_description`: Organization: $Organization.name ($Organization.osb_guid) * `servala.com/erp_item_description = $ServiceInstance.name on $ServiceInstance.$ControlPlaneCRD.$ControlPlane.name [Org: $Organization.osb_guid]` Example: * `servala.com/erp_item_group_description` = Organization: ACME (01998651-dc86-7d43-9e49-cdb790fcc4f0) * `servala.com/erp_item_description` = MyProdRedis on Geneva (CH-GVA-2) [Org: 01998651-dc86-7d43-9e49-cdb790fcc4f0] Only set `osb_guid` when the field is not empty. #### Part 3: Admin Command Create a Django admin command that can be used to sync all labels and annotations to all organization namespaces on all or selected control planes.
tobru changed title from Organization namespace annotations for billing purposes to Service instance annotations for billing purposes 2025-10-30 15:00:05 +00:00
tobru closed this issue 2025-12-05 12:55:39 +00:00
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#263
No description provided.