diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..96e08a3
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,6 @@
+[flake8]
+max-line-length = 88
+extend-select = B950
+extend-ignore = E203, E501, E701
+per-file-ignores =
+    __manifest__.py:B018
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..5d60408
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,21 @@
+name: test
+
+on:
+  workflow_dispatch:
+  pull_request:
+  push:
+    branches:
+      - main
+
+jobs:
+  pre-commit:
+    runs-on: ubuntu-22.04
+    steps:
+      - name: Checkout branch
+        uses: actions/checkout@v4
+      - name: Setup Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.8"
+      - name: Run pre-commit hooks
+        uses: pre-commit/action@v3.0.1
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..c33ab45
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,30 @@
+---
+
+repos:
+  - repo: https://github.com/pre-commit/pre-commit-hooks.git
+    rev: v4.5.0
+    hooks:
+      - id: trailing-whitespace
+      - id: mixed-line-ending
+      - id: end-of-file-fixer
+      - id: detect-private-key
+      - id: check-added-large-files
+      - id: check-merge-conflict
+  - repo: https://github.com/crate-ci/typos.git
+    rev: v1.21.0
+    hooks:
+      - id: typos
+  - repo: https://github.com/psf/black.git
+    rev: 24.4.2
+    hooks:
+      - id: black
+  - repo: https://github.com/PyCQA/flake8.git
+    rev: 7.0.0
+    hooks:
+      - id: flake8
+        additional_dependencies:
+          - flake8-bugbear==24.4.26
+  - repo: https://github.com/adrienverge/yamllint.git
+    rev: v1.35.1
+    hooks:
+      - id: yamllint
diff --git a/README.md b/README.md
new file mode 100755
index 0000000..e9499c9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,81 @@
+# OpenStack Integration Addon for Odoo v13
+
+This is an addon to facilitate the integration between OpenStack using Adjutant and Distil with Odoo v13.
+
+These are models and changes based on the Catalystcloud use cases, and based on how we invoice, but likely applicable in some form for other clouds running OpenStack.
+
+## Altered Odoo Models
+
+### Partner
+
+We add quite a few custom fields here that link back to our models elsewhere. We also add the field `stripe_customer_id` to optional link to a customer in Stripe itself for credit card related things.
+
+### Sale Order
+
+The main change to Sale Order is the inclusion of `os_project` on both the Sale Order itself, and the lines. The idea here being that `os_project` on the Sale Order represents the root project for the given project group which the invoice is ultimately for, while `os_project` on each line allows each line to potentially come from a different project in that subgroup in the case of invoice grouping. We also add overrides for `_prepare_invoice` and `_prepare_invoice_line` to handle these fields.
+
+**NOTE**: We also add `os_invoice_date` to deal with a particularly annoying issue around sale order date and how it is handled. But this will be moved to a standalone addon and renamed to `invoice_date` later.
+
+### Account Move
+
+Much like Sale Order, we add `os_project` to both the Account Move (invoice) and Account Move Lines. This is for the same reasons/functionality as in Sale Order.
+
+We also have the function `is_openstack_invoice`, which works off is `os_project` is set or not.
+
+Then there is a custom categorisation function `categorised_openstack_invoice_lines` which sorts things in a way more useful for the PDF summary page.
+
+## Custom Models
+
+The bulk of this addon is custom addons, many of which don't have much logic built in on the odoo side, and most of that logic (for now) is primarily handled external to Odoo in Adjutant and Distil (including the Distil Odoo Writer).
+
+### Project
+
+The core of this addon is built mostly around the OpenStack concept of a project, and this model is meant to represent that Odoo side. A project can only have one `owner` which is the customer partner, but then through an intermediary model called `project_contacts` has relationships to other partners such as billing contacts, etc.
+
+Many other models link back to projects, as they are ultimately the main connection between a customer, and their OpenStack side usage.
+
+### Customer Group
+
+A customer group is a grouping of customers for the purposes of special discounting. The customer group is mostly for special case handling of volume discounts.
+
+While not implemented yet, a group can also be given a pricelist which would then apply to all customers in the group.
+
+### Voucher Code (Not used yet)
+
+These are unused as yet, but their intended function is to allow us to create codes that can be added to an account during signup, or later, which will confer credit, or discounts as tired to the code. They would then turn into a Credit, Grant, or add a customer to a customer group. Their purpose was to be useful for a range of things.
+
+### Credit
+
+Credits are a model representation of credit that an OpenStack Customer has against their invoices. The balance is made up from credit transactions that are attached to the model, which gives us a history of the use of that credit.
+
+Credits by default are not refundable, but for the purposes of prepaid or SLA credit they may have to be, and the credit_type model lets you define if a given type of credit has to be able to be refunded.
+
+### Grant
+
+A grant is like credit, except unlike credits, it doesn't have a balance, just a max value that can be applied to an invoice each month. It is reoccurring credit essentially.
+
+A grant type has a special case field `only_on_group_root` that handles a case where this grant cannot be applied to any project that isn't the root of a invoice project group. This is because when grouping invoices credits/grants are applied to the whole group, and something like a dev grant should never be applied to parent projects, so a dev grant can only affect the project it is applied to or sub projects.
+
+### Volume Discount
+
+These are ranged of volume discount, and they are used as part of invoicing. A customer group may have it's own unique volume discounts, but by default a customer without a customer group will use the volume discounts with a group set.
+
+### Term Discount
+
+A model representing an agreed to term discount.
+
+### Support Subscription (Not used, yet)
+
+A model (and type) to represent a support subscription that a customer has signed up for of a given type.
+
+### Reseller
+
+We have a model for representing a reseller on our cloud, as well has what tier that reseller is at. This handles their discounts, as well as other special cases. A project is optionally linked back to a reseller.
+
+### Trial (Not used)
+
+A model representing a trial signup for OpenStack, and the status of that.
+
+### Referral (Not used)
+
+A basic referral model that would allow customers who provide referrals to earn some credit themselves.
diff --git a/__init__.py b/__init__.py
new file mode 100755
index 0000000..de694df
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from . import controllers
+from . import models
+from . import report
+
+__all__ = ["controllers", "models", "report"]
diff --git a/__manifest__.py b/__manifest__.py
new file mode 100755
index 0000000..18be504
--- /dev/null
+++ b/__manifest__.py
@@ -0,0 +1,55 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+    "name": "OpenStack Integration",
+    "category": "Tools",
+    "summary": """OpenStack integration for the Odoo ERP.""",
+    "version": "14.0.1.0.0",
+    "author": "Catalyst Cloud",
+    "website": "https://catalystcloud.nz",
+    "license": "Other OSI approved licence",
+    "depends": [
+        "account",
+        "base",
+        "product",
+        "report_csv",
+        "sale",
+    ],
+    "data": [
+        "report/openstack_invoice_csv.xml",
+        "security/openstack_security.xml",
+        "security/ir.model.access.csv",
+        "views/main_menu.xml",
+        "views/account_move_view.xml",
+        "views/credit_view.xml",
+        "views/customer_group_view.xml",
+        "views/grant_view.xml",
+        "views/project_view.xml",
+        "views/referral_view.xml",
+        "views/report_invoice.xml",
+        "views/reseller_view.xml",
+        "views/res_partner_view.xml",
+        "views/sale_order_view.xml",
+        "views/support_subscription_view.xml",
+        "views/term_discount_view.xml",
+        "views/trial_view.xml",
+        "views/volume_discount_view.xml",
+        "views/voucher_code_view.xml",
+        # load mail template after report_invoice.xml which it refers to
+        "data/mail_template_data.xml",
+    ],
+    "installable": True,
+}
diff --git a/_typos.toml b/_typos.toml
new file mode 100644
index 0000000..02d3ec1
--- /dev/null
+++ b/_typos.toml
@@ -0,0 +1,2 @@
+[default.extend-words]
+datas = "datas"
diff --git a/controllers/__init__.py b/controllers/__init__.py
new file mode 100755
index 0000000..5dbc015
--- /dev/null
+++ b/controllers/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from . import main
+
+__all__ = ["main"]
diff --git a/controllers/main.py b/controllers/main.py
new file mode 100755
index 0000000..9f85f7f
--- /dev/null
+++ b/controllers/main.py
@@ -0,0 +1,71 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+
+from odoo.http import content_disposition, request, route
+from odoo.tools.safe_eval import safe_eval, time
+
+from odoo.addons.report_csv.controllers import main as report
+
+
+class ReportController(report.ReportController):
+    """report_csv.ReportController that fixes a mysterious permission problem"""
+
+    @route()
+    def report_routes(self, reportname, docids=None, converter=None, **data):
+        if converter == "csv":
+            report = request.env["ir.actions.report"]._get_report_from_name(reportname)
+            context = dict(request.env.context)
+            if docids:
+                docids = [int(i) for i in docids.split(",")]
+            if data.get("options"):
+                data.update(json.loads(data.pop("options")))
+            if data.get("context"):
+                # Ignore 'lang' here, because the context in data is the one
+                # from the webclient *but* if the user explicitly wants to
+                # change the lang, this mechanism overwrites it.
+                data["context"] = json.loads(data["context"])
+                if data["context"].get("lang"):
+                    del data["context"]["lang"]
+                context.update(data["context"])
+            # TODO: Investigate why we need to insert sudo() here
+            csv = report.sudo().with_context(context)._render_csv(docids, data=data)[0]
+            filename = "{}.{}".format(report.name, "csv")
+            if docids:
+                obj = request.env[report.model].browse(docids)
+                if report.print_report_name and not len(obj) > 1:
+                    report_name = safe_eval(
+                        report.print_report_name,
+                        {"object": obj, "time": time, "multi": False},
+                    )
+                    filename = "{}.{}".format(report_name, "csv")
+                # When we print multiple records we still allow a custom
+                # filename.
+                elif report.print_report_name and len(obj) > 1:
+                    report_name = safe_eval(
+                        report.print_report_name,
+                        {"objects": obj, "time": time, "multi": True},
+                    )
+                    filename = "{}.{}".format(report_name, "csv")
+            csvhttpheaders = [
+                ("Content-Type", "text/csv"),
+                ("Content-Length", len(csv)),
+                ("Content-Disposition", content_disposition(filename)),
+            ]
+            return request.make_response(csv, headers=csvhttpheaders)
+        return super(ReportController, self).report_routes(
+            reportname, docids, converter, **data
+        )
diff --git a/data/mail_template_data.xml b/data/mail_template_data.xml
new file mode 100755
index 0000000..37417b2
--- /dev/null
+++ b/data/mail_template_data.xml
@@ -0,0 +1,839 @@
+<?xml version="1.0"?>
+<odoo>
+
+    <!-- This mail template WILL revert when you upgrade the addon:
+         if you want to change it, change this source (or work on a copy) -->
+    <data>
+        <!--Email template -->
+        <record id="email_template_openstack_invoice" model="mail.template">
+            <field name="name">Invoice: Send by email (OpenStack with CSV)</field>
+            <field name="model_id" ref="account.model_account_move"/>
+            <field name="email_from">${(object.invoice_user_id.email_formatted or user.email_formatted) |safe}</field>
+            <field name="partner_to">${object.get_email_to()}</field>
+            <field name="subject">${ctx.subject}</field>
+            <field name="body_html" type="html">
+                <html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
+                % if ctx.email_type == 'credit_card':
+
+                <head>
+                <title>
+                </title>
+                <!--[if !mso]><!-->
+                <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+                <!--<![endif]-->
+                <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+                <meta name="viewport" content="width=device-width, initial-scale=1" />
+                <style type="text/css">
+                    #outlook a {
+                    padding: 0;
+                    }
+
+                    body {
+                    margin: 0;
+                    padding: 0;
+                    -webkit-text-size-adjust: 100%;
+                    -ms-text-size-adjust: 100%;
+                    }
+
+                    table,
+                    td {
+                    border-collapse: collapse;
+                    mso-table-lspace: 0pt;
+                    mso-table-rspace: 0pt;
+                    }
+
+                    img {
+                    border: 0;
+                    height: auto;
+                    line-height: 100%;
+                    outline: none;
+                    text-decoration: none;
+                    -ms-interpolation-mode: bicubic;
+                    }
+
+                    p {
+                    display: block;
+                    margin: 13px 0;
+                    }
+                </style>
+                <!--[if mso]>
+                        <noscript>
+                        <xml>
+                        <o:OfficeDocumentSettings>
+                        <o:AllowPNG/>
+                        <o:PixelsPerInch>96</o:PixelsPerInch>
+                        </o:OfficeDocumentSettings>
+                        </xml>
+                        </noscript>
+                        <![endif]-->
+                <!--[if lte mso 11]>
+                        <style type="text/css">
+                        .mj-outlook-group-fix { width:100% !important; }
+                        </style>
+                        <![endif]-->
+                <!--[if !mso]><!-->
+                <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,500,700" rel="stylesheet" type="text/css" />
+                <style type="text/css">
+                    @import url(https://fonts.googleapis.com/css?family=Montserrat:300,400,500,700);
+                </style>
+                <!--<![endif]-->
+                <style type="text/css">
+                    @media only screen and (min-width:480px) {
+                    .mj-column-per-100 {
+                        width: 100% !important;
+                        max-width: 100%;
+                    }
+
+                    .mj-column-px-500 {
+                        width: 500px !important;
+                        max-width: 500px;
+                    }
+                    }
+                </style>
+                <style media="screen and (min-width:480px)">
+                    .moz-text-html .mj-column-per-100 {
+                    width: 100% !important;
+                    max-width: 100%;
+                    }
+
+                    .moz-text-html .mj-column-px-500 {
+                    width: 500px !important;
+                    max-width: 500px;
+                    }
+                </style>
+                <style type="text/css">
+                    @media only screen and (max-width:480px) {
+                    table.mj-full-width-mobile {
+                        width: 100% !important;
+                    }
+
+                    td.mj-full-width-mobile {
+                        width: auto !important;
+                    }
+                    }
+                </style>
+                </head>
+
+                <body style="word-spacing:normal;background-color:#ffffff;">
+                <div style="background-color:#ffffff;">
+                    <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#fafafa" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="background:#fafafa;background-color:#fafafa;margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#fafafa;background-color:#fafafa;width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+                            <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                                <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                                <tbody>
+                                    <tr>
+                                    <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
+                                        <tbody>
+                                            <tr>
+                                            <td style="width:300px;">
+                                                <a href="https://openstack.org" target="_blank">
+                                                <img height="auto" src="/openstack_integration/static/OpenStack-Logo-Horizontal.svg" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="300" />
+                                                </a>
+                                            </td>
+                                            </tr>
+                                        </tbody>
+                                        </table>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#e1e1e1" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="background:#e1e1e1;background-color:#e1e1e1;margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#e1e1e1;background-color:#e1e1e1;width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:500px;" ><![endif]-->
+                            <div class="mj-column-px-500 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                                <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                                <tbody>
+                                    <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">Thank you for using our cloud</div>
+                                    </td>
+                                    </tr>
+                                    <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:1.3;text-align:left;color:#525252;">The invoice for ${ctx.bill_period} has been processed, and the balance was automatically deducted from your credit card.</div>
+                                    </td>
+                                    </tr>
+                                    <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:1.3;text-align:left;color:#525252;">A detailed breakdown of your usage is on the attached invoice.</div>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#e1e1e1" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="background:#e1e1e1;background-color:#e1e1e1;margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#e1e1e1;background-color:#e1e1e1;width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:500px;" ><![endif]-->
+                            <div class="mj-column-px-500 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                                <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;">
+                                <tbody>
+                                    <tr>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;vertical-align:bottom;">
+                                            <div style="font-family:Montserrat;font-size:16px;font-style:italic;line-height:1;text-align:left;color:#626262;">Customer:</div>
+                                        </td>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;">
+                                            <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">${object.partner_id.name}</div>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;vertical-align:bottom;">
+                                            <div style="font-family:Montserrat;font-size:16px;font-style:italic;line-height:1;text-align:left;color:#626262;">Invoice ID:</div>
+                                        </td>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;">
+                                            <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">${object.name}</div>
+                                        </td>
+                                    </tr>
+                                    % if ctx.project:
+                                    <tr>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;vertical-align:bottom;">
+                                            <div style="font-family:Montserrat;font-size:16px;font-style:italic;line-height:1;text-align:left;color:#626262;">Project name:</div>
+                                        </td>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;">
+                                            <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">${ctx.project.name}</div>
+                                        </td>
+                                    </tr>
+                                    % endif
+                                </tbody>
+                            </table>
+                            <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                                <tbody>
+                                    <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <table cellpadding="0" cellspacing="0" width="100%" border="0" style="color:#000000;font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:22px;table-layout:auto;width:100%;border:none;">
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td>Resource usage during ${ctx.bill_period}</td>
+                                            <td>$${"%.2f" % object.amount_untaxed}</td>
+                                        </tr>
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td>Tax (GST 15%)</td>
+                                            <td>$${"%.2f" % object.amount_tax}</td>
+                                        </tr>
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td>Subtotal</td>
+                                            <td>$${"%.2f" % object.amount_total}</td>
+                                        </tr>
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td style="font-weight: 600;">Amount charged</td>
+                                            <td style="font-weight: 600;">$${"%.2f" % object.amount_total}</td>
+                                        </tr>
+                                        </table>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:500px;" ><![endif]-->
+                            <div class="mj-column-px-500 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                                <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                                <tbody>
+                                    <tr>
+                                    <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:1.3;text-align:center;color:#000000;">OpenStack</div>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><![endif]-->
+                </div>
+                </body>
+
+                % elif ctx.email_type == 'summary':
+
+                <head>
+                <title>
+                </title>
+                <!--[if !mso]><!-->
+                <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+                <!--<![endif]-->
+                <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+                <meta name="viewport" content="width=device-width, initial-scale=1" />
+                <style type="text/css">
+                    #outlook a {
+                    padding: 0;
+                    }
+
+                    body {
+                    margin: 0;
+                    padding: 0;
+                    -webkit-text-size-adjust: 100%;
+                    -ms-text-size-adjust: 100%;
+                    }
+
+                    table,
+                    td {
+                    border-collapse: collapse;
+                    mso-table-lspace: 0pt;
+                    mso-table-rspace: 0pt;
+                    }
+
+                    img {
+                    border: 0;
+                    height: auto;
+                    line-height: 100%;
+                    outline: none;
+                    text-decoration: none;
+                    -ms-interpolation-mode: bicubic;
+                    }
+
+                    p {
+                    display: block;
+                    margin: 13px 0;
+                    }
+                </style>
+                <!--[if mso]>
+                        <noscript>
+                        <xml>
+                        <o:OfficeDocumentSettings>
+                        <o:AllowPNG/>
+                        <o:PixelsPerInch>96</o:PixelsPerInch>
+                        </o:OfficeDocumentSettings>
+                        </xml>
+                        </noscript>
+                        <![endif]-->
+                <!--[if lte mso 11]>
+                        <style type="text/css">
+                        .mj-outlook-group-fix { width:100% !important; }
+                        </style>
+                        <![endif]-->
+                <!--[if !mso]><!-->
+                <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,500,700" rel="stylesheet" type="text/css" />
+                <style type="text/css">
+                    @import url(https://fonts.googleapis.com/css?family=Montserrat:300,400,500,700);
+                </style>
+                <!--<![endif]-->
+                <style type="text/css">
+                    @media only screen and (min-width:480px) {
+                    .mj-column-per-100 {
+                        width: 100% !important;
+                        max-width: 100%;
+                    }
+
+                    .mj-column-px-500 {
+                        width: 500px !important;
+                        max-width: 500px;
+                    }
+                    }
+                </style>
+                <style media="screen and (min-width:480px)">
+                    .moz-text-html .mj-column-per-100 {
+                    width: 100% !important;
+                    max-width: 100%;
+                    }
+
+                    .moz-text-html .mj-column-px-500 {
+                    width: 500px !important;
+                    max-width: 500px;
+                    }
+                </style>
+                <style type="text/css">
+                    @media only screen and (max-width:480px) {
+                    table.mj-full-width-mobile {
+                        width: 100% !important;
+                    }
+
+                    td.mj-full-width-mobile {
+                        width: auto !important;
+                    }
+                    }
+                </style>
+                </head>
+
+                <body style="word-spacing:normal;background-color:#ffffff;">
+                <div style="background-color:#ffffff;">
+                    <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#fafafa" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="background:#fafafa;background-color:#fafafa;margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#fafafa;background-color:#fafafa;width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+                            <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                                <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                                <tbody>
+                                    <tr>
+                                    <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
+                                        <tbody>
+                                            <tr>
+                                            <td style="width:300px;">
+                                                <a href="https://openstack.org" target="_blank">
+                                                <img height="auto" src="/openstack_integration/static/OpenStack-Logo-Horizontal.svg" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="300" />
+                                                </a>
+                                            </td>
+                                            </tr>
+                                        </tbody>
+                                        </table>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#e1e1e1" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="background:#e1e1e1;background-color:#e1e1e1;margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#e1e1e1;background-color:#e1e1e1;width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:500px;" ><![endif]-->
+                            <div class="mj-column-px-500 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                                <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                                <tbody>
+                                    <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">Thank you for using our cloud</div>
+                                    </td>
+                                    </tr>
+                                    <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:1.3;text-align:left;color:#525252;">A summary of your resource usage for ${ctx.bill_period} has been processed.</div>
+                                    </td>
+                                    </tr>
+                                    <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:1.3;text-align:left;color:#525252;">It appears that you have usage, but your project credits or discounts have covered your costs. Attached is a detailed summary of your usage.</div>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#e1e1e1" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="background:#e1e1e1;background-color:#e1e1e1;margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#e1e1e1;background-color:#e1e1e1;width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:500px;" ><![endif]-->
+                            <div class="mj-column-px-500 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                            <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;">
+                                <tbody>
+                                    <tr>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;vertical-align:bottom;">
+                                            <div style="font-family:Montserrat;font-size:16px;font-style:italic;line-height:1;text-align:left;color:#626262;">Customer:</div>
+                                        </td>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;">
+                                            <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">${object.partner_id.name}</div>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;vertical-align:bottom;">
+                                            <div style="font-family:Montserrat;font-size:16px;font-style:italic;line-height:1;text-align:left;color:#626262;">Invoice ID:</div>
+                                        </td>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;">
+                                            <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">${object.name}</div>
+                                        </td>
+                                    </tr>
+                                    % if ctx.project:
+                                    <tr>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;vertical-align:bottom;">
+                                            <div style="font-family:Montserrat;font-size:16px;font-style:italic;line-height:1;text-align:left;color:#626262;">Project name:</div>
+                                        </td>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;">
+                                            <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">${ctx.project.name}</div>
+                                        </td>
+                                    </tr>
+                                    % endif
+                                </tbody>
+                            </table>
+                            <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                            <tbody>
+                                <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <table cellpadding="0" cellspacing="0" width="100%" border="0" style="color:#000000;font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:22px;table-layout:auto;width:100%;border:none;">
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td>Resource usage during ${ctx.bill_period}</td>
+                                            <td>$${"%.2f" % ctx.usage_total}</td>
+                                        </tr>
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td>Free Resources</td>
+                                            <td>$${"%.2f" % ctx.free_resource_total}</td>
+                                        </tr>
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td>Discount and credits</td>
+                                            <td>$${"%.2f" % ctx.discount_total}</td>
+                                        </tr>
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td style="font-weight: 600;">Amount due</td>
+                                            <td style="font-weight: 600;">$${"%.2f" % object.amount_total}</td>
+                                        </tr>
+                                        </table>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:500px;" ><![endif]-->
+                            <div class="mj-column-px-500 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                                <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                                <tbody>
+                                    <tr>
+                                    <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:1.3;text-align:center;color:#000000;">OpenStack</div>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><![endif]-->
+                </div>
+                </body>
+
+
+                % else:
+
+                <head>
+                <title>
+                </title>
+                <!--[if !mso]><!-->
+                <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+                <!--<![endif]-->
+                <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+                <meta name="viewport" content="width=device-width, initial-scale=1" />
+                <style type="text/css">
+                    #outlook a {
+                    padding: 0;
+                    }
+
+                    body {
+                    margin: 0;
+                    padding: 0;
+                    -webkit-text-size-adjust: 100%;
+                    -ms-text-size-adjust: 100%;
+                    }
+
+                    table,
+                    td {
+                    border-collapse: collapse;
+                    mso-table-lspace: 0pt;
+                    mso-table-rspace: 0pt;
+                    }
+
+                    img {
+                    border: 0;
+                    height: auto;
+                    line-height: 100%;
+                    outline: none;
+                    text-decoration: none;
+                    -ms-interpolation-mode: bicubic;
+                    }
+
+                    p {
+                    display: block;
+                    margin: 13px 0;
+                    }
+                </style>
+                <!--[if mso]>
+                        <noscript>
+                        <xml>
+                        <o:OfficeDocumentSettings>
+                        <o:AllowPNG/>
+                        <o:PixelsPerInch>96</o:PixelsPerInch>
+                        </o:OfficeDocumentSettings>
+                        </xml>
+                        </noscript>
+                        <![endif]-->
+                <!--[if lte mso 11]>
+                        <style type="text/css">
+                        .mj-outlook-group-fix { width:100% !important; }
+                        </style>
+                        <![endif]-->
+                <!--[if !mso]><!-->
+                <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,500,700" rel="stylesheet" type="text/css" />
+                <style type="text/css">
+                    @import url(https://fonts.googleapis.com/css?family=Montserrat:300,400,500,700);
+                </style>
+                <!--<![endif]-->
+                <style type="text/css">
+                    @media only screen and (min-width:480px) {
+                    .mj-column-per-100 {
+                        width: 100% !important;
+                        max-width: 100%;
+                    }
+
+                    .mj-column-px-500 {
+                        width: 500px !important;
+                        max-width: 500px;
+                    }
+                    }
+                </style>
+                <style media="screen and (min-width:480px)">
+                    .moz-text-html .mj-column-per-100 {
+                    width: 100% !important;
+                    max-width: 100%;
+                    }
+
+                    .moz-text-html .mj-column-px-500 {
+                    width: 500px !important;
+                    max-width: 500px;
+                    }
+                </style>
+                <style type="text/css">
+                    @media only screen and (max-width:480px) {
+                    table.mj-full-width-mobile {
+                        width: 100% !important;
+                    }
+
+                    td.mj-full-width-mobile {
+                        width: auto !important;
+                    }
+                    }
+                </style>
+                </head>
+
+                <body style="word-spacing:normal;background-color:#212b17;">
+                <div style="background-color:#212b17;">
+                    <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#fafafa" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="background:#fafafa;background-color:#fafafa;margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#fafafa;background-color:#fafafa;width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+                            <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                                <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                                <tbody>
+                                    <tr>
+                                    <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
+                                        <tbody>
+                                            <tr>
+                                            <td style="width:300px;">
+                                                <a href="https://openstack.org" target="_blank">
+                                                <img height="auto" src="/openstack_integration/static/OpenStack-Logo-Horizontal.svg" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="300" />
+                                                </a>
+                                            </td>
+                                            </tr>
+                                        </tbody>
+                                        </table>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#e1e1e1" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="background:#e1e1e1;background-color:#e1e1e1;margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#e1e1e1;background-color:#e1e1e1;width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:500px;" ><![endif]-->
+                            <div class="mj-column-px-500 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                                <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                                <tbody>
+                                    <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">Thank you for using our cloud</div>
+                                    </td>
+                                    </tr>
+                                    <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:1.3;text-align:left;color:#525252;">The invoice for ${ctx.bill_period} has been processed, and is now due for payment.</div>
+                                    </td>
+                                    </tr>
+                                    <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:1.3;text-align:left;color:#525252;">A detailed breakdown of your usage is on the attached invoice, with remittance advice included.</div>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#e1e1e1" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="background:#e1e1e1;background-color:#e1e1e1;margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#e1e1e1;background-color:#e1e1e1;width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:500px;" ><![endif]-->
+                            <div class="mj-column-px-500 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                            <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;">
+                                <tbody>
+                                    <tr>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;vertical-align:bottom;">
+                                            <div style="font-family:Montserrat;font-size:16px;font-style:italic;line-height:1;text-align:left;color:#626262;">Customer:</div>
+                                        </td>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;">
+                                            <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">${object.partner_id.name}</div>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;vertical-align:bottom;">
+                                            <div style="font-family:Montserrat;font-size:16px;font-style:italic;line-height:1;text-align:left;color:#626262;">Invoice ID:</div>
+                                        </td>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;">
+                                            <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">${object.name}</div>
+                                        </td>
+                                    </tr>
+                                    % if ctx.project:
+                                    <tr>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;vertical-align:bottom;">
+                                            <div style="font-family:Montserrat;font-size:16px;font-style:italic;line-height:1;text-align:left;color:#626262;">Project name:</div>
+                                        </td>
+                                        <td align="left" style="font-size:0px;padding:3px 25px;word-break:break-word;">
+                                            <div style="font-family:Montserrat;font-size:20px;font-style:italic;line-height:1;text-align:left;color:#626262;">${ctx.project.name}</div>
+                                        </td>
+                                    </tr>
+                                    % endif
+                                </tbody>
+                            </table>
+                            <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                                <tbody>
+                                    <tr>
+                                    <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <table cellpadding="0" cellspacing="0" width="100%" border="0" style="color:#000000;font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:22px;table-layout:auto;width:100%;border:none;">
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td>Resource usage during ${ctx.bill_period}</td>
+                                            <td>$${"%.2f" % object.amount_untaxed}</td>
+                                        </tr>
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td>Tax (GST 15%)</td>
+                                            <td>$${"%.2f" % object.amount_tax}</td>
+                                        </tr>
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td>Subtotal</td>
+                                            <td>$${"%.2f" % object.amount_total}</td>
+                                        </tr>
+                                        <tr style="text-align:left;padding:15px 0;">
+                                            <td style="font-weight: 600;">Amount due</td>
+                                            <td style="font-weight: 600;">$${"%.2f" % object.amount_total}</td>
+                                        </tr>
+                                        </table>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+                    <div style="margin:0px auto;max-width:600px;">
+                    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
+                        <tbody>
+                        <tr>
+                            <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
+                            <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:500px;" ><![endif]-->
+                            <div class="mj-column-px-500 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
+                                <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
+                                <tbody>
+                                    <tr>
+                                    <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
+                                        <div style="font-family:Montserrat, Helvetica, Arial, sans-serif;font-size:13px;line-height:1.3;text-align:center;color:#000000;">OpenStack</div>
+                                    </td>
+                                    </tr>
+                                </tbody>
+                                </table>
+                            </div>
+                            <!--[if mso | IE]></td></tr></table><![endif]-->
+                            </td>
+                        </tr>
+                        </tbody>
+                    </table>
+                    </div>
+                    <!--[if mso | IE]></td></tr></table><![endif]-->
+                </div>
+                </body>
+
+                % endif
+                </html>
+
+
+
+            </field>
+            <field name="report_template" ref="openstack_integration.account_invoices"/>
+            <field name="report_name">Invoice_${(object.name or '').replace('/','_')}${object.state == 'draft' and '_draft' or ''}</field>
+            <field name="lang">${object.partner_id.lang}</field>
+            <field name="auto_delete" eval="True"/>
+        </record>
+    </data>
+</odoo>
diff --git a/models/__init__.py b/models/__init__.py
new file mode 100755
index 0000000..638a314
--- /dev/null
+++ b/models/__init__.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from . import account_move
+from . import credit
+from . import customer_group
+from . import grant
+from . import project
+from . import referral
+from . import reseller
+from . import res_partner
+from . import sale_order
+from . import support_subscription
+from . import term_discount
+from . import trial
+from . import volume_discount
+from . import voucher_codes
+
+__all__ = [
+    "account_move",
+    "credit",
+    "customer_group",
+    "grant",
+    "project",
+    "referral",
+    "reseller",
+    "res_partner",
+    "sale_order",
+    "support_subscription",
+    "term_discount",
+    "trial",
+    "volume_discount",
+    "voucher_codes",
+]
diff --git a/models/account_move.py b/models/account_move.py
new file mode 100755
index 0000000..18cf070
--- /dev/null
+++ b/models/account_move.py
@@ -0,0 +1,280 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import base64
+import datetime
+
+from odoo import fields, models
+
+
+def credit_and_debit(input, old_credit=0, old_debit=0):
+    if input < 0:
+        return old_credit + input, old_debit
+    return old_credit, old_debit + input
+
+
+class InvoiceRegionCategory:
+    def __init__(self):
+        self.lines = []
+        self.count = 0
+        self.credit = 0
+        self.debit = 0
+        self.name = ""
+
+    def total(self):
+        return self.credit + self.debit
+
+    def add_region(self, line):
+        self.name = line.product_id.categ_id.parent_id["name"]
+        self.credit, self.debit = credit_and_debit(
+            line.price_subtotal, self.credit, self.debit
+        )
+        self.count += 1
+
+        self.lines.append(line)
+
+
+class InvoiceProductCategory:
+    def __init__(self):
+        self.regions = {}
+        self.count = 0
+        self.credit = 0
+        self.debit = 0
+        self.name = ""
+        self.uncategorised = []
+
+    def total(self):
+        return self.credit + self.debit
+
+    def add_product(self, line):
+        product = line.product_id
+        self.credit, self.debit = credit_and_debit(
+            line.price_subtotal, self.credit, self.debit
+        )
+        self.count += 1
+
+        if product and product.categ_id and product.categ_id.parent_id:
+            self.name = product.categ_id["name"]
+            region_name = product.categ_id.parent_id["name"]
+            try:
+                region_group = self.regions[region_name]
+                region_group.add_region(line)
+            except KeyError:
+                self.regions[region_name] = InvoiceRegionCategory()
+                self.regions[region_name].add_region(line)
+        else:
+            self.uncategorised.append(line)
+
+
+class CategorisedInvoice:
+    def __init__(self):
+        self.products = {}
+        self.count = 0
+        self.credit = 0
+        self.debit = 0
+        self.uncategorised = []
+
+    def total(self):
+        return self.credit + self.debit
+
+    def add_line(self, line):
+        product = line.product_id
+        self.credit, self.debit = credit_and_debit(
+            line.price_subtotal, self.credit, self.debit
+        )
+        self.count += 1
+
+        if product and product.categ_id:
+            product_name = product.categ_id["name"]
+            try:
+                product_group = self.products[product_name]
+                product_group.add_product(line)
+            except KeyError:
+                self.products[product_name] = InvoiceProductCategory()
+                self.products[product_name].add_product(line)
+        else:
+            self.uncategorised.append(line)
+
+
+class OutstandingInvoices:
+    def __init__(self):
+        self.old_invoices = []
+        self.current_invoices = []
+
+    def _reset(self):
+        # use instead of reinit OutStandingInvoices - report_invoice.xml template is
+        # already tracking this object. But if we re-run invoicing in quick
+        # succession without resetting these, the contents will be doubled up.
+        # It's probably cursed.
+        self.old_invoices = []
+        self.current_invoices = []
+
+    def ordered_old_invoices(self):
+        return sorted(self.old_invoices, key=lambda x: x["invoice_date_due"])
+
+    def ordered_current_invoices(self):
+        return sorted(self.current_invoices, key=lambda x: x["invoice_date_due"])
+
+    def total_owing(self):
+        return self.old_owing() + self.current_owing()
+
+    def old_owing(self):
+        sum = 0
+
+        for inv in self.old_invoices:
+            sum = sum + inv["amount_residual_signed"]
+        return sum
+
+    def current_owing(self):
+        sum = 0
+        for inv in self.current_invoices:
+            sum = sum + inv["amount_residual_signed"]
+        return sum
+
+    def add(self, invoices):
+        for invoice in invoices:
+            # temp dict to prevent attempting deeper calls on the object when
+            # connection has closed just pulls the relevant values and stores them
+            # for recall later.
+            temp_invoice = {
+                "invoice_date_due": invoice.invoice_date_due,
+                "amount_residual_signed": invoice.amount_residual_signed,
+                "name": invoice.name,
+            }
+
+            if datetime.date.today() > invoice.invoice_date_due:
+                self.old_invoices.append(temp_invoice)
+            else:
+                self.current_invoices.append(temp_invoice)
+
+
+class AccountMove(models.Model):
+    _inherit = "account.move"
+    outstanding_invoices = OutstandingInvoices()
+
+    os_project = fields.Many2one(
+        comodel_name="openstack.project",
+        string="OpenStack Project",
+    )
+
+    os_is_cloud_framework_agreement_invoice = fields.Boolean(
+        string="CFA invoice",
+        default=False,
+        required=False,
+    )
+
+    def is_openstack_invoice(self):
+        """Return whether this is an OpenStack invoice."""
+
+        return bool(self.os_project)
+
+    def categorised_openstack_invoice_lines(self):
+        """Group invoice lines by project and categorise"""
+
+        categorised_invoice = CategorisedInvoice()
+
+        for line in self.invoice_line_ids:
+            categorised_invoice.add_line(line)
+        return categorised_invoice
+
+    def get_outstanding_invoices(self, project_id, partner_id):
+        if not project_id:
+            return self.env["account.move"].search(
+                [
+                    ("amount_residual_signed", ">", 0),
+                    ("partner_id.id", "=", partner_id["id"]),
+                ]
+            )
+
+        return self.env["account.move"].search(
+            [
+                ("amount_residual_signed", ">", 0),
+                ("os_project.id", "=", project_id["id"]),
+            ]
+        )
+
+    def send_openstack_invoice_email(self, email_ctx=None):
+        # reset before, to ensure data is current
+        self.outstanding_invoices._reset()
+        try:
+            if email_ctx is None:
+                email_ctx = {}
+            template_id = self.env.ref(
+                "openstack_integration.email_template_openstack_invoice"
+            ).with_context(**email_ctx)
+            for invoice in self:
+                if invoice["is_move_sent"]:
+                    continue
+                outstanding = self.get_outstanding_invoices(
+                    invoice.os_project, invoice.partner_id
+                )
+                for inv in outstanding:
+                    self.outstanding_invoices.add(inv)
+                csv_data = self.env.ref(
+                    "openstack_integration.openstack_invoice_csv"
+                )._render_csv([invoice.id], False)[0]
+                csv_data_encoded = base64.encodebytes(csv_data.encode("utf-8"))
+                invoice_name = (invoice.state == "posted") and (
+                    (invoice.name or "INV").replace("/", "_")
+                )
+                filename = "Detail_{}.csv".format(invoice_name)
+                attachment = {
+                    "name": filename,
+                    "datas": csv_data_encoded,
+                    "store_fname": filename,
+                    "res_model": "account.move",
+                    "type": "binary",  # "binary" just means file, as opposed to "url"
+                    "res_id": invoice.id,
+                    "mimetype": "text/csv",
+                }
+                attachment_id = self.env["ir.attachment"].create(attachment)
+                # Set additional attachment, send, remove attachments again
+                template_id.attachment_ids = [(6, 0, [attachment_id.id])]
+                template_id.send_mail(invoice.id)
+                template_id.attachment_ids = False
+
+                invoice.write({"is_move_sent": True})
+        finally:
+            # Reset after - avoids manually generated Statement Of Accounts issues
+            self.outstanding_invoices._reset()
+
+
+class AccountMoveLine(models.Model):
+    _inherit = "account.move.line"
+
+    os_project = fields.Many2one(
+        comodel_name="openstack.project",
+        string="OpenStack Project",
+    )
+
+    os_region = fields.Text(
+        string="OS Region",
+        required=False,
+    )
+
+    os_resource_type = fields.Text(
+        string="OS Resource Type",
+        required=False,
+    )
+
+    os_resource_name = fields.Text(
+        string="OS Resource Name",
+        required=False,
+    )
+
+    os_resource_id = fields.Text(
+        string="OS Resource ID",
+        required=False,
+    )
diff --git a/models/credit.py b/models/credit.py
new file mode 100755
index 0000000..74009e9
--- /dev/null
+++ b/models/credit.py
@@ -0,0 +1,181 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import date
+
+from odoo import _, api, fields, models, exceptions
+
+
+class OpenStackCredit(models.Model):
+    """OpenStack credit which is attached to a project"""
+
+    _name = "openstack.credit"
+    _description = "OpenStack Credit"
+
+    voucher_code = fields.Many2one(
+        comodel_name="openstack.voucher_code", string="Voucher Code"
+    )
+
+    project = fields.Many2one(
+        comodel_name="openstack.project",
+        string="Project",
+        required=True,
+    )
+
+    @api.depends("project", "voucher_code")
+    def _get_name(self):
+        """Computed field to show in the breadcrumbs"""
+        for credit in self:
+            name = credit.project.name
+            if credit.voucher_code:
+                name = f"{credit.project.name} - {credit.voucher_code.name}"
+            credit.name = name
+
+    name = fields.Char(string="Name", compute="_get_name")
+
+    credit_type = fields.Many2one(
+        comodel_name="openstack.credit.type",
+        string="Credit Type",
+        required=True,
+    )
+
+    start_date = fields.Date(string="Start Date", required=True, default=date.today())
+    expiry_date = fields.Date(string="Expiry Date")
+
+    initial_balance = fields.Float(string="Initial Balance", required=True)
+
+    current_balance = fields.Float(string="Current Balance", compute="_compute_balance")
+
+    transactions = fields.One2many(
+        comodel_name="openstack.credit.transaction",
+        inverse_name="credit",
+        string="Credit Transactions",
+    )
+
+    @api.depends("initial_balance")
+    def _compute_balance(self):
+        for record in self:
+            transactions = self.env["openstack.credit.transaction"].search(
+                [("credit", "=", record.id)]
+            )
+            if not transactions:
+                record.current_balance = record.initial_balance
+                continue
+            record.current_balance = record.initial_balance + sum(
+                [t.value for t in transactions]
+            )
+
+    @api.constrains("start_date", "expiry_date")
+    def _check_expiry_date(self):
+        """Constrain start date
+
+        Start date must always be before the expiry date.
+        """
+        if self.expiry_date and self.start_date > self.expiry_date:
+            raise exceptions.ValidationError(
+                _("Expiry date must be after the start date")
+            )
+
+    @property
+    def is_active(self):
+        """Return whether this credit has started and not expired"""
+        if date.today() < self.start_date:
+            return False
+        return self.expiry_date is None or date.today() < self.expiry_date
+
+    @property
+    def available_balance(self):
+        """Return current balance or 0.0 if the credit is not active
+
+        That is, if the credit has not yet started or has expired.
+        """
+        if not self.is_active:
+            return 0.0
+        else:
+            return self.current_balance
+
+
+class OpenStackCreditTransaction(models.Model):
+    """Credit transaction attached to Credit instances"""
+
+    _name = "openstack.credit.transaction"
+    _description = "OpenStack Credit Transaction"
+
+    credit = fields.Many2one(
+        comodel_name="openstack.credit",
+        string="Credit",
+        required=True,
+    )
+    description = fields.Char(string="Description")
+    value = fields.Float(string="Value", required=True)
+
+    @api.constrains("credit", "value")
+    def _check_value(self):
+        """Constraint value
+
+        Current balance on credit can never go below zero.
+        """
+        transactions = self.env["openstack.credit.transaction"].search(
+            [("credit", "=", self.credit.id), ("id", "!=", self.id)]
+        )
+        if not transactions:
+            return
+
+        current_balance = self.credit.initial_balance + sum(
+            [t.value for t in transactions]
+        )
+        if (current_balance + self.value) < 0:
+            if abs(current_balance + self.value) < 0.01:
+                # If the diff is this small, just make it zero.
+                self.value = -current_balance
+            else:
+                raise exceptions.ValidationError(
+                    _("Cannot add transaction that brings balance below zero.")
+                )
+
+
+class OpenStackCreditType(models.Model):
+    """Credit types attached to each Credit instance"""
+
+    _name = "openstack.credit.type"
+    _description = "OpenStack Credit Type"
+
+    name = fields.Char(string="Name", required=True)
+    refundable = fields.Boolean(string="Refundable", default=False)
+
+    product = fields.Many2one(
+        comodel_name="product.product", string="Product", required=True
+    )
+    credits = fields.One2many(
+        comodel_name="openstack.credit", inverse_name="credit_type"
+    )
+
+    only_for_products = fields.Many2many(
+        "product.product",
+        string="Only for these products",
+        help=(
+            "This credit type can only apply to these products. "
+            "This is mutually inclusive with `only_for_product_categories`."
+        ),
+    )
+
+    only_for_product_categories = fields.Many2many(
+        "product.category",
+        string="Only for these product categories",
+        help=(
+            "This credit type can only apply to these product categories. "
+            "This is mutually inclusive with `only_for_products`."
+        ),
+    )
diff --git a/models/customer_group.py b/models/customer_group.py
new file mode 100755
index 0000000..603c8e6
--- /dev/null
+++ b/models/customer_group.py
@@ -0,0 +1,29 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from odoo import fields, models
+
+
+class OpenStackCustomerGroup(models.Model):
+    """ """
+
+    _name = "openstack.customer_group"
+    _description = "OpenStack customer groups."
+
+    name = fields.Char(string="Customer group name", required=True)
+    partners = fields.One2many(
+        comodel_name="res.partner", inverse_name="os_customer_group"
+    )
+    pricelist = fields.Many2one(comodel_name="product.pricelist", string="Pricelist")
diff --git a/models/grant.py b/models/grant.py
new file mode 100755
index 0000000..fba2871
--- /dev/null
+++ b/models/grant.py
@@ -0,0 +1,123 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import date
+
+from odoo import _, api, fields, models, exceptions
+
+
+class OpenStackGrant(models.Model):
+    """OpenStack grant which is attached to a project"""
+
+    _name = "openstack.grant"
+    _description = "Cloud Grant"
+
+    voucher_code = fields.Many2one(
+        comodel_name="openstack.voucher_code",
+        string="Voucher Code",
+    )
+
+    project = fields.Many2one(
+        comodel_name="openstack.project",
+        string="Project",
+        required=True,
+    )
+
+    @api.depends("project", "voucher_code")
+    def _get_name(self):
+        """Computed field to show in the breadcrumbs"""
+        for grant in self:
+            name = grant.project.name
+            if grant.voucher_code:
+                name = f"{grant.project.name} - {grant.voucher_code.name}"
+            grant.name = name
+
+    name = fields.Char(string="Name", compute="_get_name")
+
+    grant_type = fields.Many2one(
+        comodel_name="openstack.grant.type",
+        string="Grant Type",
+        required=True,
+    )
+
+    start_date = fields.Date(string="Start Date", required=True, default=date.today())
+    expiry_date = fields.Date(string="Expiry Date")
+
+    value = fields.Float(string="Value", required=True)
+
+    @api.constrains("start_date", "expiry_date")
+    def _check_expiry_date(self):
+        """Constrain start date
+
+        Start date must always be before the expiry date.
+        """
+        if self.expiry_date and self.start_date > self.expiry_date:
+            raise exceptions.ValidationError(
+                _("Expiry date must be after the start date")
+            )
+
+    @api.constrains("value")
+    def _check_value_is_positive(self):
+        """Constrain value"""
+        if self.value < 0.0:
+            raise exceptions.ValidationError(_("value cannot be negative"))
+
+    def is_active(self, today=None):
+        """Return whether this grant has started and not expired"""
+        if today is None:
+            today = date.today()
+        if today < self.start_date:
+            return False
+        return self.expiry_date is None or today < self.expiry_date
+
+
+class OpenStackGrantType(models.Model):
+    """Grant types attached to each grant instance"""
+
+    _name = "openstack.grant.type"
+    _description = "OpenStack Grant Type"
+
+    name = fields.Char(string="Name", required=True)
+    product = fields.Many2one(
+        comodel_name="product.product", string="Product", required=True
+    )
+    grants = fields.One2many(comodel_name="openstack.grant", inverse_name="grant_type")
+
+    only_on_group_root = fields.Boolean(
+        string="Only on group root",
+        help=(
+            "If true, this grant type is only allowed to be part of an invoice "
+            "grouping if it is on the group root project."
+        ),
+        default=False,
+    )
+
+    only_for_products = fields.Many2many(
+        "product.product",
+        string="Only for these products",
+        help=(
+            "This grant type can only apply to these products. "
+            "This is mutually inclusive with `only_for_product_categories`."
+        ),
+    )
+
+    only_for_product_categories = fields.Many2many(
+        "product.category",
+        string="Only for these product categories",
+        help=(
+            "This grant type can only apply to these product categories. "
+            "This is mutually inclusive with `only_for_products`."
+        ),
+    )
diff --git a/models/project.py b/models/project.py
new file mode 100755
index 0000000..98d85cd
--- /dev/null
+++ b/models/project.py
@@ -0,0 +1,124 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from odoo import api, fields, models
+
+
+class OpenStackProject(models.Model):
+    """A Project, attached to partner"""
+
+    _name = "openstack.project"
+    _description = "OpenStack Project"
+    _rec_name = "display_name"
+    _inherit = ["mail.thread"]
+
+    name = fields.Char(string="Project Name", required=True)
+    os_id = fields.Char(
+        string="Project ID",
+        required=True,
+        unique=True,
+    )
+
+    @api.depends("name", "os_id")
+    def _get_name(self):
+        """Computed field to show in the breadcrumbs"""
+        for project in self:
+            project.display_name = f"{project.name} ({project.os_id})"
+
+    display_name = fields.Char(string="Display Name", compute="_get_name", store=True)
+
+    parent = fields.Many2one(comodel_name="openstack.project", string="Parent Project")
+    enabled = fields.Boolean(string="Enabled", default=True)
+
+    billing_type = fields.Selection(
+        selection=[
+            ("customer", "customer"),
+            ("internal", "internal"),
+        ],
+        default="customer",
+        string="Billing Type",
+        required=True,
+    )
+
+    group_invoices = fields.Boolean(string="Group Invoices")
+
+    payment_method = fields.Selection(
+        selection=[("credit_card", "credit_card"), ("invoice", "invoice")],
+        default="invoice",
+        string="Payment Method",
+        required=True,
+    )
+    stripe_card_id = fields.Char("Stripe Card ID", required=False)
+    po_number = fields.Char(string="PO Number")
+    override_po_number = fields.Boolean(string="Override PO Number")
+
+    owner = fields.Many2one(
+        comodel_name="res.partner", string="Owner partner", required=True
+    )
+    project_contacts = fields.One2many(
+        comodel_name="openstack.project_contact",
+        inverse_name="project",
+        string="Project Contacts",
+    )
+
+    project_credits = fields.One2many(
+        comodel_name="openstack.credit",
+        inverse_name="project",
+        string="Credits",
+    )
+    project_grants = fields.One2many(
+        comodel_name="openstack.grant",
+        inverse_name="project",
+        string="Grants",
+    )
+    term_discounts = fields.One2many(
+        comodel_name="openstack.term_discount",
+        inverse_name="project",
+        string="Term Discounts",
+    )
+
+    support_subscription = fields.Many2one(
+        comodel_name="openstack.support_subscription",
+        string="Premium Support Subscription",
+    )
+
+
+class OpenStackProjectContacts(models.Model):
+    """Bridge table for openstack project and a partner"""
+
+    _name = "openstack.project_contact"
+    _description = "OpenStack Project Contacts bridge table"
+
+    project = fields.Many2one(comodel_name="openstack.project", string="Project")
+    partner = fields.Many2one(
+        comodel_name="res.partner", string="Partner", required=True
+    )
+    inherit = fields.Boolean(
+        string="Inherit to sub-projects",
+        default=False,
+    )
+
+    contact_type = fields.Selection(
+        selection=[
+            ("primary", "primary"),
+            ("billing", "billing"),
+            ("technical", "technical"),
+            ("legal", "legal"),
+            # TODO(adriant): REMOVE RESELLER LATER:
+            ("reseller customer", "reseller customer"),
+        ],
+        string="Contact Type",
+        required=True,
+    )
diff --git a/models/referral.py b/models/referral.py
new file mode 100755
index 0000000..48b3cd1
--- /dev/null
+++ b/models/referral.py
@@ -0,0 +1,80 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from odoo import api, fields, models
+
+
+class OpenStackReferralCode(models.Model):
+    _name = "openstack.referral_code"
+    _description = "OpenStack referral code."
+
+    partner = fields.Many2one(
+        comodel_name="res.partner",
+        string="Partner",
+        required=True,
+    )
+    code = fields.Char("Code", required=True, unique=True)
+
+    @api.depends("partner", "code")
+    def _get_name(self):
+        """Computed field to show in the breadcrumbs"""
+        for ref in self:
+            ref.name = f"{ref.partner.name} - {ref.code}"
+
+    name = fields.Char("Name", compute="_get_name")
+
+    referral_credit_amount = fields.Float(
+        string="Referral credit initial balance",
+    )
+    referral_credit_type = fields.Many2one(
+        comodel_name="openstack.credit.type",
+        string="Referral credit Type",
+        required=True,
+    )
+    referral_credit_duration = fields.Integer(
+        string="Referral credit duration in days",
+        required=True,
+    )
+
+    before_reward_usage_threshold = fields.Float(
+        string="Before reward usage threshold",
+        required=True,
+    )
+
+    reward_credit_amount = fields.Float(
+        string="Reward credit initial balance",
+        required=True,
+    )
+    reward_credit_type = fields.Many2one(
+        comodel_name="openstack.credit.type",
+        string="Reward credit Type",
+        required=True,
+    )
+    reward_credit_duration = fields.Integer(
+        string="Reward credit duration in days",
+        required=True,
+    )
+
+    allowed_uses = fields.Integer(
+        string="Number of allowed uses of this code. Default: -1 (infinite)",
+        default=-1,
+        required=True,
+    )
+
+    referrals = fields.One2many(
+        comodel_name="res.partner",
+        inverse_name="os_referral",
+        string="Referrals using this code.",
+    )
diff --git a/models/res_partner.py b/models/res_partner.py
new file mode 100755
index 0000000..eee7534
--- /dev/null
+++ b/models/res_partner.py
@@ -0,0 +1,88 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from odoo import fields, models
+
+
+class ResPartner(models.Model):
+    """Add some fields related to OpenStack"""
+
+    _inherit = "res.partner"
+
+    stripe_customer_id = fields.Char(
+        string="Stripe Customer ID",
+        copy=False,
+        groups="openstack_integration.group_openstack_user,"
+        "openstack_integration.group_openstack_manager",
+    )
+    os_projects = fields.One2many(
+        comodel_name="openstack.project",
+        inverse_name="owner",
+        copy=False,
+        groups="openstack_integration.group_openstack_user,"
+        "openstack_integration.group_openstack_manager",
+    )
+    os_project_contacts = fields.One2many(
+        comodel_name="openstack.project_contact",
+        inverse_name="partner",
+        copy=False,
+        groups="openstack_integration.group_openstack_user,"
+        "openstack_integration.group_openstack_manager",
+    )
+    os_customer_group = fields.Many2one(
+        comodel_name="openstack.customer_group",
+        string="OpenStack Customer Group",
+        copy=False,
+        groups="openstack_integration.group_openstack_user,"
+        "openstack_integration.group_openstack_manager",
+    )
+    os_reseller = fields.Many2one(
+        comodel_name="openstack.reseller",
+        string="OpenStack Reseller",
+        copy=False,
+        groups="openstack_integration.group_openstack_user,"
+        "openstack_integration.group_openstack_manager",
+    )
+    os_trial = fields.Many2one(
+        comodel_name="openstack.trial",
+        string="OpenStack trial on signup.",
+        copy=False,
+        groups="openstack_integration.group_openstack_user,"
+        "openstack_integration.group_openstack_manager",
+    )
+    os_referral = fields.Many2one(
+        comodel_name="openstack.referral_code",
+        string="OpenStack referral on signup.",
+        copy=False,
+        groups="openstack_integration.group_openstack_user,"
+        "openstack_integration.group_openstack_manager",
+    )
+    os_referral_codes = fields.One2many(
+        comodel_name="openstack.referral_code",
+        inverse_name="partner",
+        string="OpenStack Referral codes",
+        copy=False,
+        groups="openstack_integration.group_openstack_user,"
+        "openstack_integration.group_openstack_manager",
+    )
+    os_is_cloud_framework_agreement_partner = fields.Boolean(
+        string="CFA partner",
+        default=False,
+        required=False,
+        groups=(
+            "openstack_integration.group_openstack_user,"
+            "openstack_integration.group_openstack_manager"
+        ),
+    )
diff --git a/models/reseller.py b/models/reseller.py
new file mode 100755
index 0000000..e486d32
--- /dev/null
+++ b/models/reseller.py
@@ -0,0 +1,110 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from odoo import _, api, fields, models, exceptions
+
+
+class OpenStackReseller(models.Model):
+    """A customer of OpenStack who has signed as a reseller."""
+
+    _name = "openstack.reseller"
+    _description = "OpenStack Reseller "
+
+    partner = fields.Many2one(
+        comodel_name="res.partner",
+        string="Reseller partner",
+        required=True,
+    )
+
+    @api.depends("partner")
+    def _get_name(self):
+        """Computed field to show in the breadcrumbs"""
+        for reseller in self:
+            reseller.name = reseller.partner.name
+
+    name = fields.Char(string="Name", compute="_get_name")
+
+    tier = fields.Many2one(
+        "openstack.reseller.tier",
+        "Reseller tier",
+        required=True,
+    )
+    hide_billing = fields.Boolean(
+        string="Hide billing",
+        default=False,
+    )
+    alternative_billing_url = fields.Char(
+        string="Alternative billing url",
+    )
+    hide_support = fields.Boolean(
+        string="Hide support",
+        default=False,
+    )
+    alternative_support_url = fields.Char(
+        string="Alternative support url",
+    )
+
+    demo_project = fields.Many2one(
+        comodel_name="openstack.project",
+        string="Demo tenant",
+    )
+
+
+class OpenStackResellerTier(models.Model):
+    _name = "openstack.reseller.tier"
+    _description = "Reseller tier"
+
+    name = fields.Char(
+        string="Tier name",
+        required=True,
+    )
+
+    min_usage_threshold = fields.Float(
+        string="Required usage amount for tier.",
+        required=True,
+    )
+
+    discount_percent = fields.Float(
+        string="Discount percentage for this range (0 to 100)",
+        required=True,
+    )
+    discount_product = fields.Many2one(
+        comodel_name="product.product",
+        string="Discount product",
+        required=True,
+    )
+
+    free_support_hours = fields.Integer(
+        string="Free support hours per month",
+        required=True,
+    )
+
+    free_monthly_credit = fields.Float(
+        string="Free monthly demo credit",
+        required=True,
+    )
+    free_monthly_credit_product = fields.Many2one(
+        comodel_name="product.product",
+        string="Free monthly credit product",
+        required=True,
+    )
+
+    @api.constrains("discount_percent")
+    def _check_discount_percent(self):
+        """Constrain discount to 0-100"""
+        if self.discount_percent < 0.0 or self.discount_percent > 100.0:
+            raise exceptions.ValidationError(
+                _("discount_percent must be between 0-100")
+            )
diff --git a/models/sale_order.py b/models/sale_order.py
new file mode 100755
index 0000000..1f9b388
--- /dev/null
+++ b/models/sale_order.py
@@ -0,0 +1,115 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from odoo import fields, models
+
+
+class SaleOrder(models.Model):
+    _inherit = "sale.order"
+
+    os_project = fields.Many2one(
+        comodel_name="openstack.project",
+        string="OpenStack Project",
+    )
+
+    # NOTE(adriant): because odoo is stupid...
+    # NOTE(TODO): move this into standalone addon
+    os_invoice_date = fields.Date(
+        string="Invoice Date",
+        required=False,
+    )
+
+    os_invoice_due_date = fields.Date(
+        string="Invoice Due Date",
+        required=False,
+    )
+
+    os_is_cloud_framework_agreement_sale_order = fields.Boolean(
+        string="CFA Sale Order",
+        default=False,
+        required=False,
+    )
+
+    def create_invoices(self):
+        sale_order_ids = [s.id for s in self]
+        payment = self.env["sale.advance.payment.inv"].create(
+            {"advance_payment_method": "delivered"}
+        )
+        payment.with_context(active_ids=sale_order_ids).create_invoices()
+
+    def _prepare_invoice(self):
+        self.ensure_one()
+        invoice_vals = super(SaleOrder, self)._prepare_invoice()
+        invoice_vals["os_project"] = self.os_project.id
+        if self.os_invoice_date:
+            invoice_vals["invoice_date"] = self.os_invoice_date
+        if self.os_invoice_due_date:
+            invoice_vals["invoice_date_due"] = self.os_invoice_due_date
+        invoice_vals["os_is_cloud_framework_agreement_invoice"] = False
+        if self.os_is_cloud_framework_agreement_sale_order:
+            invoice_vals["os_is_cloud_framework_agreement_invoice"] = (
+                self.os_is_cloud_framework_agreement_sale_order
+            )
+        return invoice_vals
+
+
+class SaleOrderLine(models.Model):
+    _inherit = "sale.order.line"
+
+    os_project = fields.Many2one(
+        comodel_name="openstack.project",
+        string="OpenStack Project",
+    )
+
+    os_region = fields.Text(
+        string="OS Region",
+        required=False,
+    )
+
+    os_resource_type = fields.Text(
+        string="OS Resource Type",
+        required=False,
+    )
+
+    os_resource_name = fields.Text(
+        string="OS Resource Name",
+        required=False,
+    )
+
+    os_resource_id = fields.Text(
+        string="OS Resource ID",
+        required=False,
+    )
+
+    def _prepare_invoice_line(self, **optional_values):
+        self.ensure_one()
+        invoice_line_vals = super(SaleOrderLine, self)._prepare_invoice_line()
+        invoice_line_vals["os_project"] = self.os_project.id
+
+        invoice_line_vals["os_region"] = "NZ"
+        invoice_line_vals["os_resource_type"] = ""
+        invoice_line_vals["os_resource_name"] = ""
+        invoice_line_vals["os_resource_id"] = ""
+
+        if self.os_region:
+            invoice_line_vals["os_region"] = self.os_region
+        if self.os_resource_type:
+            invoice_line_vals["os_resource_type"] = self.os_resource_type
+        if self.os_resource_name:
+            invoice_line_vals["os_resource_name"] = self.os_resource_name
+        if self.os_resource_id:
+            invoice_line_vals["os_resource_id"] = self.os_resource_id
+
+        return invoice_line_vals
diff --git a/models/support_subscription.py b/models/support_subscription.py
new file mode 100755
index 0000000..419d73c
--- /dev/null
+++ b/models/support_subscription.py
@@ -0,0 +1,111 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import date
+
+from odoo import _, api, fields, models, exceptions
+
+
+class OpenStackSupportSubscription(models.Model):
+    """An openstack support subscription
+
+    Attached to a partner or optionally a project."""
+
+    _name = "openstack.support_subscription"
+    _description = "OpenStack Support Subscription"
+    _rec_name = "project"
+
+    project = fields.Many2one(
+        comodel_name="openstack.project",
+        string="Project",
+    )
+
+    partner = fields.Many2one(
+        "res.partner",
+        string="Partner",
+    )
+
+    billing_type = fields.Selection(
+        selection=[
+            ("paid", "paid"),
+            ("complimentary", "complimentary"),
+        ],
+        default="paid",
+        string="Billing Type",
+        required=True,
+    )
+
+    start_date = fields.Date(
+        string="Start Date",
+        required=True,
+        default=date.today(),
+    )
+    end_date = fields.Date(string="End Date")
+    support_subscription_type = fields.Many2one(
+        comodel_name="openstack.support_subscription.type",
+        string="Subscription Type",
+        required=True,
+    )
+
+    @api.constrains("project", "")
+    def _check_owner(self):
+        """Check an owner is set
+
+        Project and/or Partner must be set
+        """
+        if not self.project and not self.partner:
+            raise exceptions.ValidationError(
+                _("Must set at one or both Project/Partner.")
+            )
+
+    @api.constrains("start_date", "end_date")
+    def _check_end_date(self):
+        """Constrain start date
+
+        Start date must always be before the expiry date.
+        """
+        if self.end_date and self.start_date > self.end_date:
+            raise exceptions.ValidationError(_("End date must be after the start date"))
+
+
+class OpenStackSupportSubscriptionType(models.Model):
+    """Support Subscription type."""
+
+    _name = "openstack.support_subscription.type"
+    _description = "OpenStack Support Subscription Type"
+
+    name = fields.Char(string="Name", required=True)
+    product = fields.Many2one(
+        comodel_name="product.product",
+        string="Product",
+        required=True,
+    )
+    usage_percent = fields.Float(
+        string="Percentage of usage to compare to price (0 to 100)",
+        required=True,
+    )
+
+    support_subscription = fields.One2many(
+        comodel_name="openstack.support_subscription",
+        inverse_name="support_subscription_type",
+    )
+
+    @api.constrains("discount_percent")
+    def _check_discount_percent(self):
+        """Constrain discount to 0-100"""
+        if self.discount_percent < 0.0 or self.discount_percent > 100.0:
+            raise exceptions.ValidationError(
+                _("discount_percent must be between 0-100")
+            )
diff --git a/models/term_discount.py b/models/term_discount.py
new file mode 100755
index 0000000..9c2a917
--- /dev/null
+++ b/models/term_discount.py
@@ -0,0 +1,80 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import date
+
+from odoo import _, api, fields, models, exceptions
+
+
+class OpenStackTermDiscount(models.Model):
+    """OpenStack Term Discount which is attached to a Partner or project."""
+
+    _name = "openstack.term_discount"
+    _description = "OpenStack Term Discount"
+    _rec_name = "project"
+
+    partner = fields.Many2one(
+        comodel_name="res.partner",
+        string="Partner",
+        required=True,
+    )
+
+    project = fields.Many2one(
+        comodel_name="openstack.project",
+        string="Project",
+    )
+
+    start_date = fields.Date(
+        string="Start Date",
+        required=True,
+        default=date.today(),
+    )
+    end_date = fields.Date(
+        string="Expiry Date",
+        required=True,
+    )
+    early_termination_date = fields.Date(string="Early termination Date")
+
+    min_commit = fields.Float(
+        string="Minimum Commitment",
+        required=True,
+    )
+
+    discount_percent = fields.Float(
+        string="Discount percentage (0 to 100)",
+        required=True,
+    )
+
+    superseded_by = fields.Many2one(
+        comodel_name="openstack.term_discount",
+        string="Superseded by",
+    )
+
+    @api.constrains("discount_percent")
+    def _check_discount_percent(self):
+        """Constrain discount to 0-100"""
+        if self.discount_percent < 0.0 or self.discount_percent > 100.0:
+            raise exceptions.ValidationError(
+                _("discount_percent must be between 0-100")
+            )
+
+    @api.constrains("start_date", "end_date")
+    def _check_end_date(self):
+        """Constrain start date
+
+        Start date must always be before the expiry date.
+        """
+        if self.end_date and self.start_date > self.end_date:
+            raise exceptions.ValidationError(_("End date must be after the start date"))
diff --git a/models/trial.py b/models/trial.py
new file mode 100755
index 0000000..c61952b
--- /dev/null
+++ b/models/trial.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import date
+
+from odoo import _, api, fields, models, exceptions
+
+
+class OpenStackTrial(models.Model):
+    _name = "openstack.trial"
+    _description = "OpenStack Trial"
+    _rec_name = "partner"
+
+    partner = fields.Many2one(
+        "res.partner",
+        string="Trial partner",
+        required=True,
+    )
+
+    start_date = fields.Date(
+        string="Start Date",
+        required=True,
+        default=date.today(),
+    )
+    end_date = fields.Date(string="End Date", required=True)
+
+    account_suspended_on = fields.Date(string="Account suspended date")
+    account_terminated_on = fields.Date(string="Account terminated date")
+
+    account_upgraded_on = fields.Date(string="Account upgraded date")
+
+    @api.constrains("start_date", "end_date")
+    def _check_end_date(self):
+        """Constrain start date
+
+        Start date must always be before the expiry date.
+        """
+        if self.end_date and self.start_date > self.end_date:
+            raise exceptions.ValidationError(_("End date must be after the start date"))
diff --git a/models/volume_discount.py b/models/volume_discount.py
new file mode 100755
index 0000000..797cc3f
--- /dev/null
+++ b/models/volume_discount.py
@@ -0,0 +1,68 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from odoo import _, api, fields, models, exceptions
+
+
+class OpenStackVolumeDiscountRange(models.Model):
+    """OpenStack Volume Discount ranges"""
+
+    _name = "openstack.volume_discount_range"
+    _description = "OpenStack Volume Discount Range"
+
+    customer_group = fields.Many2one(
+        "openstack.customer_group",
+        "Customer Group",
+    )
+
+    @api.depends("use_max", "min", "max", "customer_group")
+    def _get_name(self):
+        """Computed field to show in the breadcrumbs"""
+        for vol_disc in self:
+            if vol_disc.use_max:
+                name = f"{vol_disc.min} to {vol_disc.max}"
+            else:
+                name = f"{vol_disc.min} and higher"
+            if vol_disc.customer_group:
+                name += f" {vol_disc.customer_group.name}"
+            vol_disc.name = name
+
+    name = fields.Char(string="Name", compute="_get_name")
+
+    min = fields.Float(string="min end of the range", required=True)
+
+    use_max = fields.Boolean(string="Use max", default=True)
+    max = fields.Float(
+        string="Max end of the range",
+        default=None,
+    )
+
+    discount_percent = fields.Float(
+        "Discount percentage for this range (0-100)", required=True
+    )
+
+    @api.constrains("discount_percent")
+    def _check_discount_percent(self):
+        """Constrain discount to 0-100"""
+        if self.discount_percent < 0.0 or self.discount_percent > 100.0:
+            raise exceptions.ValidationError(
+                _("discount_percent must be between 0-100")
+            )
+
+    @api.constrains("min", "max")
+    def _check_max(self):
+        """Min must be less than max."""
+        if self.max and self.min > self.max:
+            raise exceptions.ValidationError(_("Min must be less than max."))
diff --git a/models/voucher_codes.py b/models/voucher_codes.py
new file mode 100755
index 0000000..6a2eff4
--- /dev/null
+++ b/models/voucher_codes.py
@@ -0,0 +1,85 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from odoo import _, api, fields, models, exceptions
+
+
+class OpenStackVoucherCode(models.Model):
+    """OpenStack voucher code."""
+
+    _name = "openstack.voucher_code"
+    _description = "OpenStack Voucher Code"
+
+    code = fields.Char(string="Code", required=True, unique=True)
+
+    @api.depends("code")
+    def _get_name(self):
+        """Computed field to show field 'code' in the breadcrumbs"""
+        for voucher in self:
+            voucher.name = voucher.code
+
+    name = fields.Char(string="Name", compute="_get_name")
+
+    claimed = fields.Boolean(string="Claimed", default=False)
+    multi_use = fields.Boolean(string="Multi-use code", default=False)
+
+    expiry_date = fields.Date(string="Expiry Date")
+
+    sales_person = fields.Many2one(
+        comodel_name="res.partner",
+        string="Sales person",
+    )
+
+    customer_group = fields.Many2one(
+        comodel_name="openstack.customer_group",
+        string="Customer Group",
+    )
+
+    credit_amount = fields.Float(
+        string="Credit initial balance",
+    )
+    credit_type = fields.Many2one(
+        comodel_name="openstack.credit.type",
+        string="Credit Type",
+    )
+    credit_duration = fields.Integer(
+        string="Credit duration in days",
+    )
+
+    grant_value = fields.Float(
+        string="Grant value",
+    )
+    grant_type = fields.Many2one(
+        comodel_name="openstack.grant.type",
+        string="Grant Type",
+    )
+    grant_duration = fields.Integer(
+        string="Grant duration in days",
+    )
+
+    quota_size = fields.Char(string="Default quota size on signup")
+
+    tags = fields.Many2many(
+        "res.partner.category",
+        column1="openstack_voucher_id",
+        column2="category_id",
+        string="Tags",
+    )
+
+    @api.constrains("claimed", "multi_use")
+    def _check_claimed_if_multi_use(self):
+        """Can't be claimed if multi_use"""
+        if self.multi_use and self.claimed:
+            raise exceptions.ValidationError(_("Can't claim if multi_use is true."))
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..8dbe98f
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,8 @@
+[tool.black]
+target-version = [
+    "py38",
+    "py39",
+    "py310",
+    "py311",
+    "py312",
+]
diff --git a/report/__init__.py b/report/__init__.py
new file mode 100755
index 0000000..d3573be
--- /dev/null
+++ b/report/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from . import openstack_invoice_csv
+
+__all__ = ["openstack_invoice_csv"]
diff --git a/report/openstack_invoice_csv.py b/report/openstack_invoice_csv.py
new file mode 100755
index 0000000..57a1c05
--- /dev/null
+++ b/report/openstack_invoice_csv.py
@@ -0,0 +1,72 @@
+# Copyright (C) 2021-2024 Catalyst Cloud Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import csv
+
+from odoo.exceptions import UserError
+from odoo import models
+
+
+class OpenStackInvoiceCSV(models.AbstractModel):
+    _name = "report.openstack_integration.openstack_invoice_csv"
+    _inherit = "report.report_csv.abstract"
+    _description = "OpenStack Invoice CSV"
+
+    def generate_csv_report(self, writer, data, invoice):
+        if len(invoice) != 1:
+            # TODO: Hey why - can do for more
+            raise UserError("This CSV can only be created for one invoice at a time")
+        writer.writeheader()
+        # TODO: Investigate why this line is needed
+        invoice = invoice.with_context(allowed_company_ids=[invoice.company_id.id])
+        for line in invoice.invoice_line_ids:
+            writer.writerow(
+                {
+                    "name": line.name,
+                    "project": line.os_project.name,
+                    "project_id": line.os_project.os_id,
+                    "product": line.product_id.name,
+                    "region": line.os_region,
+                    "resource_name": line.os_resource_name,
+                    "resource_type": line.os_resource_type,
+                    "resource_id": line.os_resource_id,
+                    "unit_type": line.product_id.default_code,
+                    "units": line.quantity,
+                    "price_per_unit": line.price_unit,
+                    "subtotal": round(line.price_subtotal, 6),
+                }
+            )
+
+    def csv_report_options(self):
+        res = super().csv_report_options()
+        fieldnames = [
+            "name",
+            "project",
+            "project_id",
+            "product",
+            "region",
+            "resource_name",
+            "resource_type",
+            "resource_id",
+            "unit_type",
+            "units",
+            "price_per_unit",
+            "subtotal",
+        ]
+        for field in fieldnames:
+            res["fieldnames"].append(field)
+        res["delimiter"] = ","
+        res["quoting"] = csv.QUOTE_NONNUMERIC
+        return res
diff --git a/report/openstack_invoice_csv.xml b/report/openstack_invoice_csv.xml
new file mode 100755
index 0000000..3fcd040
--- /dev/null
+++ b/report/openstack_invoice_csv.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <report
+                id="openstack_invoice_csv"
+                model="account.move"
+                string="OpenStack CSV"
+                report_type="csv"
+                name="openstack_integration.openstack_invoice_csv"
+                file="openstack_integration.openstack_invoice_csv"
+                attachment="(object.state == 'posted') and ((object.name or 'INV').replace('/','_')+'.csv')"
+                print_report_name="(object._get_report_base_filename())"
+                attachment_use="False"
+                groups="openstack_integration.group_openstack_user"
+        />
+
+    </data>
+</odoo>
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
new file mode 100755
index 0000000..f9380b4
--- /dev/null
+++ b/security/ir.model.access.csv
@@ -0,0 +1,35 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_openstack_credit_user,access_openstack_credit_user,model_openstack_credit,group_openstack_user,1,0,0,0
+access_openstack_credit_manager,access_openstack_credit_manager,model_openstack_credit,group_openstack_manager,1,1,1,1
+access_openstack_credit_transaction_user,access_openstack_credit_transaction_user,model_openstack_credit_transaction,group_openstack_user,1,0,0,0
+access_openstack_credit_transaction_manager,access_openstack_credit_transaction_manager,model_openstack_credit_transaction,group_openstack_manager,1,1,1,1
+access_openstack_credit_type_user,access_openstack_credit_type_user,model_openstack_credit_type,group_openstack_user,1,0,0,0
+access_openstack_credit_type_manager,access_openstack_credit_type_manager,model_openstack_credit_type,group_openstack_manager,1,1,1,1
+access_openstack_customer_group_user,access_openstack_customer_group_user,model_openstack_customer_group,group_openstack_user,1,0,0,0
+access_openstack_customer_group_manager,access_openstack_customer_group_manager,model_openstack_customer_group,group_openstack_manager,1,1,1,1
+access_openstack_grant_user,access_openstack_grant_user,model_openstack_grant,group_openstack_user,1,0,0,0
+access_openstack_grant_manager,access_openstack_grant_manager,model_openstack_grant,group_openstack_manager,1,1,1,1
+access_openstack_grant_type_user,access_openstack_grant_type_user,model_openstack_grant_type,group_openstack_user,1,0,0,0
+access_openstack_grant_type_manager,access_openstack_grant_type_manager,model_openstack_grant_type,group_openstack_manager,1,1,1,1
+access_openstack_project_user,access_openstack_project_user,model_openstack_project,group_openstack_user,1,0,0,0
+access_openstack_project_manager,access_openstack_project_manager,model_openstack_project,group_openstack_manager,1,1,1,1
+access_openstack_project_contact_user,access_openstack_project_contact_user,model_openstack_project_contact,group_openstack_user,1,0,0,0
+access_openstack_project_contact_manager,access_openstack_project_contact_manager,model_openstack_project_contact,group_openstack_manager,1,1,1,1
+access_openstack_referral_code_user,access_openstack_referral_code_user,model_openstack_referral_code,group_openstack_user,1,0,0,0
+access_openstack_referral_code_manager,access_openstack_referral_code_manager,model_openstack_referral_code,group_openstack_manager,1,1,1,1
+access_openstack_reseller_user,access_openstack_reseller_user,model_openstack_reseller,group_openstack_user,1,0,0,0
+access_openstack_reseller_manager,access_openstack_reseller_manager,model_openstack_reseller,group_openstack_manager,1,1,1,1
+access_openstack_reseller_tier_user,access_openstack_reseller_tier_user,model_openstack_reseller_tier,group_openstack_user,1,0,0,0
+access_openstack_reseller_tier_manager,access_openstack_reseller_tier_manager,model_openstack_reseller_tier,group_openstack_manager,1,1,1,1
+access_openstack_support_subscription_user,access_openstack_support_subscription_user,model_openstack_support_subscription,group_openstack_user,1,0,0,0
+access_openstack_support_subscription_manager,access_openstack_support_subscription_manager,model_openstack_support_subscription,group_openstack_manager,1,1,1,1
+access_openstack_support_subscription_type_user,access_openstack_support_subscription_type_user,model_openstack_support_subscription_type,group_openstack_user,1,0,0,0
+access_openstack_support_subscription_type_manager,access_openstack_support_subscription_type_manager,model_openstack_support_subscription_type,group_openstack_manager,1,1,1,1
+access_openstack_term_discount_user,access_openstack_term_discount_user,model_openstack_term_discount,group_openstack_user,1,0,0,0
+access_openstack_term_discount_manager,access_openstack_term_discount_manager,model_openstack_term_discount,group_openstack_manager,1,1,1,1
+access_openstack_trial_user,access_openstack_trial_user,model_openstack_trial,group_openstack_user,1,0,0,0
+access_openstack_trial_manager,access_openstack_trial_manager,model_openstack_trial,group_openstack_manager,1,1,1,1
+access_openstack_volume_discount_range_user,access_openstack_volume_discount_range_user,model_openstack_volume_discount_range,group_openstack_user,1,0,0,0
+access_openstack_volume_discount_range_manager,access_openstack_volume_discount_range_manager,model_openstack_volume_discount_range,group_openstack_manager,1,1,1,1
+access_openstack_voucher_code_user,access_openstack_voucher_code_user,model_openstack_voucher_code,group_openstack_user,1,0,0,0
+access_openstack_voucher_code_manager,access_openstack_voucher_code_manager,model_openstack_voucher_code,group_openstack_manager,1,1,1,1
diff --git a/security/openstack_security.xml b/security/openstack_security.xml
new file mode 100755
index 0000000..d7d38e7
--- /dev/null
+++ b/security/openstack_security.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data noupdate="0">
+
+        <record model="ir.module.category" id="module_category_openstack">
+            <field name="name">OpenStack Services</field>
+        </record>
+
+        <record id="group_openstack_user" model="res.groups">
+            <field name="name">User</field>
+            <field name="category_id" ref="module_category_openstack" />
+        </record>
+        <record id="group_openstack_manager" model="res.groups">
+            <field name="name">Manager</field>
+            <field name="category_id" ref="module_category_openstack" />
+            <field name="implied_ids" eval="[(4, ref('group_openstack_user'))]"/>
+        </record>
+    </data>
+</odoo>
diff --git a/static/OpenStack-Logo-Horizontal.png b/static/OpenStack-Logo-Horizontal.png
new file mode 100644
index 0000000..b1946cd
Binary files /dev/null and b/static/OpenStack-Logo-Horizontal.png differ
diff --git a/static/OpenStack-Logo-Horizontal.svg b/static/OpenStack-Logo-Horizontal.svg
new file mode 100644
index 0000000..085d529
--- /dev/null
+++ b/static/OpenStack-Logo-Horizontal.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480.62 85.39"><defs><style>.cls-1{fill:#ed1944;}.cls-2{fill:#4e4540;}</style></defs><title>OpenStack_Logo_Horizontal</title><path class="cls-1" d="M234.37,262.07h-71a8.13,8.13,0,0,0-8.11,8.11v19.1h19.7v-3.12a4.4,4.4,0,0,1,4.39-4.39h39.07a4.39,4.39,0,0,1,4.38,4.39v3.12h19.71v-19.1A8.13,8.13,0,0,0,234.37,262.07Z" transform="translate(-155.23 -262.07)"/><path class="cls-1" d="M222.77,323.37a4.39,4.39,0,0,1-4.38,4.39H179.32a4.4,4.4,0,0,1-4.39-4.39v-3.12h-19.7v19.1a8.13,8.13,0,0,0,8.11,8.11h71a8.13,8.13,0,0,0,8.11-8.11v-19.1H222.77Z" transform="translate(-155.23 -262.07)"/><rect class="cls-1" y="32.84" width="19.7" height="19.7"/><rect class="cls-1" x="67.55" y="32.84" width="19.7" height="19.7"/><path class="cls-2" d="M635.85,321.32a5.55,5.55,0,1,0-5.54,5.54A5.55,5.55,0,0,0,635.85,321.32Zm-5.54,4.18a4.19,4.19,0,1,1,4.18-4.18A4.18,4.18,0,0,1,630.31,325.5Z" transform="translate(-155.23 -262.07)"/><path class="cls-2" d="M632.39,320.31v0a1.75,1.75,0,0,0-.46-1.26,2,2,0,0,0-1.48-.52h-2v5.43h1.13v-1.78h.7l1,1.78h1.29l-1.15-2A1.66,1.66,0,0,0,632.39,320.31Zm-1.14,0a.78.78,0,0,1-.88.82h-.83v-1.64h.8c.58,0,.91.27.91.81Z" transform="translate(-155.23 -262.07)"/><path class="cls-2" d="M302.94,305.14v-.75c0-13.11-8.6-22.63-20.3-22.63s-20.46,9.68-20.46,22.71v.76c0,13.11,8.6,22.55,20.3,22.55S302.94,318.17,302.94,305.14ZM271.62,305v-.51c0-8.27,4.26-14.36,10.86-14.36,6.43,0,11,6.18,11,14.45v.5c0,8.27-4.26,14.45-10.86,14.45C276.21,319.51,271.62,313.24,271.62,305Z" transform="translate(-155.23 -262.07)"/><path class="cls-2" d="M347.87,305.31v-1.17c0-14.95-8.35-22.38-17.21-22.38-6.34,0-10.19,3.67-12.94,7.93v-7.1h-9.44v56.88h9.44V320.18c2.67,3.92,6.51,7.51,12.94,7.51C339.6,327.69,347.87,320.18,347.87,305.31Zm-30.32-.17v-.83c0-8.69,4.84-14.2,10.52-14.2s10.28,5.43,10.28,14.28v.75c0,8.86-4.51,14.2-10.28,14.2S317.55,313.83,317.55,305.14Z" transform="translate(-155.23 -262.07)"/><path class="cls-2" d="M388.76,320.93l-4.92-5.85c-2.93,2.84-6,4.68-10.19,4.68-5.77,0-10.36-4-11.2-11.44h27.73c.09-1.34.09-2.68.09-3.26,0-13.28-6.35-23.3-18.38-23.3-10.86,0-18.87,9.52-18.87,22.8v.67c0,13.86,8.85,22.55,20.21,22.55A20.34,20.34,0,0,0,388.76,320.93Zm-16.87-31.24c5.51,0,8.69,5.1,9.19,12.11H362.37C363,294.28,366.71,289.69,371.89,289.69Z" transform="translate(-155.23 -262.07)"/><path class="cls-2" d="M418,281.76c-6,0-9.6,3.42-12.11,7.35v-6.52h-9.44v44.27h9.44V300.05c0-6.1,3.42-9.77,8.35-9.77s7.94,3.42,7.94,9.6v27h9.43V297.21C431.59,287.77,426.5,281.76,418,281.76Z" transform="translate(-155.23 -262.07)"/><path class="cls-2" d="M469.64,314.25v-.09c0-6.93-5.18-10.1-12-13.19-5.18-2.34-8.68-3.85-8.68-6.77V294c0-2.42,2.08-4.34,5.59-4.34s7.18,1.67,10.52,3.93l4-6.94a25.29,25.29,0,0,0-14.36-4.76c-8.27,0-14.62,5-14.62,12.95V295c0,7.26,5.51,10.27,12.11,13.11,5.26,2.25,8.69,3.67,8.69,6.85v.08c0,2.76-2.26,4.85-6.18,4.85s-8.19-1.93-12.2-5.18l-4.42,6.76a26.11,26.11,0,0,0,16.45,6.18C463,327.69,469.64,322.93,469.64,314.25Z" transform="translate(-155.23 -262.07)"/><path class="cls-2" d="M498.86,325.61v-7.77a10.71,10.71,0,0,1-5.09,1.25c-3,0-4.76-1.42-4.76-4.84V290.69H499v-8.1H489V270.65h-9.44v11.94h-4.85l1.56,8.1h3.29v25.48c0,8.6,4.93,11.36,11.44,11.36A16.12,16.12,0,0,0,498.86,325.61Z" transform="translate(-155.23 -262.07)"/><path class="cls-2" d="M535,286.6c-2.84-2.92-7.27-4.51-13-4.51a31.47,31.47,0,0,0-14.62,3.51l2.67,7.52a25.14,25.14,0,0,1,10.69-2.68c6,0,9.52,3.09,9.52,9v1.84a30.13,30.13,0,0,0-10.19-1.75c-9.35,0-15.7,4.67-15.7,14.19v.42c0,8.61,6.18,13.53,13.53,13.53a14.07,14.07,0,0,0,12.28-6.26v5.43h9.27V299.38C539.45,293.87,538,289.52,535,286.6ZM530.35,312c0,5-4.18,8.69-9.61,8.6-4.09-.08-7.18-2.59-7.18-7v-.17c0-4.68,3.17-7.43,8.6-7.43a21.47,21.47,0,0,1,8.19,1.58Z" transform="translate(-155.23 -262.07)"/><path class="cls-2" d="M579.57,321.09l-5.09-6.26c-2.59,2.59-5.18,4.51-9,4.51-6.44,0-11-5.76-11-14.36v-.42c0-8.36,4.51-14.28,10.7-14.28,4,0,6.59,2,8.93,4.42l5.26-6.93a18.66,18.66,0,0,0-14.19-6c-11.61,0-20.22,9.52-20.22,22.71v.84c0,13.2,8.36,22.47,19.88,22.47A18.51,18.51,0,0,0,579.57,321.09Z" transform="translate(-155.23 -262.07)"/><polygon class="cls-2" points="453.23 41.81 446.6 48.93 455.49 64.79 466.43 64.79 453.23 41.81"/><polygon class="cls-2" points="454.82 20.52 439.7 39.98 439.7 4.48 430.27 4.48 430.27 64.79 439.7 64.79 439.7 52.01 466.01 20.52 454.82 20.52"/><script xmlns=""/></svg>
diff --git a/static/OpenStack-Logo-Mark.png b/static/OpenStack-Logo-Mark.png
new file mode 100644
index 0000000..8061ae5
Binary files /dev/null and b/static/OpenStack-Logo-Mark.png differ
diff --git a/static/OpenStack-Logo-Mark.svg b/static/OpenStack-Logo-Mark.svg
new file mode 100644
index 0000000..666ec98
--- /dev/null
+++ b/static/OpenStack-Logo-Mark.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 209.67 180.35"><defs><style>.cls-1{opacity:0.98;}.cls-2{fill:#ed1944;}</style></defs><title>OpenStack_Logo_Mark</title><g class="cls-1"><path class="cls-2" d="M461.82,215.24h-150a17.17,17.17,0,0,0-17.12,17.12v40.35h41.61v-6.59a9.26,9.26,0,0,1,9.26-9.26h82.53a9.26,9.26,0,0,1,9.26,9.26v6.59H479V232.36A17.18,17.18,0,0,0,461.82,215.24Z" transform="translate(-294.67 -215.24)"/><path class="cls-2" d="M437.33,344.72a9.27,9.27,0,0,1-9.26,9.26H345.54a9.27,9.27,0,0,1-9.26-9.26v-6.59H294.67v40.34a17.17,17.17,0,0,0,17.12,17.13h150A17.18,17.18,0,0,0,479,378.47V338.13H437.33Z" transform="translate(-294.67 -215.24)"/><rect class="cls-2" y="69.37" width="41.62" height="41.62"/><rect class="cls-2" x="142.66" y="69.37" width="41.62" height="41.62"/></g><path class="cls-2" d="M504.33,386.39a9.2,9.2,0,1,0-9.2,9.21A9.21,9.21,0,0,0,504.33,386.39Zm-9.2,6.94a6.94,6.94,0,1,1,6.94-6.94A6.94,6.94,0,0,1,495.13,393.33Z" transform="translate(-294.67 -215.24)"/><path class="cls-2" d="M498.58,384.72v-.05a2.88,2.88,0,0,0-.76-2.09,3.38,3.38,0,0,0-2.45-.86H492v9h1.86v-3H495l1.66,3h2.14l-1.92-3.35A2.72,2.72,0,0,0,498.58,384.72Zm-1.88.06a1.3,1.3,0,0,1-1.47,1.35h-1.38v-2.72h1.34c1,0,1.51.45,1.51,1.35Z" transform="translate(-294.67 -215.24)"/><script xmlns=""/></svg>
diff --git a/views/account_move_view.xml b/views/account_move_view.xml
new file mode 100755
index 0000000..076a560
--- /dev/null
+++ b/views/account_move_view.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+        <!-- Add (optionally visible) column for OS Project -->
+        <record id="view_invoice_tree" model="ir.ui.view">
+            <field name="name">account.invoice.tree (in openstack_integration)</field>
+            <field name="model">account.move</field>
+            <field name="inherit_id" ref="account.view_invoice_tree" />
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr='//field[@name="invoice_date_due"]' position="before">
+                        <field
+                            name="os_project"
+                            string="OS Project"
+                            optional="hide"
+                        />
+                    </xpath>
+                </data>
+            </field>
+        </record>
+
+        <!-- Add project fields to an account move -->
+        <record id="view_move_form" model="ir.ui.view">
+            <field name="name">account.move.form</field>
+            <field name="model">account.move</field>
+            <field name="type">form</field>
+            <field name="inherit_id" ref="account.view_move_form" />
+            <field name="arch" type="xml">
+                <data>
+
+                    <xpath expr='//page[@name="other_info"]' position="after">
+                        <page
+                            string="OpenStack"
+                            groups="openstack_integration.group_openstack_user,openstack_integration.group_openstack_manager">
+                            <group>
+                                <field name="os_project" string="OS Project" />
+                                <field name="os_is_cloud_framework_agreement_invoice" string="CFA Invoice" />
+                            </group>
+                        </page>
+                    </xpath>
+
+                    <xpath expr="//field[@name='product_id']" position="before">
+                        <field name="os_project" optional="hide" string="OS Project" groups="openstack_integration.group_openstack_user,openstack_integration.group_openstack_manager" />
+                    </xpath>
+
+                </data>
+            </field>
+        </record>
+
+    </data>
+</odoo>
diff --git a/views/credit_view.xml b/views/credit_view.xml
new file mode 100755
index 0000000..d857087
--- /dev/null
+++ b/views/credit_view.xml
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+        <!-- form view for a openstack credit -->
+        <record id="view_openstack_credit_form" model="ir.ui.view">
+            <field name="name">openstack.credit.form</field>
+            <field name="model">openstack.credit</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Credit">
+                    <group col="4">
+                        <field name="credit_type" />
+                        <field name="project" />
+                        <field name="voucher_code" />
+                    </group>
+                    <group col="4">
+                        <field name="current_balance" />
+                        <field name="start_date" />
+                        <field name="initial_balance" />
+                        <field name="expiry_date" />
+                    </group>
+                    <group col="4">
+                        <field name="create_date" />
+                    </group>
+                    <notebook>
+                        <page string="Transactions">
+                            <field
+                                name="transactions"
+                                widget="one2many_list"
+                                string="Transactions"
+                                context="{'default_credit': id}" />
+                        </page>
+                    </notebook>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack credits -->
+        <record id="view_openstack_credit_tree" model="ir.ui.view">
+            <field name="name">openstack.credit.tree</field>
+            <field name="model">openstack.credit</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Credits">
+                    <field name="voucher_code" />
+                    <field name="project" />
+                    <field name="credit_type" />
+                    <field name="start_date" />
+                    <field name="expiry_date" />
+                    <field name="current_balance" />
+                    <field name="initial_balance" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack credit transactions -->
+        <record id="view_openstack_credit_transaction_tree" model="ir.ui.view">
+            <field name="name">openstack.credit.transaction.tree</field>
+            <field name="model">openstack.credit.transaction</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Credit Transactions">
+                    <field name="credit" />
+                    <field name="value" />
+                    <field name="description" />
+                    <field name="create_date" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- Search functionality for credit -->
+        <record id="view_openstack_credit_filter" model="ir.ui.view">
+            <field name="name">openstack.credit.select</field>
+            <field name="model">openstack.credit</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Credit">
+                    <field name="voucher_code" />
+                    <field name="project" />
+                </search>
+            </field>
+        </record>
+
+        <!-- action to open the openstack credit list -->
+        <record id="action_openstack_credit_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Credits</field>
+            <field name="res_model">openstack.credit</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack credit -->
+        <menuitem
+            action="action_openstack_credit_form"
+            id="menu_action_openstack_credit_form"
+            parent="menu_openstack_discounts" sequence="0" />
+
+        <!-- form view for a openstack credit type -->
+        <record id="view_openstack_credit_type_form" model="ir.ui.view">
+            <field name="name">openstack.credit.type.form</field>
+            <field name="model">openstack.credit.type</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Credit Types">
+                    <group col="4">
+                        <field name="name" />
+                        <field name="refundable" />
+                        <!-- <field name="only_for_products" widget="one2many" />
+                        <field name="only_for_product_categories" widget="one2many" /> -->
+                    </group>
+                    <group col="4">
+                        <field name="product" />
+                        <field name="create_date" />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack credit type -->
+        <record id="view_openstack_credit_type_tree" model="ir.ui.view">
+            <field name="name">openstack.credit.type.tree</field>
+            <field name="model">openstack.credit.type</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Credit Types">
+                    <field name="name" />
+                    <field name="refundable" />
+                    <!-- <field name="only_for_products" />
+                    <field name="only_for_product_categories" /> -->
+                    <field name="product" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- action to open the openstack credit type list -->
+        <record id="action_openstack_credit_type_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Credit Types</field>
+            <field name="res_model">openstack.credit.type</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack credit type -->
+        <menuitem
+            action="action_openstack_credit_type_form"
+            id="menu_action_openstack_credit_type_form"
+            parent="menu_openstack_configuration" sequence="0" />
+
+    </data>
+</odoo>
diff --git a/views/customer_group_view.xml b/views/customer_group_view.xml
new file mode 100755
index 0000000..e70f77e
--- /dev/null
+++ b/views/customer_group_view.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- form view for a openstack customer_group -->
+        <record id="view_openstack_customer_group_form" model="ir.ui.view">
+            <field name="name">openstack.customer_group.form</field>
+            <field name="model">openstack.customer_group</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Customer Group">
+                    <group col="4">
+                        <field name="name" />
+                        <field name="pricelist" />
+                    </group>
+                    <group col="4">
+                        <field name="create_date" />
+                    </group>
+                    <group col="4">
+                        <field name="partners" widget="one2many_list"/>
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack customer_groups -->
+        <record id="view_openstack_customer_group_tree" model="ir.ui.view">
+            <field name="name">openstack.customer_group.tree</field>
+            <field name="model">openstack.customer_group</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Customer Groups">
+                    <field name="name" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- Search functionality on customer_groups -->
+        <record id="view_openstack_customer_group_filter" model="ir.ui.view">
+            <field name="name">openstack.customer_group.select</field>
+            <field name="model">openstack.customer_group</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Customer Group">
+                    <field name="name" />
+                    <field name="partners" />
+                    <field name="pricelist" />
+                </search>
+            </field>
+        </record>
+
+        <!-- action to open the openstack customer_group list -->
+        <record id="action_openstack_customer_group_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Customer Groups</field>
+            <field name="res_model">openstack.customer_group</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack customer_group -->
+        <menuitem
+            action="action_openstack_customer_group_form"
+            id="menu_action_openstack_customer_group_form"
+            parent="menu_openstack_main" sequence="2" />
+
+    </data>
+</odoo>
diff --git a/views/grant_view.xml b/views/grant_view.xml
new file mode 100755
index 0000000..26fc436
--- /dev/null
+++ b/views/grant_view.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- form view for a openstack grant -->
+        <record id="view_openstack_grant_form" model="ir.ui.view">
+            <field name="name">openstack.grant.form</field>
+            <field name="model">openstack.grant</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Grant">
+                    <group col="4">
+                        <field name="grant_type" />
+                        <field name="project" />
+                        <field name="voucher_code" />
+                    </group>
+                    <group col="4">
+                        <field name="start_date" />
+                        <field name="expiry_date" />
+                        <field name="value" />
+                    </group>
+                    <group col="4">
+                        <field name="create_date" />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack grants -->
+        <record id="view_openstack_grant_tree" model="ir.ui.view">
+            <field name="name">openstack.grant.tree</field>
+            <field name="model">openstack.grant</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Grants">
+                    <field name="voucher_code" />
+                    <field name="project" />
+                    <field name="grant_type" />
+                    <field name="start_date" />
+                    <field name="expiry_date" />
+                    <field name="value" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- Search functionality on grants -->
+        <record id="view_openstack_grant_filter" model="ir.ui.view">
+            <field name="name">openstack.grant.select</field>
+            <field name="model">openstack.grant</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Grant">
+                    <field name="project" />
+                    <field name="voucher_code" />
+                    <field name="grant_type" />
+                </search>
+            </field>
+        </record>
+
+        <!-- action to open the openstack grant list -->
+        <record id="action_openstack_grant_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Grants</field>
+            <field name="res_model">openstack.grant</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack grant -->
+        <menuitem
+            action="action_openstack_grant_form"
+            id="menu_action_openstack_grant_form"
+            parent="menu_openstack_discounts" sequence="1" />
+
+        <!-- form view for a openstack grant type -->
+        <record id="view_openstack_grant_type_form" model="ir.ui.view">
+            <field name="name">openstack.grant.type.form</field>
+            <field name="model">openstack.grant.type</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Grant Types">
+                    <group col="4">
+                        <field name="name" />
+                        <field name="only_on_group_root" />
+                        <!-- <field name="only_for_products" />
+                        <field name="only_for_product_categories" /> -->
+                    </group>
+                    <group col="4">
+                        <field name="product" />
+                        <field name="create_date" />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack grant type -->
+        <record id="view_openstack_grant_type_tree" model="ir.ui.view">
+            <field name="name">openstack.grant.type.tree</field>
+            <field name="model">openstack.grant.type</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Grant Types">
+                    <field name="name" />
+                    <field name="product" />
+                    <field name="only_on_group_root" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- action to open the openstack grant type list -->
+        <record id="action_openstack_grant_type_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Grant Types</field>
+            <field name="res_model">openstack.grant.type</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack grant type -->
+        <menuitem
+            action="action_openstack_grant_type_form"
+            id="menu_action_openstack_grant_type_form"
+            parent="menu_openstack_configuration" sequence="1" />
+
+    </data>
+</odoo>
diff --git a/views/main_menu.xml b/views/main_menu.xml
new file mode 100755
index 0000000..c73f8a6
--- /dev/null
+++ b/views/main_menu.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+        <menuitem id="menu_openstack_root" name="OpenStack" web_icon="openstack_integration,static/OpenStack-Logo-Mark.svg" groups="openstack_integration.group_openstack_user,openstack_integration.group_openstack_manager" sequence="20" />
+            <menuitem id="menu_openstack_main" name="OpenStack" parent="menu_openstack_root" sequence="0" />
+                <menuitem id="menu_action_move_out_invoice_type" parent="menu_openstack_main" action="account.action_move_out_invoice_type" sequence="0"/>
+                <menuitem id="menu_point_of_sale_customer" name="Customers" parent="menu_openstack_main" action="account.res_partner_action_customer" sequence="1" />
+                <menuitem id="menu_product_template_action" parent="menu_openstack_main" action="sale.product_template_action"  sequence="21" />
+            <menuitem id="menu_openstack_discounts" name="Discounts and Credits" parent="menu_openstack_root" sequence="1" />
+            <menuitem id="menu_openstack_configuration" name="Configuration" parent="menu_openstack_root" sequence="2" />
+    </data>
+</odoo>
diff --git a/views/project_view.xml b/views/project_view.xml
new file mode 100755
index 0000000..aa84b1a
--- /dev/null
+++ b/views/project_view.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- Form view for a project -->
+        <record id="view_openstack_project_form" model="ir.ui.view">
+            <field name="name">openstack.project.form</field>
+            <field name="model">openstack.project</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Project">
+                    <group col="4">
+                        <field name="name" />
+                        <field name="os_id" />
+                        <field name="parent" />
+                        <field name="enabled" />
+                    </group>
+                    <group col="4">
+                        <field name="owner" />
+                    </group>
+                    <group col="4">
+                        <field name="billing_type" />
+                        <field name="group_invoices" />
+                        <field name="payment_method" />
+                        <field name="stripe_card_id" />
+                        <field name="po_number" />
+                        <field name="override_po_number" />
+                    </group>
+                    <group col="4">
+                        <field name="support_subscription" />
+                        <field name="create_date" />
+                    </group>
+                    <notebook>
+                        <page string="Contacts">
+                            <field
+                                name="project_contacts"
+                                string="Contacts"
+                                context="{'default_project': id}">
+
+                                <tree string="Partners">
+                                    <field name="partner" />
+                                    <field name="contact_type" />
+                                </tree>
+
+                            </field>
+                        </page>
+                        <page string="Credits">
+                            <field
+                                name="project_credits"
+                                widget="one2many_list"
+                                string="Credits"
+                                context="{'default_project': id}" />
+                        </page>
+                        <page string="Grants">
+                            <field
+                                name="project_grants"
+                                widget="one2many_list"
+                                string="Grants"
+                                context="{'default_project': id}" />
+                        </page>
+                    </notebook>
+                    <div class="oe_chatter">
+                        <field name="message_follower_ids" widget="mail_followers"/>
+                        <field name="message_ids" widget="mail_thread"/>
+                    </div>
+                </form>
+            </field>
+        </record>
+
+        <!-- Tree view for all projects -->
+        <record id="view_openstack_project_tree" model="ir.ui.view">
+            <field name="name">openstack.project.tree</field>
+            <field name="model">openstack.project</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Project">
+                    <field name="name" />
+                    <field name="os_id" />
+                    <field name="payment_method" />
+                    <field name="enabled" />
+                    <field name="create_date" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- Search functionality on projects -->
+        <record id="view_openstack_project_filter" model="ir.ui.view">
+            <field name="name">openstack.project.select</field>
+            <field name="model">openstack.project</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Project">
+                    <field name="name" />
+                    <field name="os_id" />
+                    <field name="parent" />
+                    <field name="owner" />
+                    <field name="billing_type" />
+                    <field name="payment_method" />
+                    <field name="po_number" />
+                    <filter
+                        string="Enabled"
+                        name="filter_openstack_project"
+                        domain="[('enabled', '=', 'True')]" />
+                </search>
+            </field>
+        </record>
+
+        <!-- Action to open the openstack projects list -->
+        <!-- By default action filters for enabled -->
+        <record id="action_openstack_project_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Projects</field>
+            <field name="res_model">openstack.project</field>
+            <field name="view_mode">tree,form</field>
+            <field name="context">{"search_default_filter_openstack_project":1}</field>
+        </record>
+
+        <!-- Menu item for openstack project -->
+        <menuitem
+            action="action_openstack_project_form"
+            id="menu_action_openstack_project_form"
+            parent="menu_openstack_main" sequence="3" />
+
+    </data>
+</odoo>
diff --git a/views/referral_view.xml b/views/referral_view.xml
new file mode 100755
index 0000000..eb37119
--- /dev/null
+++ b/views/referral_view.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- form view for a openstack referral_code -->
+        <record id="view_openstack_referral_code_form" model="ir.ui.view">
+            <field name="name">openstack.referral_code.form</field>
+            <field name="model">openstack.referral_code</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Referral Code">
+                    <group col="4">
+                        <field name="code" />
+                        <field name="partner" />
+                    </group>
+                    <group col="4">
+                        <field name="referral_credit_amount" />
+                        <field name="referral_credit_type" />
+                        <field name="referral_credit_duration" />
+                    </group>
+                    <group col="4">
+                        <field name="before_reward_usage_threshold" />
+                        <field name="allowed_uses" />
+                    </group>
+                    <group col="4">
+                        <field name="reward_credit_amount" />
+                        <field name="reward_credit_type" />
+                        <field name="reward_credit_duration" />
+                    </group>
+                    <group col="4">
+                        <field name="create_date" />
+                    </group>
+                    <notebook>
+                        <page string="Referrals">
+                            <field
+                                name="referrals"
+                                widget="one2many_list"
+                                string="Referrals" />
+                        </page>
+                    </notebook>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack referral_codes -->
+        <record id="view_openstack_referral_code_tree" model="ir.ui.view">
+            <field name="name">openstack.referral_code.tree</field>
+            <field name="model">openstack.referral_code</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Referral Codes">
+                    <field name="code" />
+                    <field name="partner" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- Search functionality on referral_codes -->
+        <record id="view_openstack_referral_code_filter" model="ir.ui.view">
+            <field name="name">openstack.referral_code.select</field>
+            <field name="model">openstack.referral_code</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Referral Code">
+                    <field name="partner" />
+                    <field name="code" />
+                </search>
+            </field>
+        </record>
+
+        <!-- action to open the openstack referral_code list -->
+        <record id="action_openstack_referral_code_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Referral Codes</field>
+            <field name="res_model">openstack.referral_code</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack referral_code -->
+        <menuitem
+            action="action_openstack_referral_code_form"
+            id="menu_action_openstack_referral_code_form"
+            parent="menu_openstack_discounts" sequence="4" />
+
+    </data>
+</odoo>
diff --git a/views/report_invoice.xml b/views/report_invoice.xml
new file mode 100755
index 0000000..0cd927e
--- /dev/null
+++ b/views/report_invoice.xml
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+        <template id="report_invoice_document" name="report_invoice_document (in openstack integration)">
+            <t t-call="web.external_layout">
+                <t t-set="o" t-value="o.with_context(lang=lang)" />
+                <t t-set="categorised" t-value="o.categorised_openstack_invoice_lines()" />
+                <t t-set="uncategorised" t-value="categorised.uncategorised" />
+                <t t-set="address">
+                    <address t-field="o.partner_id" t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
+                    <div t-if="o.partner_id.vat" class="mt16">
+                        <t t-if="o.company_id.country_id.vat_label" t-esc="o.company_id.country_id.vat_label" id="inv_tax_id_label"/>
+                        <t t-else="">Tax ID</t>: <span t-field="o.partner_id.vat"/></div>
+                </t>
+                <div class="page">
+                    <h2>
+                        <span t-if="o.move_type == 'out_invoice' and o.state == 'posted'">Invoice</span>
+                        <span t-if="o.move_type == 'out_invoice' and o.state == 'draft'">Draft Invoice</span>
+                        <span t-if="o.move_type == 'out_invoice' and o.state == 'cancel'">Cancelled Invoice</span>
+                        <span t-if="o.move_type == 'out_refund'">Credit Note</span>
+                        <span t-if="o.move_type == 'in_refund'">Vendor Credit Note</span>
+                        <span t-if="o.move_type == 'in_invoice'">Vendor Bill</span>
+                        <span t-if="o.name != '/'" t-field="o.name"/>
+                    </h2>
+                    <t t-if="o.os_project">
+                        <h3>Project: <span t-esc="o.os_project['name']"></span></h3>
+                        <div><p>Project ID: <span t-esc="o.os_project['os_id']"></span></p></div>
+                    </t>
+                    <div id="information" class="row mt32 mb32">
+                        <div class="col-auto mw-100 mb-2" t-if="o.invoice_date" name="invoice_date">
+                            <strong>Invoice Date:</strong>
+                            <p class="m-0" t-field="o.invoice_date"/>
+                        </div>
+                        <div class="col-auto mw-100 mb-2" t-if="o.invoice_date_due and o.move_type == 'out_invoice' and o.state == 'posted'" name="due_date">
+                            <strong>Due Date:</strong>
+                            <p class="m-0" t-field="o.invoice_date_due"/>
+                        </div>
+                        <div class="col-auto mw-100 mb-2" t-if="o.invoice_origin" name="origin">
+                            <strong>Source:</strong>
+                            <p class="m-0" t-field="o.invoice_origin"/>
+                        </div>
+                        <div class="col-auto mw-100 mb-2" t-if="o.partner_id.ref" name="customer_code">
+                            <strong>Customer Code:</strong>
+                            <p class="m-0" t-field="o.partner_id.ref"/>
+                        </div>
+                        <div class="col-auto mw-100 mb-2" t-if="o.ref" name="reference">
+                            <strong>Reference:</strong>
+                            <p class="m-0" t-field="o.ref"/>
+                        </div>
+                    </div>
+
+                    <t t-set="outstanding" t-value="o.outstanding_invoices" />
+
+                    <t t-if="o.os_project and outstanding.total_owing() > 0">
+                        <h3>Statement of accounts:</h3>
+                        <t t-if="outstanding.old_owing() != 0 or outstanding.current_owing() != 0">
+                            <div>
+                                <table class="table" style="width:100%">
+                                    <thead>
+                                        <th>Invoice ID</th>
+                                        <th>Due date</th>
+                                        <th>Status</th>
+                                        <th class="text-right">Amount due</th>
+                                    </thead>
+                                    <tbody>
+                                        <t t-foreach="outstanding.ordered_old_invoices()" t-as="invoice">
+                                            <tr>
+                                                <td><span t-esc="invoice['name']"></span></td>
+                                                <td><span t-esc="invoice['invoice_date_due'].strftime('%d/%m/%Y')"></span></td>
+                                                <td><span>Overdue</span></td>
+                                                <td class="text-right"><span t-esc="'%.2f'%(invoice['amount_residual_signed'])" style="color: red"></span></td>
+                                            </tr>
+                                        </t>
+                                        <t t-foreach="outstanding.ordered_current_invoices()" t-as="invoice">
+                                            <tr>
+                                                <td><span t-esc="invoice['name']"></span></td>
+                                                <td><span t-esc="invoice['invoice_date_due'].strftime('%d/%m/%Y')"></span></td>
+                                                <td><span>Due</span></td>
+                                                <td class="text-right"><span t-esc="'%.2f'%(invoice['amount_residual_signed'])"></span></td>
+                                            </tr>
+                                        </t>
+                                    </tbody>
+                                </table>
+                            </div>
+                        </t>
+
+                        <div class="clearfix">
+                            <div class="row">
+                                <div t-attf-class="#{'col-6' if report_type != 'html' else 'col-sm-7 col-md-6'} ml-auto">
+                                    <table class="table table-sm" style="page-break-inside: avoid;">
+                                        <t t-if="outstanding.old_owing() != 0">
+                                            <tr>
+                                                <td><strong>Overdue</strong></td>
+                                                <td class="text-right">
+                                                    <span t-esc="'%.2f'%(outstanding.old_owing())" style="color:red"/>
+                                                </td>
+                                            </tr>
+                                        </t>
+                                        <t t-if="outstanding.old_owing() != 0 or outstanding.current_owing() != 0">
+                                            <tr>
+                                                <td>Currently due</td>
+                                                <td class="text-right">
+                                                    <span t-esc="'%.2f'%(outstanding.current_owing())"/>
+                                                </td>
+                                            </tr>
+                                        </t>
+                                        <tr>
+                                            <td><strong>Total due</strong></td>
+                                            <td class="text-right">
+                                                <strong><span t-esc="'%.2f'%(outstanding.total_owing())"/></strong>
+                                            </td>
+                                        </tr>
+                                    </table>
+                                </div>
+                            </div>
+                        </div>
+                    <div style="page-break-before: always;"></div>
+                </t>
+
+
+                    <h3>Invoice summary:</h3>
+
+                    <t t-set="display_discount" t-value="any([l.discount for l in o.invoice_line_ids])"/>
+
+                    <div>
+                        <table class="table" style="width:100%">
+                            <thead>
+                                <th>Product</th>
+                                <th class="text-right">Credit</th>
+                                <th class="text-right">Debit</th>
+                                <th class="text-right">Total</th>
+                            </thead>
+                            <tbody>
+                                <tr>
+                                    <td><span style="font-size:1.25em"><strong>Overall Usage</strong></span> (before tax)</td>
+                                    <td class="text-right"><strong><span t-esc="'%.2f'%(categorised.credit)" style="font-size:1.25em"></span></strong></td>
+                                    <td class="text-right"><strong><span t-esc="'%.2f'%(categorised.debit)" style="font-size:1.25em"></span></strong></td>
+                                    <td class="text-right"><strong><span t-esc="'%.2f'%(categorised.total())" style="font-size:1.25em"></span></strong></td>
+                                </tr>
+                                <t t-foreach="categorised.products.values()" t-as="product">
+                                    <tr>
+                                        <th><span t-esc="product.name" style="font-size:1.1em"></span></th>
+                                        <td class="text-right"><strong><span t-esc="'%.2f'%(product.credit)" style="font-size:1.1em"></span></strong></td>
+                                        <td class="text-right"><strong><span t-esc="'%.2f'%(product.debit)" style="font-size:1.1em"></span></strong></td>
+                                        <td class="text-right"><strong><span t-esc="'%.2f'%(product.total())" style="font-size:1.1em"></span></strong></td>
+
+                                    </tr>
+                                    <t t-foreach="product.regions.values()" t-as="region">
+                                        <tr>
+                                            <td><span t-esc="region.name" style="margin-left:50px"></span></td>
+                                            <td class="text-right"><span t-esc="'%.2f'%(region.credit)"></span></td>
+                                            <td class="text-right"><span t-esc="'%.2f'%(region.debit)"></span></td>
+                                            <td class="text-right"><span t-esc="'%.2f'%(region.total())"></span></td>
+                                        </tr>
+                                    </t>
+
+                                </t>
+                                <t t-foreach="uncategorised" t-as="product">
+                                    <tr>
+                                        <td style="width: 100%"><span t-esc="product.name" t-options="{'widget': 'text'}"></span></td>
+                                        <t t-if="product['display_type'] != 'line_note'">
+                                            <td class="text-right"><strong><span t-esc="'%.2f'%(product.credit)" style="font-size:1.1em"></span></strong></td>
+                                            <td class="text-right"><strong><span t-esc="'%.2f'%(product.debit)" style="font-size:1.1em"></span></strong></td>
+                                            <td class="text-right"><strong><span t-esc="'%.2f'%(product.credit+product.debit)" style="font-size:1.1em"></span></strong></td>
+                                        </t>
+                                        <t t-else="">
+                                            <td></td>
+                                            <td></td>
+                                            <td></td>
+                                        </t>
+                                    </tr>
+                                </t>
+                            </tbody>
+                        </table>
+                    </div>
+
+                    <div class="clearfix">
+                        <div id="total" class="row">
+                            <div t-attf-class="#{'col-6' if report_type != 'html' else 'col-sm-7 col-md-6'} ml-auto">
+                                <table class="table table-sm" style="page-break-inside: avoid;">
+                                    <tr class="border-black o_subtotal" style="">
+                                        <td><strong>Subtotal</strong></td>
+                                        <td class="text-right">
+                                            <span t-field="o.amount_untaxed"/>
+                                        </td>
+                                    </tr>
+                                    <t t-foreach="o.amount_by_group" t-as="amount_by_group">
+                                        <tr style="">
+                                            <t t-if="len(o.line_ids.filtered(lambda line: line.tax_line_id)) in [0, 1] and o.amount_untaxed == amount_by_group[2]">
+                                                <td><span class="text-nowrap" t-esc="amount_by_group[0]"/></td>
+                                                <td class="text-right o_price_total">
+                                                    <span class="text-nowrap" t-esc="amount_by_group[3]" />
+                                                </td>
+                                            </t>
+                                            <t t-else="">
+                                                <td>
+                                                    <span t-esc="amount_by_group[0]"/>
+                                                    <span class="text-nowrap"> on
+                                                        <t t-esc="amount_by_group[4]"/>
+                                                    </span>
+                                                </td>
+                                                <td class="text-right o_price_total">
+                                                    <span class="text-nowrap" t-esc="amount_by_group[3]"/>
+                                                </td>
+                                            </t>
+                                        </tr>
+                                    </t>
+                                    <tr class="border-black o_total">
+                                        <td><strong>Total</strong></td>
+                                        <td class="text-right">
+                                            <span class="text-nowrap" t-field="o.amount_total"/>
+                                        </td>
+                                    </tr>
+                                </table>
+                            </div>
+                        </div>
+                    </div>
+                    <br />
+                    <p t-if="o.move_type in ('out_invoice', 'in_refund')">
+                        Please use the following communication for your payment : <b><span t-field="o.payment_reference"/></b>
+                    </p>
+                    <p t-if="o.narration" name="comment">
+                        <span t-field="o.narration"/>
+                    </p>
+                    <p t-if="o.invoice_payment_term_id" name="payment_term">
+                        <span t-field="o.invoice_payment_term_id.note"/>
+                    </p>
+                    <p t-if="o.fiscal_position_id.note" name="note">
+                        <span t-field="o.fiscal_position_id.note"/>
+                    </p>
+                    <div id="qrcode" t-if="(o.company_id.qr_code) and (o.currency_id.name == 'EUR') and (o.invoice_partner_bank_id.acc_number != False)">
+                        <p t-if="(o.invoice_partner_bank_id.qr_code_valid)">
+                            <strong class="text-center">Scan me with your banking app.</strong><br /><br />
+                            <img class="border border-dark rounded" t-att-src="o.invoice_partner_bank_id.build_qr_code_url(o.amount_residual, o.payment_reference or o.ref or o.name)"/>
+                        </p>
+                        <p t-if="(o.invoice_partner_bank_id.qr_code_valid == False)">
+                            <strong class="text-center">The SEPA QR Code information are not set correctly.</strong><br />
+                        </p>
+                    </div>
+                </div>
+
+                <t  t-set="bank_account" t-value="o.invoice_partner_bank_id or o.company_id.bank_ids[0]"/>
+                <style>
+                    .os_remittence_advice {
+                        page-break-inside: avoid;
+                    }
+                    .os_remittance_advice .os_remittance_advice_title {
+                        font-weight: 600;
+                    }
+                    .os_remittance_advice .os_remittance_table {
+                        width: 100%;
+                        border-bottom: 0;
+                    }
+                    .os_remittance_advice .os_remittance_table tr {
+                        border-bottom: 0;
+                    }
+                    .os_remittance_advice .os_remittance_table tr:last-child td {
+                        border-bottom: 0;
+                    }
+                    .os_remittance_advice .os_remittance_table td {
+                        padding: 1px 0;
+                        margin: 0;
+                    }
+                </style>
+                <t t-if="o.os_project">
+                    <div name="bank_details" class="os_remittance_advice" style="page-break-inside: avoid" t-if="o.move_type == 'out_invoice'">
+                        <h4 class="os_remittance_advice_title">Remittance advice</h4>
+                        <table class="os_remittance_table">
+                            <tr>
+                                <td><span style="font-size:1.1rem;font-weight:600">Please pay before the 20th of the month</span></td>
+                            </tr>
+                            <tr><td></td></tr>
+                            <tr>
+                                <td style="font-size:0.9rem">Please remit directly to our bank account and quote the invoice number</td>
+                                <td>Invoice number:</td>
+                                <td><span t-field="o.payment_reference"/></td>
+                            </tr>
+                            <tr>
+                                <td><span t-esc="bank_account.bank_name"></span></td>
+                                <td>From:</td>
+                                <td><span t-esc="o.partner_id.name"></span></td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <t t-if="bank_account.acc_type=='bank'">BANK:</t>
+                                    <t t-if="bank_account.acc_type=='iban'">IBAN:</t> <span t-esc="bank_account.acc_number"></span>
+                                </td>
+                                <td><b>Amount due:</b></td>
+                                <td><b>
+                                    <t t-if="outstanding.old_owing() != 0"><span style="color:red">$<span t-esc="'%.2f'%(o.amount_residual + outstanding.old_owing())"></span></span></t>
+                                    <t t-if="outstanding.old_owing() == 0"><span>$<span t-esc="'%.2f'%(o.amount_residual)"></span></span></t>
+                                </b></td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <t t-if="bank_account.acc_type=='bank'">SWIFT Code:</t>
+                                    <t t-if="bank_account.acc_type=='iban'">SWIFT/BIC:</t> <t t-esc="bank_account.bank_bic"/>
+                                </td>
+                                <td></td>
+                                <td></td>
+                            </tr>
+                        </table>
+                    </div>
+                </t>
+            </t>
+        </template>
+
+        <template id="report_invoice_document_with_payments" inherit_id="openstack_integration.report_invoice_document" primary="True">
+            <xpath expr="//div[@id='total']/div/table" position="inside">
+                <t t-set="payments_vals" t-value="o.sudo()._get_reconciled_info_JSON_values()"/>
+                <t t-foreach="payments_vals" t-as="payment_vals">
+                    <tr>
+                        <td>
+                            <i class="oe_form_field text-right oe_payment_label">Paid on <t t-esc="payment_vals['date']" t-options='{"widget": "date"}'/></i>
+                        </td>
+                        <td class="text-right">
+                            <span t-esc="payment_vals['amount']" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
+                        </td>
+                    </tr>
+                </t>
+                <t t-if="len(payments_vals) > 0">
+                    <tr class="border-black">
+                        <td><strong>Amount Due</strong></td>
+                        <td class="text-right">
+                            <span t-field="o.amount_residual"/>
+                        </td>
+                    </tr>
+                </t>
+            </xpath>
+        </template>
+
+        <template id="report_invoice_with_payments">
+            <t t-call="web.html_container">
+                <t t-foreach="docs" t-as="o">
+                    <t t-set="lang" t-value="o.invoice_user_id.sudo().lang if o.move_type in ('in_invoice', 'in_refund') else o.partner_id.lang"/>
+                    <t t-call="openstack_integration.report_invoice_document_with_payments" t-lang="lang"/>
+                </t>
+            </t>
+        </template>
+
+        <report
+                id="account_invoices"
+                model="account.move"
+                string="OpenStack Invoices"
+                report_type="qweb-pdf"
+                name="openstack_integration.report_invoice_with_payments"
+                file="openstack_integration.report_invoice_with_payments"
+                attachment="(object.state == 'posted') and ((object.name or 'INV').replace('/','_')+'.pdf')"
+                print_report_name="(object._get_report_base_filename())"
+                groups="account.group_account_invoice"
+        />
+
+    </data>
+</odoo>
diff --git a/views/res_partner_view.xml b/views/res_partner_view.xml
new file mode 100755
index 0000000..f3e4db7
--- /dev/null
+++ b/views/res_partner_view.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- OpenStack tab extension on partner form -->
+        <record id="view_partner_openstack_project_form" model="ir.ui.view">
+            <field name="name">res.partner.openstack.project.form</field>
+            <field name="model">res.partner</field>
+            <field name="inherit_id" ref="base.view_partner_form" />
+            <field name="arch" type="xml">
+                <xpath expr='//page[@name="sales_purchases"]' position="after">
+                    <page
+                        string="OpenStack"
+                        groups="openstack_integration.group_openstack_user,openstack_integration.group_openstack_manager">
+                        <group>
+                            <field name="stripe_customer_id" string="Stripe Customer ID" />
+                            <field name="os_customer_group" string="Customer Group" />
+                            <field name="os_reseller" string="Reseller" />
+                            <field name="os_trial" string="Trial" />
+                            <field name="os_referral" string="Referral" />
+                            <field name="os_is_cloud_framework_agreement_partner" string="CFA Customer" />
+                        </group>
+                        <group>
+                            <field name="os_projects" string="Projects" context="{'default_owner': id}">
+                                <tree string="Projects">
+                                    <field name="enabled" />
+                                    <field name="name" />
+                                    <field name="os_id" />
+                                </tree>
+                            </field>
+                            <field name="os_project_contacts" string="Project Contacts" context="{'default_partner': id}">
+                                <tree string="Project Contacts">
+                                    <field name="project" />
+                                    <field name="contact_type" />
+                                </tree>
+                            </field>
+                            <field name="os_referral_codes" string="Referral Codes" context="{'default_partner': id}">
+                                <tree string="Support Subscriptions">
+                                    <field name="code" />
+                                </tree>
+                            </field>
+                        </group>
+                    </page>
+                </xpath>
+            </field>
+        </record>
+
+    </data>
+</odoo>
diff --git a/views/reseller_view.xml b/views/reseller_view.xml
new file mode 100755
index 0000000..e422cc1
--- /dev/null
+++ b/views/reseller_view.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- form view for a openstack reseller -->
+        <record id="view_openstack_reseller_form" model="ir.ui.view">
+            <field name="name">openstack.reseller.form</field>
+            <field name="model">openstack.reseller</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Reseller">
+                    <group col="4">
+                        <field name="partner" />
+                        <field name="tier" />
+                        <field name="demo_project" />
+                    </group>
+                    <group col="4">
+                        <field name="hide_billing" />
+                        <field name="alternative_billing_url" />
+                        <field name="hide_support" />
+                        <field name="alternative_support_url" />
+                    </group>
+                    <group col="4">
+                        <field name="create_date" />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack resellers -->
+        <record id="view_openstack_reseller_tree" model="ir.ui.view">
+            <field name="name">openstack.reseller.tree</field>
+            <field name="model">openstack.reseller</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Resellers">
+                    <field name="partner" />
+                    <field name="tier" />
+                    <field name="demo_project" />
+                    <field name="create_date" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- Search functionality on resellers -->
+        <record id="view_openstack_reseller_filter" model="ir.ui.view">
+            <field name="name">openstack.reseller.select</field>
+            <field name="model">openstack.reseller</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Reseller">
+                    <field name="partner" />
+                    <field name="tier" />
+                    <field name="demo_project" />
+                </search>
+            </field>
+        </record>
+
+        <!-- action to open the openstack reseller list -->
+        <record id="action_openstack_reseller_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Resellers</field>
+            <field name="res_model">openstack.reseller</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack reseller -->
+        <menuitem
+            action="action_openstack_reseller_form"
+            id="menu_action_openstack_reseller_form"
+            parent="menu_openstack_main" sequence="7" />
+
+        <!-- form view for a openstack reseller_tier -->
+        <record id="view_openstack_reseller_tier_form" model="ir.ui.view">
+            <field name="name">openstack.reseller.tier.form</field>
+            <field name="model">openstack.reseller.tier</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Reseller Tier">
+                    <group col="4">
+                        <field name="name" />
+                        <field name="min_usage_threshold" />
+                    </group>
+                    <group col="4">
+                        <field name="discount_percent" />
+                        <field name="discount_product" />
+                    </group>
+                    <group col="4">
+                        <field name="free_support_hours" />
+                        <field name="free_monthly_credit_product" />
+                        <field name="free_monthly_credit" />
+                    </group>
+                    <group col="4">
+                        <field name="create_date" />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack reseller_tier -->
+        <record id="view_openstack_reseller_tier_tree" model="ir.ui.view">
+            <field name="name">openstack.reseller.tier.tree</field>
+            <field name="model">openstack.reseller.tier</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Reseller Tiers">
+                    <field name="name" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- action to open the openstack reseller_tier list -->
+        <record id="action_openstack_reseller_tier_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Reseller Tiers</field>
+            <field name="res_model">openstack.reseller.tier</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack reseller_tier -->
+        <menuitem
+            action="action_openstack_reseller_tier_form"
+            id="menu_action_openstack_reseller_tier_form"
+            parent="menu_openstack_configuration" sequence="3" />
+
+    </data>
+</odoo>
diff --git a/views/sale_order_view.xml b/views/sale_order_view.xml
new file mode 100755
index 0000000..aac46d8
--- /dev/null
+++ b/views/sale_order_view.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- Add project fields to an account move -->
+        <record id="view_order_form" model="ir.ui.view">
+            <field name="name">sale.order.form</field>
+            <field name="model">sale.order</field>
+            <field name="type">form</field>
+            <field name="inherit_id" ref="sale.view_order_form" />
+            <field name="arch" type="xml">
+                <data>
+
+                    <xpath expr='//page[@name="other_information"]' position="after">
+                        <page
+                            string="OpenStack"
+                            groups="openstack_integration.group_openstack_user,openstack_integration.group_openstack_manager">
+                            <group>
+                                <field name="os_project" string="OS Project" />
+                                <field name="os_is_cloud_framework_agreement_sale_order" string="CFA Sale Order" />
+                            </group>
+                        </page>
+                    </xpath>
+
+                    <xpath expr="//page[@name='order_lines']/field/tree/field[@name='product_id']" position="before">
+                        <field name="os_project" optional="hide" string="OS Project" groups="openstack_integration.group_openstack_user,openstack_integration.group_openstack_manager" />
+                    </xpath>
+
+                </data>
+            </field>
+        </record>
+
+    </data>
+</odoo>
diff --git a/views/support_subscription_view.xml b/views/support_subscription_view.xml
new file mode 100755
index 0000000..f607269
--- /dev/null
+++ b/views/support_subscription_view.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- form view for a openstack support_subscription -->
+        <record id="view_openstack_support_subscription_form" model="ir.ui.view">
+            <field name="name">openstack.support_subscription.form</field>
+            <field name="model">openstack.support_subscription</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Support Subscription">
+                    <group col="4">
+                        <field name="project" />
+                        <field name="partner" />
+                    </group>
+                    <group col="4">
+                        <field name="start_date" />
+                        <field name="end_date" />
+                    </group>
+                    <group col="4">
+                        <field name="support_subscription_type" />
+                        <field name="billing_type" />
+                    </group>
+                    <group col="4">
+                        <field name="create_date" />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack support_subscriptions -->
+        <record id="view_openstack_support_subscription_tree" model="ir.ui.view">
+            <field name="name">openstack.support_subscription.tree</field>
+            <field name="model">openstack.support_subscription</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Support Subscriptions">
+                    <field name="project" />
+                    <field name="partner" />
+                    <field name="start_date" />
+                    <field name="end_date" />
+                    <field name="support_subscription_type" />
+                    <field name="billing_type" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- Search functionality for support_subscription -->
+        <record id="view_openstack_support_subscription_filter" model="ir.ui.view">
+            <field name="name">openstack.support_subscription.select</field>
+            <field name="model">openstack.support_subscription</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Support Subscription">
+                    <field name="project" />
+                    <field name="support_subscription_type" />
+                </search>
+            </field>
+        </record>
+
+        <!-- action to open the openstack support_subscription list -->
+        <record id="action_openstack_support_subscription_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Support Subscriptions</field>
+            <field name="res_model">openstack.support_subscription</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack support_subscription -->
+        <menuitem
+            action="action_openstack_support_subscription_form"
+            id="menu_action_openstack_support_subscription_form"
+            parent="menu_openstack_main" sequence="4" />
+
+        <!-- form view for a openstack support_subscription type -->
+        <record id="view_openstack_support_subscription_type_form" model="ir.ui.view">
+            <field name="name">openstack.support_subscription.type.form</field>
+            <field name="model">openstack.support_subscription.type</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Support Subscription Types">
+                    <group col="4">
+                        <field name="name" />
+                        <field name="product" />
+                    </group>
+                    <group col="4">
+                        <field name="usage_percent" />
+                        <field name="create_date" />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack support_subscription type -->
+        <record id="view_openstack_support_subscription_type_tree" model="ir.ui.view">
+            <field name="name">openstack.support_subscription.type.tree</field>
+            <field name="model">openstack.support_subscription.type</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Support Subscription Types">
+                    <field name="name" />
+                    <field name="product" />
+                    <field name="usage_percent" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- action to open the openstack support_subscription type list -->
+        <record id="action_openstack_support_subscription_type_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Support Subscription Types</field>
+            <field name="res_model">openstack.support_subscription.type</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack support_subscription type -->
+        <menuitem
+            action="action_openstack_support_subscription_type_form"
+            id="menu_action_openstack_support_subscription_type_form"
+            parent="menu_openstack_configuration" sequence="2" />
+
+    </data>
+</odoo>
diff --git a/views/term_discount_view.xml b/views/term_discount_view.xml
new file mode 100755
index 0000000..63a7c9b
--- /dev/null
+++ b/views/term_discount_view.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- form view for a openstack term_discount -->
+        <record id="view_openstack_term_discount_form" model="ir.ui.view">
+            <field name="name">openstack.term_discount.form</field>
+            <field name="model">openstack.term_discount</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Term Discount">
+                    <group col="4">
+                        <field name="partner" />
+                        <field name="project" />
+                    </group>
+                    <group col="4">
+                        <field name="start_date" />
+                        <field name="end_date" />
+                        <field name="early_termination_date" />
+                    </group>
+                    <group col="4">
+                        <field name="min_commit" />
+                        <field name="discount_percent" />
+                    </group>
+                    <group col="4">
+                        <field name="superseded_by" />
+                        <field name="create_date" />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack term_discounts -->
+        <record id="view_openstack_term_discount_tree" model="ir.ui.view">
+            <field name="name">openstack.term_discount.tree</field>
+            <field name="model">openstack.term_discount</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Term Discounts">
+                    <field name="partner" />
+                    <field name="project" />
+                    <field name="start_date" />
+                    <field name="end_date" />
+                    <field name="min_commit" />
+                    <field name="discount_percent" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- Search functionality on term_discounts -->
+        <record id="view_openstack_term_discount_filter" model="ir.ui.view">
+            <field name="name">openstack.term_discount.select</field>
+            <field name="model">openstack.term_discount</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Term Discount">
+                    <field name="project" />
+                </search>
+            </field>
+        </record>
+
+        <!-- action to open the openstack term_discount list -->
+        <record id="action_openstack_term_discount_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Term Discounts</field>
+            <field name="res_model">openstack.term_discount</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack term_discount -->
+        <menuitem
+            action="action_openstack_term_discount_form"
+            id="menu_action_openstack_term_discount_form"
+            parent="menu_openstack_discounts" sequence="2" />
+
+    </data>
+</odoo>
diff --git a/views/trial_view.xml b/views/trial_view.xml
new file mode 100755
index 0000000..c2034e8
--- /dev/null
+++ b/views/trial_view.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- form view for a openstack trial -->
+        <record id="view_openstack_trial_form" model="ir.ui.view">
+            <field name="name">openstack.trial.form</field>
+            <field name="model">openstack.trial</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Trial">
+                    <group col="4">
+                        <field name="partner" />
+                        <field name="create_date" />
+                    </group>
+                    <group col="4">
+                        <field name="start_date" />
+                        <field name="end_date" />
+                    </group>
+                    <group col="4">
+                        <field name="account_suspended_on" />
+                        <field name="account_terminated_on" />
+                        <field name="account_upgraded_on" />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack trials -->
+        <record id="view_openstack_trial_tree" model="ir.ui.view">
+            <field name="name">openstack.trial.tree</field>
+            <field name="model">openstack.trial</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Trials">
+                    <field name="partner" />
+                    <field name="start_date" />
+                    <field name="end_date" />
+                    <field name="account_suspended_on" />
+                    <field name="account_terminated_on" />
+                    <field name="account_upgraded_on" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- Search functionality on openstack trials -->
+        <record id="view_openstack_trial_filter" model="ir.ui.view">
+            <field name="name">openstack.trial.select</field>
+            <field name="model">openstack.trial</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Trial">
+                    <field name="partner" />
+                </search>
+            </field>
+        </record>
+
+        <!-- action to open the openstack trial list -->
+        <record id="action_openstack_trial_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Trials</field>
+            <field name="res_model">openstack.trial</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack trial -->
+        <menuitem
+            action="action_openstack_trial_form"
+            id="menu_action_openstack_trial_form"
+            parent="menu_openstack_main" sequence="5" />
+
+    </data>
+</odoo>
diff --git a/views/volume_discount_view.xml b/views/volume_discount_view.xml
new file mode 100755
index 0000000..e0589d0
--- /dev/null
+++ b/views/volume_discount_view.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- form view for a openstack volume_discount_range -->
+        <record id="view_openstack_volume_discount_range_form" model="ir.ui.view">
+            <field name="name">openstack.volume_discount_range.form</field>
+            <field name="model">openstack.volume_discount_range</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Volume Discount Range">
+                    <group col="4">
+                        <field name="discount_percent" />
+                        <field name="min" />
+                        <field name="use_max" />
+                        <field name="max" />
+                        <field name="customer_group" />
+                    </group>
+                    <group col="4">
+                        <field name="create_date" />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack volume_discount_ranges -->
+        <record id="view_openstack_volume_discount_range_tree" model="ir.ui.view">
+            <field name="name">openstack.volume_discount_range.tree</field>
+            <field name="model">openstack.volume_discount_range</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Volume Discount Ranges">
+                    <field name="customer_group" />
+                    <field name="min" />
+                    <field name="max" />
+                    <field name="discount_percent" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- Search functionality on volume_discount_ranges -->
+        <record id="view_openstack_volume_discount_range_filter" model="ir.ui.view">
+            <field name="name">openstack.volume_discount_range.select</field>
+            <field name="model">openstack.volume_discount_range</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Volume Discount Range">
+                    <field name="customer_group" />
+                </search>
+            </field>
+        </record>
+
+        <!-- action to open the openstack volume_discount_range list -->
+        <record id="action_openstack_volume_discount_range_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Volume Discount Ranges</field>
+            <field name="res_model">openstack.volume_discount_range</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack volume_discount_range -->
+        <menuitem
+            action="action_openstack_volume_discount_range_form"
+            id="menu_action_openstack_volume_discount_range_form"
+            parent="menu_openstack_discounts" sequence="3" />
+
+    </data>
+</odoo>
diff --git a/views/voucher_code_view.xml b/views/voucher_code_view.xml
new file mode 100755
index 0000000..6672ce4
--- /dev/null
+++ b/views/voucher_code_view.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+
+        <!-- form view for a openstack voucher_code -->
+        <record id="view_openstack_voucher_code_form" model="ir.ui.view">
+            <field name="name">openstack.voucher_code.form</field>
+            <field name="model">openstack.voucher_code</field>
+            <field name="arch" type="xml">
+                <form string="OpenStack Voucher Code">
+                    <group col="4">
+                        <field name="code" />
+                        <field name="create_date" />
+                    </group>
+                    <group col="4">
+                        <field name="claimed" />
+                        <field name="expiry_date" />
+                        <field name="multi_use" />
+                    </group>
+                    <group col="4">
+                        <field name="sales_person" />
+                        <field name="customer_group" />
+                        <field name="tags" widget="many2many_tags" />
+                        <field name="quota_size" />
+                    </group>
+                    <group col="4">
+                        <field name="credit_amount" />
+                        <field name="credit_type" />
+                        <field name="credit_duration" />
+                    </group>
+                    <group col="4">
+                        <field name="grant_value" />
+                        <field name="grant_type" />
+                        <field name="grant_duration" />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <!-- tree view for all openstack voucher_codes -->
+        <record id="view_openstack_voucher_code_tree" model="ir.ui.view">
+            <field name="name">openstack.voucher_code.tree</field>
+            <field name="model">openstack.voucher_code</field>
+            <field name="arch" type="xml">
+                <tree string="OpenStack Voucher Codes">
+                    <field name="code" />
+                    <field name="claimed" />
+                    <field name="multi_use" />
+                    <field name="expiry_date" />
+                    <field name="create_date" />
+                </tree>
+            </field>
+        </record>
+
+        <!-- Search functionality on openstack voucher_codes -->
+        <record id="view_openstack_voucher_code_filter" model="ir.ui.view">
+            <field name="name">openstack.voucher_code.select</field>
+            <field name="model">openstack.voucher_code</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Voucher Code">
+                    <field name="code" />
+                    <field name="sales_person" />
+                    <field name="customer_group" />
+                    <field name="credit_type" />
+                    <field name="tags" widget="many2many_tags" />
+                </search>
+            </field>
+        </record>
+
+        <!-- action to open the openstack voucher_code list -->
+        <record id="action_openstack_voucher_code_form" model="ir.actions.act_window">
+            <field name="name">OpenStack Voucher Codes</field>
+            <field name="res_model">openstack.voucher_code</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+
+        <!-- Menu item for openstack voucher_code -->
+        <menuitem
+            action="action_openstack_voucher_code_form"
+            id="menu_action_openstack_voucher_code_form"
+            parent="menu_openstack_main" sequence="6" />
+
+    </data>
+</odoo>
