From cbdbc253e8f5eff38598aed337b8035ab7557040 Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Fri, 7 Nov 2025 11:21:40 +0100 Subject: [PATCH 1/2] Read-only billing address ref #246 --- src/servala/core/admin.py | 8 +- src/servala/core/models/organization.py | 14 ++ .../frontend/organizations/update.html | 135 ++++++++++-------- 3 files changed, 95 insertions(+), 62 deletions(-) diff --git a/src/servala/core/admin.py b/src/servala/core/admin.py index fb64a2b..39fbab9 100644 --- a/src/servala/core/admin.py +++ b/src/servala/core/admin.py @@ -86,7 +86,13 @@ class BillingEntityAdmin(admin.ModelAdmin): @admin.register(OrganizationOrigin) class OrganizationOriginAdmin(admin.ModelAdmin): - list_display = ("name", "billing_entity", "default_odoo_sale_order_id") + list_display = ( + "name", + "billing_entity", + "default_odoo_sale_order_id", + "hide_billing_address", + ) + list_filter = ("hide_billing_address",) search_fields = ("name",) autocomplete_fields = ("billing_entity",) filter_horizontal = ("limit_cloudproviders",) diff --git a/src/servala/core/models/organization.py b/src/servala/core/models/organization.py index 09205dc..be4f587 100644 --- a/src/servala/core/models/organization.py +++ b/src/servala/core/models/organization.py @@ -419,6 +419,20 @@ class OrganizationOrigin(ServalaModelMixin, models.Model): "If set, this sale order will be used for new organizations with this origin." ), ) + hide_billing_address = models.BooleanField( + default=False, + verbose_name=_("Hide Billing Address"), + help_text=_( + "If enabled, the billing address will not be shown in the organization details view." + ), + ) + billing_message = models.TextField( + blank=True, + verbose_name=_("Billing Message"), + help_text=_( + "Optional message to display instead of billing address (e.g., 'You will be invoiced by Exoscale')." + ), + ) class Meta: verbose_name = _("Organization origin") diff --git a/src/servala/frontend/templates/frontend/organizations/update.html b/src/servala/frontend/templates/frontend/organizations/update.html index 73c2c69..bc4edf8 100644 --- a/src/servala/frontend/templates/frontend/organizations/update.html +++ b/src/servala/frontend/templates/frontend/organizations/update.html @@ -156,72 +156,85 @@ - {% if form.instance.billing_entity and form.instance.billing_entity.odoo_data.invoice_address %} + {% if not form.instance.origin.hide_billing_address %} + {% if form.instance.billing_entity and form.instance.billing_entity.odoo_data.invoice_address %} +
+
+

{% translate "Billing Address" %}

+ {% if form.instance.has_inherited_billing_entity %} +

+ {% translate "This billing address cannot be modified." %} +

+ {% endif %} +
+
+
+ {% with odoo_data=form.instance.billing_entity.odoo_data %} +
+ + + {% if odoo_data.invoice_address %} + + + + + + + + + + {% if odoo_data.invoice_address.street2 %} + + + + + {% endif %} + + + + + + + + + + + + + + + + {% endif %} + +
+ {% translate "Invoice Contact Name" %} + {{ odoo_data.invoice_address.name|default:"" }}
+ {% translate "Street" %} + {{ odoo_data.invoice_address.street|default:"" }}
+ {% translate "Street 2" %} + {{ odoo_data.invoice_address.street2 }}
+ {% translate "City" %} + {{ odoo_data.invoice_address.city|default:"" }}
+ {% translate "ZIP Code" %} + {{ odoo_data.invoice_address.zip|default:"" }}
+ {% translate "Country" %} + {{ odoo_data.invoice_address.country_id.1|default:"" }}
+ {% translate "Invoice Email" %} + {{ odoo_data.invoice_address.email|default:"" }}
+
+ {% endwith %} +
+
+
+ {% endif %} + {% elif form.instance.origin.billing_message %}
-

{% translate "Billing Address" %}

- {% if form.instance.has_inherited_billing_entity %} -

- {% translate "This billing address cannot be modified." %} -

- {% endif %} +

{% translate "Billing Information" %}

- {% with odoo_data=form.instance.billing_entity.odoo_data %} -
- - - {% if odoo_data.invoice_address %} - - - - - - - - - - {% if odoo_data.invoice_address.street2 %} - - - - - {% endif %} - - - - - - - - - - - - - - - - {% endif %} - -
- {% translate "Invoice Contact Name" %} - {{ odoo_data.invoice_address.name|default:"" }}
- {% translate "Street" %} - {{ odoo_data.invoice_address.street|default:"" }}
- {% translate "Street 2" %} - {{ odoo_data.invoice_address.street2 }}
- {% translate "City" %} - {{ odoo_data.invoice_address.city|default:"" }}
- {% translate "ZIP Code" %} - {{ odoo_data.invoice_address.zip|default:"" }}
- {% translate "Country" %} - {{ odoo_data.invoice_address.country_id.1|default:"" }}
- {% translate "Invoice Email" %} - {{ odoo_data.invoice_address.email|default:"" }}
-
- {% endwith %} +

{{ form.instance.origin.billing_message }}

-- 2.49.1 From b3ecae6fd995e71bb20be94f9e39d1507dd180dd Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Wed, 12 Nov 2025 10:34:55 +0100 Subject: [PATCH 2/2] Add missing migration --- .../migrations/0014_hide_billing_address.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/servala/core/migrations/0014_hide_billing_address.py diff --git a/src/servala/core/migrations/0014_hide_billing_address.py b/src/servala/core/migrations/0014_hide_billing_address.py new file mode 100644 index 0000000..7295f3a --- /dev/null +++ b/src/servala/core/migrations/0014_hide_billing_address.py @@ -0,0 +1,41 @@ +# Generated by Django 5.2.8 on 2025-11-12 09:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0013_add_form_config"), + ] + + operations = [ + migrations.AddField( + model_name="organizationorigin", + name="billing_message", + field=models.TextField( + blank=True, + help_text="Optional message to display instead of billing address (e.g., 'You will be invoiced by Exoscale').", + verbose_name="Billing Message", + ), + ), + migrations.AddField( + model_name="organizationorigin", + name="hide_billing_address", + field=models.BooleanField( + default=False, + help_text="If enabled, the billing address will not be shown in the organization details view.", + verbose_name="Hide Billing Address", + ), + ), + migrations.AlterField( + model_name="controlplane", + name="user_info", + field=models.JSONField( + blank=True, + help_text='Array of info objects: [{"title": "…", "content": "…", "help_text": "…"}]. The help_text field is optional and will be shown as a hover popover on an info icon.', + null=True, + verbose_name="User Information", + ), + ), + ] -- 2.49.1