diff --git a/latest_migrations.manifest b/latest_migrations.manifest index 4f5e422e17f82..c1df252f8573e 100644 --- a/latest_migrations.manifest +++ b/latest_migrations.manifest @@ -5,7 +5,7 @@ contenttypes: 0002_remove_content_type_name ee: 0016_rolemembership_organization_member otp_static: 0002_throttling otp_totp: 0002_auto_20190420_0723 -posthog: 0499_hog_function_type +posthog: 0500_create_group_billing_team sessions: 0001_initial social_django: 0010_uid_db_index two_factor: 0007_auto_20201201_1019 diff --git a/posthog/admin/admins/organization_admin.py b/posthog/admin/admins/organization_admin.py index 26430cd33cfec..50cb942584808 100644 --- a/posthog/admin/admins/organization_admin.py +++ b/posthog/admin/admins/organization_admin.py @@ -1,12 +1,29 @@ from django.conf import settings from django.contrib import admin +from django.core.management import call_command from django.utils.html import format_html +from django.urls import reverse from posthog.admin.inlines.organization_member_inline import OrganizationMemberInline from posthog.admin.inlines.project_inline import ProjectInline from posthog.admin.inlines.team_inline import TeamInline from posthog.admin.paginators.no_count_paginator import NoCountPaginator from posthog.models.organization import Organization +from django.urls import path +from django.shortcuts import render, redirect +from django.contrib import messages +from django import forms +from django.utils import timezone + + +class UsageReportForm(forms.Form): + report_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"})) + + def clean_report_date(self): + report_date = self.cleaned_data["report_date"] + if report_date > timezone.now().date(): + raise forms.ValidationError("The date cannot be in the future.") + return report_date class OrganizationAdmin(admin.ModelAdmin): @@ -69,3 +86,34 @@ def usage_posthog(self, organization: Organization): 'See usage on PostHog →', organization.id, ) + + def get_urls(self): + urls = super().get_urls() + custom_urls = [ + path( + "send-usage-report/", self.admin_site.admin_view(self.send_usage_report_view), name="send-usage-report" + ), + ] + return custom_urls + urls + + def send_usage_report_view(self, request): + if not request.user.groups.filter(name="Billing Team").exists(): + messages.error(request, "You are not authorized to send usage reports.") + return redirect(reverse("admin:posthog_organization_changelist")) + + if request.method == "POST": + form = UsageReportForm(request.POST) + if form.is_valid(): + report_date = form.cleaned_data["report_date"] + call_command("send_usage_report", f"--date={report_date.strftime('%Y-%m-%d')}", "--async=1") + messages.success(request, f"Usage report for date {report_date} was sent successfully.") + return redirect(reverse("admin:posthog_organization_changelist")) + else: + form = UsageReportForm() + + return render(request, "admin/posthog/organization/send_usage_report.html", {"form": form}) + + def changelist_view(self, request, extra_context=None): + extra_context = extra_context or {} + extra_context["show_usage_report_button"] = True + return super().changelist_view(request, extra_context=extra_context) diff --git a/posthog/admin/admins/user_admin.py b/posthog/admin/admins/user_admin.py index 5a16ed14016a5..7b2853517e6e3 100644 --- a/posthog/admin/admins/user_admin.py +++ b/posthog/admin/admins/user_admin.py @@ -49,7 +49,7 @@ class UserAdmin(DjangoUserAdmin): }, ), (_("Personal info"), {"fields": ("first_name", "last_name")}), - (_("Permissions"), {"fields": ("is_active", "is_staff")}), + (_("Permissions"), {"fields": ("is_active", "is_staff", "groups")}), (_("Important dates"), {"fields": ("last_login", "date_joined")}), (_("Toolbar authentication"), {"fields": ("temporary_token",)}), ) diff --git a/posthog/migrations/0500_create_group_billing_team.py b/posthog/migrations/0500_create_group_billing_team.py new file mode 100644 index 0000000000000..4778d404e1fa5 --- /dev/null +++ b/posthog/migrations/0500_create_group_billing_team.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.15 on 2024-10-25 19:31 + +from django.db import migrations +from django.contrib.auth.models import Group + + +def create_billing_team_group(apps, schema_editor): + Group.objects.get_or_create(name="Billing Team") + + +def reverse_create_billing_team_group(apps, schema_editor): + Group.objects.filter(name="Billing Team").delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("posthog", "0499_hog_function_type"), + ] + + operations = [ + migrations.RunPython(create_billing_team_group, reverse_create_billing_team_group), + ] diff --git a/posthog/templates/admin/posthog/organization/change_list.html b/posthog/templates/admin/posthog/organization/change_list.html new file mode 100644 index 0000000000000..fe356f7d7bb90 --- /dev/null +++ b/posthog/templates/admin/posthog/organization/change_list.html @@ -0,0 +1,11 @@ +{% extends "admin/change_list.html" %} +{% load i18n admin_urls %} + +{% block object-tools-items %} + {% if show_usage_report_button %} +
  • + Send Usage Report +
  • + {% endif %} + {{ block.super }} +{% endblock %} diff --git a/posthog/templates/admin/posthog/organization/send_usage_report.html b/posthog/templates/admin/posthog/organization/send_usage_report.html new file mode 100644 index 0000000000000..6e7049911f602 --- /dev/null +++ b/posthog/templates/admin/posthog/organization/send_usage_report.html @@ -0,0 +1,21 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_urls %} + +{% block content %} +
    +
    + {% csrf_token %} +
    + {% for field in form %} +
    + {{ field.errors }} + {{ field.label_tag }} {{ field }} +
    + {% endfor %} +
    +
    + +
    +
    +
    +{% endblock %} \ No newline at end of file