diff --git a/hub/services/admin/pricing.py b/hub/services/admin/pricing.py index 19eba5f..7ee0b51 100644 --- a/hub/services/admin/pricing.py +++ b/hub/services/admin/pricing.py @@ -3,8 +3,12 @@ Admin classes for pricing models including compute plans, storage plans, and VSH """ import re -from django.contrib import admin +from django.contrib import admin, messages +from django.contrib.admin import helpers from django.utils.html import format_html +from django import forms +from django.shortcuts import render +from django.http import HttpResponseRedirect from adminsortable2.admin import SortableAdminMixin from import_export.admin import ImportExportModelAdmin from import_export import resources @@ -27,6 +31,8 @@ from ..models import ( Service, ) +from ..models.base import Term + def natural_sort_key(obj): """Extract numeric parts for natural sorting""" @@ -123,6 +129,41 @@ class ComputePlanResource(resources.ModelResource): ) +class MassUpdateComputePlanForm(forms.Form): + """Form for mass updating ComputePlan fields""" + + active = forms.ChoiceField( + choices=[("", "-- No change --"), ("True", "Active"), ("False", "Inactive")], + required=False, + help_text="Set active status for selected compute plans", + ) + + term = forms.ChoiceField( + choices=[("", "-- No change --")] + Term.choices, + required=False, + help_text="Set billing term for selected compute plans", + ) + + group = forms.ModelChoiceField( + queryset=ComputePlanGroup.objects.all(), + required=False, + empty_label="-- No change --", + help_text="Set group for selected compute plans", + ) + + valid_from = forms.DateField( + widget=forms.DateInput(attrs={"type": "date"}), + required=False, + help_text="Set valid from date for selected compute plans", + ) + + valid_to = forms.DateField( + widget=forms.DateInput(attrs={"type": "date"}), + required=False, + help_text="Set valid to date for selected compute plans", + ) + + @admin.register(ComputePlan) class ComputePlanAdmin(ImportExportModelAdmin): """Admin configuration for ComputePlan model with import/export functionality""" @@ -141,6 +182,7 @@ class ComputePlanAdmin(ImportExportModelAdmin): search_fields = ("name", "cloud_provider__name", "group__name") list_filter = ("active", "cloud_provider", "group") inlines = [ComputePlanPriceInline] + actions = ["mass_update_compute_plans"] def changelist_view(self, request, extra_context=None): """Override changelist view to apply natural sorting""" @@ -164,6 +206,80 @@ class ComputePlanAdmin(ImportExportModelAdmin): display_prices.short_description = "Prices (Amount Currency)" + def mass_update_compute_plans(self, request, queryset): + """Admin action to mass update compute plan fields""" + if request.POST.get("post"): + # Process the form submission + form = MassUpdateComputePlanForm(request.POST) + if form.is_valid(): + updated_count = 0 + updated_fields = [] + + # Prepare update data + update_data = {} + + # Handle active field + if form.cleaned_data["active"]: + update_data["active"] = form.cleaned_data["active"] == "True" + updated_fields.append("active") + + # Handle term field + if form.cleaned_data["term"]: + update_data["term"] = form.cleaned_data["term"] + updated_fields.append("term") + + # Handle group field + if form.cleaned_data["group"]: + update_data["group"] = form.cleaned_data["group"] + updated_fields.append("group") + + # Handle valid_from field + if form.cleaned_data["valid_from"]: + update_data["valid_from"] = form.cleaned_data["valid_from"] + updated_fields.append("valid_from") + + # Handle valid_to field + if form.cleaned_data["valid_to"]: + update_data["valid_to"] = form.cleaned_data["valid_to"] + updated_fields.append("valid_to") + + # Perform the bulk update + if update_data: + updated_count = queryset.update(**update_data) + + # Create success message + field_list = ", ".join(updated_fields) + self.message_user( + request, + f"Successfully updated {updated_count} compute plan(s). " + f"Updated fields: {field_list}", + messages.SUCCESS, + ) + else: + self.message_user( + request, "No fields were selected for update.", messages.WARNING + ) + + return HttpResponseRedirect(request.get_full_path()) + else: + # Show the form + form = MassUpdateComputePlanForm() + + # Render the mass update template + return render( + request, + "admin/mass_update_compute_plans.html", + { + "form": form, + "queryset": queryset, + "action_checkbox_name": helpers.ACTION_CHECKBOX_NAME, + "opts": self.model._meta, + "title": f"Mass Update {queryset.count()} Compute Plans", + }, + ) + + mass_update_compute_plans.short_description = "Mass update selected compute plans" + class VSHNAppCatBaseFeeInline(admin.TabularInline): """Inline admin for VSHNAppCatBaseFee model""" diff --git a/hub/services/templates/admin/mass_update_compute_plans.html b/hub/services/templates/admin/mass_update_compute_plans.html new file mode 100644 index 0000000..df2c558 --- /dev/null +++ b/hub/services/templates/admin/mass_update_compute_plans.html @@ -0,0 +1,126 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_urls static admin_modify %} + +{% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +
+
+

{{ title }}

+
+
+

You are about to update {{ queryset.count }} compute plan(s). Please select the fields you want to update:

+ + + + + +
+ {% csrf_token %} + + + {% for obj in queryset %} + + {% endfor %} + + + + +
+ +
+ {{ form.active }} + {% if form.active.help_text %} +
{{ form.active.help_text }}
+ {% endif %} +
+
+ +
+ +
+ {{ form.term }} + {% if form.term.help_text %} +
{{ form.term.help_text }}
+ {% endif %} +
+
+ +
+ +
+ {{ form.group }} + {% if form.group.help_text %} +
{{ form.group.help_text }}
+ {% endif %} +
+
+ +
+ +
+ {{ form.valid_from }} + {% if form.valid_from.help_text %} +
{{ form.valid_from.help_text }}
+ {% endif %} +
+
+ +
+ +
+ {{ form.valid_to }} + {% if form.valid_to.help_text %} +
{{ form.valid_to.help_text }}
+ {% endif %} +
+
+ + +
+
+ + + Cancel + +
+
+
+
+
+ + +{% endblock %} \ No newline at end of file