From 8bb25a4f6692de1501da49fec58aa42656df1743 Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Thu, 4 Dec 2025 15:08:30 +0100 Subject: [PATCH] Add OrganizationOrigin.invoice_grouping --- ...invoice_grouping_to_organization_origin.py | 43 +++++++++++++++++++ src/servala/core/models/__init__.py | 2 + src/servala/core/models/organization.py | 12 ++++++ 3 files changed, 57 insertions(+) create mode 100644 src/servala/core/migrations/0018_add_invoice_grouping_to_organization_origin.py diff --git a/src/servala/core/migrations/0018_add_invoice_grouping_to_organization_origin.py b/src/servala/core/migrations/0018_add_invoice_grouping_to_organization_origin.py new file mode 100644 index 0000000..2e8b97a --- /dev/null +++ b/src/servala/core/migrations/0018_add_invoice_grouping_to_organization_origin.py @@ -0,0 +1,43 @@ +# Generated by Django 5.2.9 on 2025-12-04 12:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0017_add_unit_and_convert_odoo_ids_to_charfield"), + ] + + operations = [ + migrations.AddField( + model_name="organizationorigin", + name="invoice_grouping", + field=models.CharField( + choices=[ + ("by_service", "By Service"), + ("by_organization", "By Organization"), + ], + default="by_service", + help_text="Determines how service instances are grouped on invoices.", + max_length=20, + verbose_name="Invoice Line Item Grouping", + ), + ), + migrations.AlterField( + model_name="computeplanassignment", + name="unit", + field=models.CharField( + choices=[ + ("hour", "Hour"), + ("day", "Day"), + ("month", "Month (30 days / 720 hours)"), + ("year", "Year"), + ], + default="hour", + help_text="Unit for the price (e.g., price per hour)", + max_length=10, + verbose_name="Billing unit", + ), + ), + ] diff --git a/src/servala/core/models/__init__.py b/src/servala/core/models/__init__.py index 4cb8fd7..2a2e9d4 100644 --- a/src/servala/core/models/__init__.py +++ b/src/servala/core/models/__init__.py @@ -1,6 +1,7 @@ from .odoo_cache import OdooObjectCache from .organization import ( BillingEntity, + InvoiceGroupingChoice, Organization, OrganizationInvitation, OrganizationMembership, @@ -30,6 +31,7 @@ __all__ = [ "ComputePlanAssignment", "ControlPlane", "ControlPlaneCRD", + "InvoiceGroupingChoice", "OdooObjectCache", "Organization", "OrganizationInvitation", diff --git a/src/servala/core/models/organization.py b/src/servala/core/models/organization.py index be4f587..f3d6c5d 100644 --- a/src/servala/core/models/organization.py +++ b/src/servala/core/models/organization.py @@ -382,6 +382,11 @@ class BillingEntity(ServalaModelMixin, models.Model): return data +class InvoiceGroupingChoice(models.TextChoices): + BY_SERVICE = "by_service", _("By Service") + BY_ORGANIZATION = "by_organization", _("By Organization") + + class OrganizationOrigin(ServalaModelMixin, models.Model): """ Every organization has an origin, though origins may be @@ -433,6 +438,13 @@ class OrganizationOrigin(ServalaModelMixin, models.Model): "Optional message to display instead of billing address (e.g., 'You will be invoiced by Exoscale')." ), ) + invoice_grouping = models.CharField( + max_length=20, + choices=InvoiceGroupingChoice.choices, + default=InvoiceGroupingChoice.BY_SERVICE, + verbose_name=_("Invoice Line Item Grouping"), + help_text=_("Determines how service instances are grouped on invoices. "), + ) class Meta: verbose_name = _("Organization origin")