Coverage for app/venv/lib/python3.14/site-packages/weblate/billing/views.py: 0%
86 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
1# Copyright © Michal Čihař <michal@weblate.org>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
4from __future__ import annotations
6from datetime import timedelta
7from typing import TYPE_CHECKING
9from django.conf import settings
10from django.contrib.auth.decorators import login_required
11from django.core.exceptions import PermissionDenied
12from django.http import FileResponse, Http404
13from django.shortcuts import get_object_or_404, redirect, render
14from django.utils import timezone
16from weblate.accounts.views import mail_admins_contact
17from weblate.billing.forms import HostingForm
18from weblate.billing.models import Billing, BillingEvent, Invoice, Plan
19from weblate.utils.views import show_form_errors
21if TYPE_CHECKING:
22 from weblate.auth.models import AuthenticatedHttpRequest
24HOSTING_TEMPLATE = """
25%(name)s <%(email)s> wants to host %(project)s
27Project: %(project)s
28Website: %(url)s
30Message:
32%(message)s
34Please review at https://hosted.weblate.org%(billing_url)s
35"""
38@login_required
39def download_invoice(request: AuthenticatedHttpRequest, pk):
40 """Download invoice PDF."""
41 invoice = get_object_or_404(Invoice, pk=pk)
43 filename = invoice.full_filename
45 if not filename:
46 msg = "No reference!"
47 raise Http404(msg)
49 if not request.user.has_perm("billing.view", invoice.billing):
50 raise PermissionDenied
52 if not invoice.filename_valid:
53 msg = f"File {invoice.filename} does not exist!"
54 raise Http404(msg)
56 return FileResponse(
57 open(filename, "rb"),
58 as_attachment=True,
59 filename=invoice.filename,
60 content_type="application/pdf",
61 )
64def handle_post(request: AuthenticatedHttpRequest, billing) -> None:
65 if "extend" in request.POST and request.user.has_perm("billing.manage"):
66 now = timezone.now()
67 if billing.is_trial:
68 billing.state = Billing.STATE_TRIAL
69 if billing.expiry and billing.expiry > now:
70 billing.expiry += timedelta(days=14)
71 else:
72 billing.expiry = now + timedelta(days=14)
73 billing.removal = None
74 billing.save(update_fields=["expiry", "removal", "state"])
75 elif billing.removal:
76 billing.removal = now + timedelta(days=14)
77 billing.save(update_fields=["removal"])
78 billing.billinglog_set.create(
79 event=BillingEvent.EXTENDED_TRIAL, user=request.user
80 )
81 elif "recurring" in request.POST:
82 if "recurring" in billing.payment:
83 del billing.payment["recurring"]
84 billing.save()
85 billing.billinglog_set.create(
86 event=BillingEvent.DISABLED_RECURRING, user=request.user
87 )
88 elif "terminate" in request.POST:
89 billing.state = Billing.STATE_TERMINATED
90 billing.expiry = None
91 billing.removal = None
92 billing.save()
93 billing.billinglog_set.create(event=BillingEvent.TERMINATED, user=request.user)
94 elif billing.valid_libre:
95 if "approve" in request.POST and request.user.has_perm("billing.manage"):
96 billing.state = Billing.STATE_ACTIVE
97 billing.plan = Plan.objects.get(slug="libre")
98 billing.removal = None
99 billing.save(update_fields=["state", "plan", "removal"])
100 billing.billinglog_set.create(
101 event=BillingEvent.LIBRE_APPROVED, user=request.user
102 )
103 elif "request" in request.POST and billing.is_libre_trial:
104 form = HostingForm(request.POST)
105 if form.is_valid():
106 project = billing.projects.get()
107 subject = f"Hosting request for {project}"
108 billing.payment["libre_request"] = True
109 billing.save(update_fields=["payment"])
110 billing.billinglog_set.create(
111 event=BillingEvent.LIBRE_REQUEST, summary=subject, user=request.user
112 )
113 mail_admins_contact(
114 request,
115 subject=subject,
116 message=HOSTING_TEMPLATE,
117 context={
118 "billing": billing,
119 "name": request.user.full_name,
120 "email": request.user.email,
121 "project": project,
122 "url": project.web,
123 "message": form.cleaned_data["message"],
124 "billing_url": billing.get_absolute_url(),
125 },
126 name=request.user.get_visible_name(),
127 email=request.user.email,
128 to=settings.ADMINS_HOSTING,
129 )
130 else:
131 show_form_errors(request, form)
134@login_required
135def overview(request: AuthenticatedHttpRequest):
136 billings = Billing.objects.for_user(request.user).prefetch_related(
137 "plan", "projects", "invoice_set"
138 )
139 if not request.user.has_perm("billing.manage") and len(billings) == 1:
140 return redirect(billings[0])
141 return render(
142 request,
143 "billing/overview.html",
144 {
145 "billings": billings,
146 "active_billing_count": billings.filter(
147 state__in=(Billing.STATE_ACTIVE, Billing.STATE_TRIAL)
148 ).count(),
149 },
150 )
153@login_required
154def detail(request: AuthenticatedHttpRequest, pk):
155 billing = get_object_or_404(Billing, pk=pk)
157 if not request.user.has_perm("billing.view", billing):
158 raise PermissionDenied
160 if request.method == "POST":
161 handle_post(request, billing)
162 return redirect(billing)
164 return render(
165 request,
166 "billing/detail.html",
167 {"billing": billing, "hosting_form": HostingForm()},
168 )