Remove poetry #93019
@ -34,7 +34,7 @@ python3.10 -m venv .venv ; source .venv/bin/activate
|
|||||||
After `virtualenv` has been created and activated, continue with the following:
|
After `virtualenv` has been created and activated, continue with the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
pip install -r requirements_dev.txt # Install dependencies.
|
pip install -e .[dev] # Install package and its development dependencies.
|
||||||
cp looper_example_project/settings.example.py looper_example_project/settings.py # Adjust settings.py as needed.
|
cp looper_example_project/settings.example.py looper_example_project/settings.py # Adjust settings.py as needed.
|
||||||
# Make sure you have your database setup according to your settings.py
|
# Make sure you have your database setup according to your settings.py
|
||||||
./manage.py migrate # Create database tables and run migrations.
|
./manage.py migrate # Create database tables and run migrations.
|
||||||
|
@ -1,159 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
|
||||||
from django.urls import reverse, reverse_lazy
|
|
||||||
from django.views.generic import UpdateView, ListView, DeleteView, DetailView, View
|
|
||||||
import stripe
|
|
||||||
|
|
||||||
from .. import forms, models, pdf, stripe_utils
|
|
||||||
from looper import admin_log
|
|
||||||
import looper.views.checkout
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def settings_receipts(request):
|
|
||||||
order_set = request.user.customer.order_set
|
|
||||||
orders = order_set.paid().select_related('subscription__plan')
|
|
||||||
context = {
|
|
||||||
'orders': orders,
|
|
||||||
'can_woosh': request.user.is_staff and request.user.has_perm('looper.change_order')
|
|
||||||
}
|
|
||||||
|
|
||||||
return render(request, 'looper/settings/receipts.html', context=context)
|
|
||||||
|
|
||||||
|
|
||||||
class ReceiptView(LoginRequiredMixin, DetailView):
|
|
||||||
template_name = 'looper/settings/receipt.html'
|
|
||||||
pk_url_kwarg = 'order_id'
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
"""Limit the allowed order IDs to the ones owned by the user.
|
|
||||||
|
|
||||||
Staff users with the permission to view orders can access
|
|
||||||
all orders of all users.
|
|
||||||
"""
|
|
||||||
user = self.request.user
|
|
||||||
if user.is_staff and user.has_perm('looper.view_order'):
|
|
||||||
return looper.models.Order.objects.paid()
|
|
||||||
return user.customer.order_set.paid()
|
|
||||||
|
|
||||||
|
|
||||||
class ReceiptPDFView(ReceiptView):
|
|
||||||
template_name = 'looper/settings/receipt_pdf.html'
|
|
||||||
response_class = pdf.PDFResponse
|
|
||||||
|
|
||||||
|
|
||||||
class BillingAddressView(LoginRequiredMixin, UpdateView):
|
|
||||||
template_name = 'looper/settings/billing_address.html'
|
|
||||||
form_class = forms.AddressForm
|
|
||||||
model = models.Address
|
|
||||||
success_url = reverse_lazy('looper:settings_billing_info')
|
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
|
||||||
return self.request.user.customer.billing_address
|
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethodsView(LoginRequiredMixin, ListView):
|
|
||||||
template_name = 'looper/settings/payment_methods.html'
|
|
||||||
model = models.PaymentMethod
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
superset = super().get_queryset()
|
|
||||||
return superset.filter(customer=self.request.user.customer, is_deleted=False)
|
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethodDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
||||||
template_name = 'looper/settings/payment_method_delete.html'
|
|
||||||
model = models.PaymentMethod
|
|
||||||
success_url = reverse_lazy('looper:payment_methods')
|
|
||||||
|
|
||||||
log = log.getChild('PaymentMethodDeleteView')
|
|
||||||
|
|
||||||
def has_permission(self) -> bool:
|
|
||||||
payment_method = self.get_object()
|
|
||||||
if self.request.user.customer.pk == payment_method.customer.pk:
|
|
||||||
return True
|
|
||||||
self.log.error(
|
|
||||||
"User %d tried to delete someone else's payment method %d",
|
|
||||||
self.request.user.pk,
|
|
||||||
payment_method.pk,
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
|
||||||
payment_method = self.get_object()
|
|
||||||
message = (
|
|
||||||
'User pk=%s is deleting payment method pk=%s',
|
|
||||||
self.request.user.id,
|
|
||||||
payment_method.pk,
|
|
||||||
)
|
|
||||||
admin_log.attach_log_entry(payment_method, message[0] % message[1:])
|
|
||||||
|
|
||||||
self.log.info(message)
|
|
||||||
return super().delete(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethodChangeView(LoginRequiredMixin, View):
|
|
||||||
"""Redirect to Stripe setup to switch a subscription's payment method."""
|
|
||||||
|
|
||||||
log = log.getChild('PaymentMethodChangeView')
|
|
||||||
subscription: models.Subscription
|
|
||||||
success_url = '/'
|
|
||||||
cancel_url = 'settings_home'
|
|
||||||
|
|
||||||
def get_cancel_url(self):
|
|
||||||
return reverse(self.cancel_url)
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
self.subscription = get_object_or_404(
|
|
||||||
request.user.customer.subscription_set, pk=kwargs['subscription_id']
|
|
||||||
)
|
|
||||||
success_url = self.request.build_absolute_uri(
|
|
||||||
reverse(
|
|
||||||
self.success_url,
|
|
||||||
kwargs={
|
|
||||||
'subscription_id': self.subscription.id,
|
|
||||||
'stripe_session_id': 'CHECKOUT_SESSION_ID',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
# we have to do it to avoid uri-encoding of curly braces,
|
|
||||||
# otherwise stripe doesn't do the template substitution
|
|
||||||
success_url = success_url.replace('CHECKOUT_SESSION_ID', '{CHECKOUT_SESSION_ID}', 1)
|
|
||||||
cancel_url = self.request.build_absolute_uri(self.get_cancel_url())
|
|
||||||
session = stripe_utils.setup_stripe_payment_method(
|
|
||||||
self.subscription.currency.lower(),
|
|
||||||
request.user.customer,
|
|
||||||
success_url,
|
|
||||||
cancel_url,
|
|
||||||
setup_intent_metadata={
|
|
||||||
'subscription_id': self.subscription.id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return redirect(session.url)
|
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethodChangeDoneView(LoginRequiredMixin, View):
|
|
||||||
log = log.getChild('PaymentMethodChangeDoneView')
|
|
||||||
subscription: models.Subscription
|
|
||||||
success_url = '/'
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
self.subscription = get_object_or_404(
|
|
||||||
request.user.customer.subscription_set, pk=kwargs['subscription_id']
|
|
||||||
)
|
|
||||||
stripe_session_id = self.kwargs['stripe_session_id']
|
|
||||||
stripe_session = stripe.checkout.Session.retrieve(
|
|
||||||
stripe_session_id,
|
|
||||||
expand=['setup_intent', 'setup_intent.payment_method']
|
|
||||||
)
|
|
||||||
setup_intent = stripe_session.setup_intent
|
|
||||||
assert stripe_session.status == 'complete'
|
|
||||||
assert setup_intent.status == 'succeeded'
|
|
||||||
stripe_utils.process_setup_intent_for_subscription(setup_intent, self.subscription)
|
|
||||||
|
|
||||||
return redirect(self.success_url)
|
|
118
pyproject.toml
118
pyproject.toml
@ -1,8 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "looper"
|
name = "looper"
|
||||||
version = "3.4.0rc1"
|
version = "3.4.0rc2"
|
||||||
description = "Billing system used in Blender Development Fund and Blender Studio website."
|
description = "Billing system used in Blender Development Fund and Blender Studio website."
|
||||||
readme = {file = "README.md", content-type = "text/markdown"}
|
|
||||||
authors = [
|
authors = [
|
||||||
{name = "Francesco Siddi", email = "francesco@blender.org"},
|
{name = "Francesco Siddi", email = "francesco@blender.org"},
|
||||||
{name = "Pablo Vazquez", email = "pablo@blender.org"},
|
{name = "Pablo Vazquez", email = "pablo@blender.org"},
|
||||||
@ -13,17 +12,116 @@ authors = [
|
|||||||
]
|
]
|
||||||
license = {text = "GPL-3.0"}
|
license = {text = "GPL-3.0"}
|
||||||
requires-python = "== 3.10.*"
|
requires-python = "== 3.10.*"
|
||||||
|
dependencies = [
|
||||||
|
"appdirs==1.4.4",
|
||||||
|
"arabic-reshaper==3.0.0",
|
||||||
|
"asgiref==3.6.0",
|
||||||
|
"asn1crypto==1.5.1",
|
||||||
|
"attrs==19.3.0",
|
||||||
|
"babel==2.12.1",
|
||||||
|
"bleach==3.3.1",
|
||||||
|
"braintree==4.17.1",
|
||||||
|
"cached-property==1.5.2",
|
||||||
|
"certifi==2022.12.7",
|
||||||
|
"cffi==1.15.1",
|
||||||
|
"charset-normalizer==3.0.1",
|
||||||
|
"click==8.1.3",
|
||||||
|
"colorhash==1.2.1",
|
||||||
|
"cryptography==39.0.0",
|
||||||
|
"cssselect2==0.7.0",
|
||||||
|
"defusedxml==0.7.1",
|
||||||
|
"django-countries==7.5.1",
|
||||||
|
"django-nested-admin==4.0.2",
|
||||||
|
"django-pipeline==3.1.0",
|
||||||
|
"django==4.2.13",
|
||||||
|
"geoip2==3.0.0",
|
||||||
|
"html5lib==1.1",
|
||||||
|
"idna==3.4",
|
||||||
|
"isodate==0.6.1",
|
||||||
|
"lxml==4.9.2",
|
||||||
|
"maxminddb==2.2.0",
|
||||||
|
"oscrypto==1.3.0",
|
||||||
|
"packaging==23.0",
|
||||||
|
"pillow==9.4.0",
|
||||||
|
"pycparser==2.21",
|
||||||
|
"pyhanko-certvalidator==0.20.1",
|
||||||
|
"pyhanko==0.17.0",
|
||||||
|
"pypdf==3.5.0",
|
||||||
|
"python-bidi==0.4.2",
|
||||||
|
"python-dateutil==2.8.2",
|
||||||
|
"python-monkey-business==1.0.0",
|
||||||
|
"python-stdnum==1.18",
|
||||||
|
"pytz-deprecation-shim==0.1.0.post0",
|
||||||
|
"pytz==2022.7.1",
|
||||||
|
"pyyaml==6.0",
|
||||||
|
"qrcode==7.4.2",
|
||||||
|
"reportlab==3.6.9",
|
||||||
|
"requests-file==1.5.1",
|
||||||
|
"requests-toolbelt==0.10.1",
|
||||||
|
"requests==2.31.0",
|
||||||
|
"six==1.16.0",
|
||||||
|
"sqlparse==0.4.3",
|
||||||
|
"stripe==7.1.0",
|
||||||
|
"svglib==1.5.1",
|
||||||
|
"tinycss2==1.2.1",
|
||||||
|
"tqdm==4.64.1",
|
||||||
|
"typing-extensions==4.8.0",
|
||||||
|
"tzdata==2022.7",
|
||||||
|
"tzlocal==4.2",
|
||||||
|
"uritools==4.0.1",
|
||||||
|
"urllib3==1.26.14",
|
||||||
|
"webencodings==0.5.1",
|
||||||
|
"xhtml2pdf==0.2.9",
|
||||||
|
"zeep==4.0.0",
|
||||||
|
]
|
||||||
|
readme = {file = "README.md", content-type = "text/markdown"}
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"black==22.12.0",
|
||||||
|
"django-stubs-ext==0.7.0",
|
||||||
|
"django-stubs==1.13.1",
|
||||||
|
"factory-boy==3.3.0",
|
||||||
|
"faker==20.1.0",
|
||||||
|
"flake8-implicit-str-concat==0.1.0",
|
||||||
|
"flake8==3.9.2",
|
||||||
|
"ghp-import==2.1.0",
|
||||||
|
"importlib-metadata==6.0.0",
|
||||||
|
"jinja2==3.1.2",
|
||||||
|
"markdown==3.3.7",
|
||||||
|
"markupsafe==2.1.2",
|
||||||
|
"mccabe==0.6.1",
|
||||||
|
"mergedeep==1.3.4",
|
||||||
|
"mkdocs-material==4.6.3",
|
||||||
|
"mkdocs==1.4.2",
|
||||||
|
"mypy-extensions==0.4.4",
|
||||||
|
"mypy==0.991",
|
||||||
|
"pathspec==0.10.3",
|
||||||
|
"platformdirs==2.6.2",
|
||||||
|
"psycopg2==2.9.5",
|
||||||
|
"pycodestyle==2.7.0",
|
||||||
|
"pyflakes==2.3.1",
|
||||||
|
"pygments==2.14.0",
|
||||||
|
"pymdown-extensions==9.9.2",
|
||||||
|
"pyyaml-env-tag==0.1",
|
||||||
|
"responses==0.25.3",
|
||||||
|
"tblib==3.0.0",
|
||||||
|
"tomli==2.0.1",
|
||||||
|
"types-pytz==2022.7.1.0",
|
||||||
|
"types-pyyaml==6.0.12.2",
|
||||||
|
"watchdog==2.3.1",
|
||||||
|
"zipp==3.15.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=61"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["."] # list of folders that contain the packages (["."] by default)
|
where = ["."]
|
||||||
include = ["looper"] # package names should match these glob patterns (["*"] by default)
|
include = ["looper"] # alternatively: `exclude = ["additional*"]`
|
||||||
exclude = [] # exclude packages matching these glob patterns (empty by default)
|
namespaces = false # Disable implicit namespaces
|
||||||
namespaces = false # to disable scanning PEP 420 namespaces (true by default)
|
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 100
|
line-length = 100
|
||||||
skip_string_normalization = true
|
skip_string_normalization = true
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["setuptools>=61", "pyodbc"]
|
|
||||||
build-backend = "setuptools.build_meta"
|
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
appdirs==1.4.4
|
|
||||||
arabic-reshaper==3.0.0
|
|
||||||
asgiref==3.6.0
|
|
||||||
asn1crypto==1.5.1
|
|
||||||
attrs==19.3.0
|
|
||||||
babel==2.12.1
|
|
||||||
bleach==3.3.1
|
|
||||||
braintree==4.17.1
|
|
||||||
cached-property==1.5.2
|
|
||||||
certifi==2022.12.7
|
|
||||||
cffi==1.15.1
|
|
||||||
charset-normalizer==3.0.1
|
|
||||||
click==8.1.3
|
|
||||||
colorama==0.4.6 ; platform_system == "Windows"
|
|
||||||
colorhash==1.2.1
|
|
||||||
cryptography==39.0.0
|
|
||||||
cssselect2==0.7.0
|
|
||||||
defusedxml==0.7.1
|
|
||||||
django-countries==7.5.1
|
|
||||||
django-nested-admin==4.0.2
|
|
||||||
django-pipeline==3.1.0
|
|
||||||
django==4.2.13
|
|
||||||
geoip2==3.0.0
|
|
||||||
html5lib==1.1
|
|
||||||
idna==3.4
|
|
||||||
isodate==0.6.1
|
|
||||||
lxml==4.9.2
|
|
||||||
maxminddb==2.2.0
|
|
||||||
oscrypto==1.3.0
|
|
||||||
packaging==23.0
|
|
||||||
pillow==9.4.0
|
|
||||||
pycparser==2.21
|
|
||||||
pyhanko-certvalidator==0.20.1
|
|
||||||
pyhanko==0.17.0
|
|
||||||
pypdf==3.5.0
|
|
||||||
python-bidi==0.4.2
|
|
||||||
python-dateutil==2.8.2
|
|
||||||
python-monkey-business==1.0.0
|
|
||||||
python-stdnum==1.18
|
|
||||||
pytz-deprecation-shim==0.1.0.post0
|
|
||||||
pytz==2022.7.1
|
|
||||||
pyyaml==6.0
|
|
||||||
qrcode==7.4.2
|
|
||||||
reportlab==3.6.9
|
|
||||||
requests-file==1.5.1
|
|
||||||
requests-toolbelt==0.10.1
|
|
||||||
requests==2.31.0
|
|
||||||
six==1.16.0
|
|
||||||
sqlparse==0.4.3
|
|
||||||
stripe==7.1.0
|
|
||||||
svglib==1.5.1
|
|
||||||
tinycss2==1.2.1
|
|
||||||
tqdm==4.64.1
|
|
||||||
typing-extensions==4.8.0
|
|
||||||
tzdata==2022.7
|
|
||||||
tzlocal==4.2
|
|
||||||
uritools==4.0.1
|
|
||||||
urllib3==1.26.14
|
|
||||||
webencodings==0.5.1
|
|
||||||
xhtml2pdf==0.2.9
|
|
||||||
zeep==4.0.0
|
|
@ -1,35 +0,0 @@
|
|||||||
-r requirements.txt
|
|
||||||
black==22.12.0
|
|
||||||
django-stubs-ext==0.7.0
|
|
||||||
django-stubs==1.13.1
|
|
||||||
factory-boy==3.3.0
|
|
||||||
faker==20.1.0
|
|
||||||
flake8-implicit-str-concat==0.1.0
|
|
||||||
flake8==3.9.2
|
|
||||||
ghp-import==2.1.0
|
|
||||||
importlib-metadata==6.0.0
|
|
||||||
jinja2==3.1.2
|
|
||||||
markdown==3.3.7
|
|
||||||
markupsafe==2.1.2
|
|
||||||
mccabe==0.6.1
|
|
||||||
mergedeep==1.3.4
|
|
||||||
mkdocs-material==4.6.3
|
|
||||||
mkdocs==1.4.2
|
|
||||||
mypy-extensions==0.4.4
|
|
||||||
mypy==0.991
|
|
||||||
pathspec==0.10.3
|
|
||||||
platformdirs==2.6.2
|
|
||||||
psycopg2==2.9.5
|
|
||||||
pycodestyle==2.7.0
|
|
||||||
pyflakes==2.3.1
|
|
||||||
pygments==2.14.0
|
|
||||||
pymdown-extensions==9.9.2
|
|
||||||
pyyaml-env-tag==0.1
|
|
||||||
responses==0.25.3
|
|
||||||
tblib==3.0.0
|
|
||||||
tomli==2.0.1
|
|
||||||
types-pytz==2022.7.1.0
|
|
||||||
types-pyyaml==6.0.12.2
|
|
||||||
tzdata==2022.7 ; sys_platform == "win32"
|
|
||||||
watchdog==2.3.1
|
|
||||||
zipp==3.15.0
|
|
Loading…
Reference in New Issue
Block a user