APPUiO Control API: Billing Entity Adapter at VSHN

This document is VSHN specific and is not part of the APPUiO Control API specification.

VSHN uses Odoo in version 8.0.

Field Mapping

At VSHN, every customer has an Odoo partner record of type Customer and a partner record of type Roleaccount for storing billing email addresses and the billing contact shown in the c/o line of the invoice. The two partner records are linked via the parent_id field of the Roleaccount partner record.

The BillingEntity resource is mapped to the Roleaccount (billing contact) partner record.

Virtual resource
apiVersion: billing.appuio.io/v1
kind: BillingEntity
metadata:
  name: be-2494 (1)
spec:
  name: Cyber AI (2)
  phone: "+41313131313"
  emails: (3)
  - contact@cyberai.example.com
  address:
    line1: "Zisternenstrasse 3f"
    line2: ""
    postalCode: "3600"
    city: "Thun"
  accountingContact: (4)
    name: "Ursula Gurnmeister" (5)
    emails: (6)
    - ursula.gurnmeister@cyberai.example.com
    - contact@cyberai.example.com
  languagePreference: de (7)
1 The name of the BillingEntity is the billing accounts partner ID in the ERP. It is prefixed with be- to keep the name RFC 1035 compliant.
Keeping the name RFC 1035 compliant is not strictly necessary, but it makes it easier to use the name in other contexts.
2 The name of the company. Also used as the display name.
3 The general contact email address of the company.
4 The accounting contact information. Used when generating invoices.
5 The name of the accounting contact person. Used in the "c/o" line of the invoice.
6 The email addresses of the accounting contact person. Invoices are sent to these addresses.
7 The language preference of the company. Used when generating invoices for text content not from the billing system. Future use.
Odoo has the terrible habit of returning false instead of null for unset fields. We already implemented a workaround for this in appuio-odoo-adapter which needs to get a generic version for string fields.

Accounting Contact

ERP Accounting Contact
{
  "id": 2495, (1)
  "name": "Accounting", (2)

  "category_id": [7], (3)
  "lang": "de_DE", (4)
  "notify_email": "always", (5)
  "parent_id": [2494, "Cyber AI"], (6)
  "property_payment_term": [2, "10 Days"], (7)

  "x_invoice_contact": "Ursula Gurnmeister", (8)
  "use_parent_address": true, (9)

  "street": "Zisternenstrasse 3f", (10)
  "street2": false,
  "city": "Thun",
  "zip": "3600",
  "country_id": [256, "Schweiz"],

  "email": "ursula.gurnmeister@cyberai.example.com, contact@cyberai.example.com", (11)
}
1 The ID of the partner record. Read-only. Exported as .metadata.name with the be- prefix.
2 Static value.
3 Tag such as Roleaccount. Statically set.
4 The language preference of the company. Not relevant yet. Exposed as .spec.languagePreference.
5 The notification preference of the contact person. Statically set to always. Invoice won’t be sent if this is not set to always.
6 The ID of the company partner record. Read-only.
7 The payment term of the company. Statically set.
8 The name of the accounting contact person. Used in the "c/o" line of the invoice. Exported as .spec.accountingContact.name.
9 Use the address of the company partner record. Statically set to true.
10 The address of the company partner record. Read-only and set from the company partner record.
11 The email addresses of the accounting contact person. Invoices are sent to these addresses. Exposed as .spec.accountingContact.emails.

Company Contact

ERP Company Contact
{
  "id": 2494, (1)
  "name": "Cyber AI", (2)

  "category_id": [1], (3)
  "lang": "de_DE", (4)
  "notify_email": "none", (5)
  "parent_id": false, (6)
  "property_payment_term": [2, "10 Days"], (7)

  "street": "Zisternenstrasse 3f", (8)
  "street2": false, (9)
  "city": "Thun", (10)
  "zip": "3600", (11)
  "country_id": [256, "Schweiz"], (12)
  "email": "contact@cyberai.example.com", (13)
  "phone": false, (14)
}
1 The ID of the partner record. Read-only. Found by the parent_id of the Roleaccount partner record.
2 The name of the company. Exported as .spec.name.
3 Tag such as Customer. Statically set.
4 The language preference of the company. Not relevant yet. Exposed as .spec.languagePreference.
5 The notification preference of the contact person. Statically set to none.
6 The ID of the parent partner record. Read-only. Unset for the company partner record.
7 The payment term of the company. Statically set.
8 The address line 1 of the company. Exposed as .spec.address.line1.
9 The address line 2 of the company. Exposed as .spec.address.line2.
10 The city of the company. Exposed as .spec.address.city.
11 The postal code of the company. Exposed as .spec.address.postalCode.
12 The country of the company. Most likely statically set to Switzerland.
13 The general contact address of the company. Exported as .spec.emails.
14 The phone number of the company. Exported as .spec.phone.

Record Handling

There are two records in the ERP that are relevant for the BillingEntity resource. They can’t be updated in the same request, so the adapter has to handle the update process in two steps.

Creation should be able to handle a failure in the second step without polluting the ERP with orphaned records.

Odoo allows to add custom fields to partner records which can be used to mark records as created by the adapter or might allow to store a transaction ID/ resourceVersion from K8s.

Get Billing Entity

  1. Load the Roleaccount partner record by the name of the BillingEntity resource

  2. Load the Customer partner record by the parent_id of the Roleaccount partner record

Create Billing Entity

  1. Create Company Partner Record

    1. Tag as coming from the BillingEntity resource

    2. Tag as in creation process (category or custom field for example x_transaction_id)

  2. Remember the ID of the company partner record

  3. Create Roleaccount Partner Record

    1. Link to the company partner record

    2. Tag as coming from the BillingEntity resource

    3. Remove the in creation process tag from the company partner record

  4. Return the BillingEntity resource with the ID of the Roleaccount partner record

Update Billing Entity

  1. Update the Roleaccount Partner Record

  2. Update the Company Partner Record