Service instance annotations for billing purposes #263

Open
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 added the
enhancement
label 2025-10-30 10:36:10 +00:00
tobru added this to the Development Planning project 2025-10-30 10:36:10 +00:00
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 added
Billing
and removed
enhancement
labels 2025-10-30 15:45:49 +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.