Compare commits

...

52 commits

Author SHA1 Message Date
PCoder
c4f6780a0e Add management command fix_generic_stripe_plan_product_names.py 2024-03-22 16:54:54 +05:30
0679a47eea Merge branch 'master' into 12334-generic-products-name-in-invoice 2024-03-21 02:42:56 +00:00
Nico Schottelius
8b08357d05 entrypoint: make executable 2024-02-20 17:19:05 +09:00
Nico Schottelius
8b06c2c6e9 docker: add entrypoint and add support for dynamic config 2024-02-20 17:18:29 +09:00
Nico Schottelius
26f3a1881d make build image only push if push is specified 2024-02-20 16:25:13 +09:00
dee2b1a90c Merge pull request 'make-docker-build-succesful' (#19) from make-docker-build-succesful into master
Reviewed-on: ungleich-public/dynamicweb#19
2024-02-20 01:06:22 +00:00
PCoder
9e98125b13 Use the same django-filer version being used on production 2024-02-19 20:50:36 +05:30
PCoder
8d13629c8b Use proper library path for Pillow 2024-02-19 20:49:29 +05:30
PCoder
b6ff2b62c1 Add comment for alpine 3.14 requirement 2024-02-19 20:44:17 +05:30
PCoder
893c816846 Add apks required for the build on alpine 3.12 2024-02-19 20:43:41 +05:30
PCoder
f178be8395 Update .dockerignore: ignore .env 2024-02-19 20:42:34 +05:30
Nico Schottelius
0d7f10776c [docker] push if build is ok 2024-02-17 20:49:31 +09:00
Nico Schottelius
4eb470df16 [docker] switch to python 3.5 2024-02-17 20:48:17 +09:00
app@dynamicweb-production
6262432b7a Merge remote-tracking branch 'ungleich/master' 2024-02-17 11:21:58 +01:00
app@dynamicweb-production
bd4d81c286 Show virtual machine plan page for admin 2024-02-17 11:20:12 +01:00
Nico Schottelius
169dc6b1ee Add initial script for image building 2024-02-17 18:27:44 +09:00
PCoder
fdb790bac7 Use the actual plan name instead of the generic plan id 2024-02-16 16:24:40 +05:30
PCoder
6a374f7fa0 Rearrange 2024-02-16 16:23:19 +05:30
PCoder
10d2c25556 Get product name from id it starts with generic otherwise use as is 2024-02-16 16:23:02 +05:30
5b5239d1d4 Merge branch 'master' into bugfix-charge-invoices 2024-02-09 15:41:43 +00:00
app@dynamicweb-production
41461538a3 Changes on prod 2024-02-09 16:38:10 +01:00
app@dynamicweb-production
7afa9d2f4c Show settings view for admin 2024-02-09 16:37:34 +01:00
PCoder
96af882e6d Fix mistake is obtaining the correct hosting order id 2024-02-09 21:03:17 +05:30
2e335fc574 Merge pull request 'bugfix-vm-detail-no-invoice' (#16) from bugfix-vm-detail-no-invoice into master
Reviewed-on: ungleich-public/dynamicweb#16
2024-01-26 03:46:11 +00:00
PCoder
7d22086677 Merge remote-tracking branch 'mainRepo/master' 2024-01-26 09:12:34 +05:30
PCoder
83d1e54137 Fix error when user accesses vm details and no invoices 2024-01-26 09:11:57 +05:30
PCoder
b6f9e6a706 Merge remote-tracking branch 'origin/master' 2024-01-26 08:18:41 +05:30
PCoder
4e891ed0bb Update countries list 2023-12-29 08:50:58 +05:30
PCoder
4d3da3387a Print case where CustomUser is not found in the datbase from Stripe customer id 2023-12-27 20:36:59 +05:30
PCoder
94a81fc976 Fix obtaining correct CustomUSer 2023-12-27 19:49:50 +05:30
PCoder
36b091700e Fix headers 2023-12-25 12:28:03 +05:30
PCoder
3c48811548 Fix typo 2023-12-25 12:25:07 +05:30
PCoder
10c167e76b Fix typo 2023-12-25 12:23:52 +05:30
PCoder
f31838dbe5 Separate fields 2023-12-25 12:22:34 +05:30
PCoder
92e5254679 Fix customer name and vat rate 2023-12-25 12:14:48 +05:30
PCoder
a0ade926fb Fix variable name typo 2023-12-25 11:44:52 +05:30
PCoder
ea4ff961c2 add missing logging 2023-12-25 11:43:29 +05:30
e3b816d8d7 Merge pull request 'Add management command to change ch vat' (#15) from 12224-change-ch-vat into master
Reviewed-on: ungleich-public/dynamicweb#15
2023-12-25 05:59:37 +00:00
PCoder
5530b48d0d Save subscriptions to be changed in a csv 2023-12-25 11:23:01 +05:30
a38a4a86a4 Merge branch 'master' into 12224-change-ch-vat 2023-12-06 10:14:28 +00:00
PCoder
a492c3de1b Add management command 2023-12-06 15:34:23 +05:30
app@dynamicweb-production
325d1ffcb9 Ignore more contact f 2023-11-17 03:43:49 +01:00
app@dynamicweb-production
3c3c614b6b Debug 2023-11-17 03:39:12 +01:00
app@dynamicweb-production
c5f8660c55 Disable all saving of contact us form and send emails 2023-11-17 03:29:10 +01:00
app@dynamicweb-production
9f74c0286e Add installed app 2023-11-16 19:24:59 +01:00
app@dynamicweb-production
dc6f5dcc5b add recaptcha coe 2023-11-16 19:13:18 +01:00
app@dynamicweb-production
c69affc9a1 Add migration 2023-07-27 10:33:40 +02:00
app@dynamicweb-production
33aeaf7ddb Increase max_digits for generic product price and vat 2023-07-27 10:32:46 +02:00
app@dynamicweb-production
26d9a77ebf Update changelog 2023-04-14 05:07:37 +02:00
app@dynamicweb-production
5f1534c152 Replace jQuery dependent code with vanilla js 2023-04-14 05:05:35 +02:00
app
590f7391c4 Merge remote-tracking branch 'origin/master' 2022-05-16 11:57:16 +02:00
app@dynamicweb-production
95c07fbed1 Change to dcl-orders for new vm and removing ipv6onlyhosting.{net,ch} references 2022-05-16 11:56:05 +02:00
22 changed files with 442 additions and 101 deletions

View file

@ -1 +1,2 @@
.git .git
.env

View file

@ -1,3 +1,5 @@
3.4: 2022-04-14
* 11566: Fix for ungleich.ch product section alignment
3.2: 2021-02-07 3.2: 2021-02-07
* 8816: Update order confirmation text to better prepared for payment dispute * 8816: Update order confirmation text to better prepared for payment dispute
* supportticket#22990: Fix: can't add a deleted card * supportticket#22990: Fix: can't add a deleted card

View file

@ -1,4 +1,5 @@
FROM python:3.10.0-alpine3.15 # FROM python:3.10.0-alpine3.15
FROM python:3.5-alpine3.12
WORKDIR /usr/src/app WORKDIR /usr/src/app
@ -7,12 +8,25 @@ RUN apk add --update --no-cache \
build-base \ build-base \
openldap-dev \ openldap-dev \
python3-dev \ python3-dev \
libpq-dev \ postgresql-dev \
jpeg-dev \
libxml2-dev \
libxslt-dev \
libmemcached-dev \
zlib-dev \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
## For alpine 3.15 replace postgresql-dev with libpq-dev
# FIX https://github.com/python-ldap/python-ldap/issues/432 # FIX https://github.com/python-ldap/python-ldap/issues/432
RUN echo 'INPUT ( libldap.so )' > /usr/lib/libldap_r.so RUN echo 'INPUT ( libldap.so )' > /usr/lib/libldap_r.so
COPY requirements.txt ./ COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Pillow seems to need LIBRARY_PATH set as follows: (see: https://github.com/python-pillow/Pillow/issues/1763#issuecomment-222383534)
RUN LIBRARY_PATH=/lib:/usr/lib /bin/sh -c "pip install --no-cache-dir -r requirements.txt"
COPY ./ . COPY ./ .
COPY entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh" ]

View file

@ -31,9 +31,10 @@ class ContactView(FormView):
return context return context
def form_valid(self, form): def form_valid(self, form):
form.save() print("alplora contactusform")
form.send_email(email_to='info@alplora.ch') #form.save()
messages.add_message(self.request, messages.SUCCESS, self.success_message) #form.send_email(email_to='info@alplora.ch')
#messages.add_message(self.request, messages.SUCCESS, self.success_message)
return render(self.request, 'alplora/contact_success.html', {}) return render(self.request, 'alplora/contact_success.html', {})

23
build-image.sh Executable file
View file

@ -0,0 +1,23 @@
#!/bin/sh
if [ $# -lt 1 ]; then
echo "$0 imageversion [push]"
echo "Version could be: $(git describe --always)"
echo "If push is specified, also push to our harbor"
exit 1
fi
tagprefix=harbor.k8s.ungleich.ch/ungleich-public/dynamicweb
version=$1; shift
tag=${tagprefix}:${version}
set -ex
docker build -t "${tag}" .
push=$1; shift
if [ "$push" ]; then
docker push "${tag}"
fi

View file

@ -0,0 +1,54 @@
from django.core.management.base import BaseCommand
from datacenterlight.tasks import handle_metadata_and_emails
from datacenterlight.models import StripePlan
from opennebula_api.models import OpenNebulaManager
from membership.models import CustomUser
from hosting.models import GenericProduct
import logging
import json
import sys
import stripe
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = '''Stripe plans created before version 3.4 saved the plan name like generic-{subscription_id}-amount. This
command aims at replacing this with the actual product name
'''
def handle(self, *args, **options):
cnt = 0
self.stdout.write(
self.style.SUCCESS(
'In Fix generic stripe plan product names'
)
)
plans_to_change = StripePlan.objects.filter(stripe_plan_id__startswith='generic')
for plan in plans_to_change:
response = input("Press 'y' to continue: ")
# Check if the user entered 'y'
if response.lower() == 'y':
plan_name = plan.stripe_plan_id
first_index_hyphen = plan_name.index("-") + 1
product_id = plan_name[
first_index_hyphen:(plan_name[first_index_hyphen:].index("-")) + first_index_hyphen]
gp = GenericProduct.objects.get(id=product_id)
if gp:
cnt += 1
# update stripe
sp = stripe.Plan.retrieve(plan_name)
pr = stripe.Product.retrieve(sp.product)
pr.name = gp.product_name
pr.save()
# update local
spl = StripePlan.objects.get(stripe_plan_id=plan_name)
spl.stripe_plan_name = gp.product_name
spl.save()
print("%s. %s => %s" % (cnt, plan_name, gp.product_name))
else:
print("Invalid input. Please try again.")
sys.exit()
print("Done")

View file

@ -144,7 +144,7 @@ def get_line_item_from_stripe_invoice(invoice):
""".format( """.format(
vm_id=vm_id if vm_id > 0 else "", vm_id=vm_id if vm_id > 0 else "",
ip_addresses=mark_safe(get_ip_addresses(vm_id)) if vm_id > 0 else ip_addresses=mark_safe(get_ip_addresses(vm_id)) if vm_id > 0 else
mark_safe(get_product_name(plan_name)), mark_safe(get_product_name(plan_name)) if plan_name.startswith("generic-") else plan_name,
period=mark_safe("%s — %s" % ( period=mark_safe("%s — %s" % (
datetime.datetime.fromtimestamp(start_date).strftime('%Y-%m-%d'), datetime.datetime.fromtimestamp(start_date).strftime('%Y-%m-%d'),
datetime.datetime.fromtimestamp(end_date).strftime('%Y-%m-%d'))), datetime.datetime.fromtimestamp(end_date).strftime('%Y-%m-%d'))),
@ -160,8 +160,7 @@ def get_product_name(plan_name):
product_name = "" product_name = ""
if plan_name and plan_name.startswith("generic-"): if plan_name and plan_name.startswith("generic-"):
first_index_hyphen = plan_name.index("-") + 1 first_index_hyphen = plan_name.index("-") + 1
product_id = plan_name[first_index_hyphen: product_id = plan_name[first_index_hyphen:(plan_name[first_index_hyphen:].index("-")) + first_index_hyphen]
(plan_name[first_index_hyphen:].index("-")) + first_index_hyphen]
try: try:
product = GenericProduct.objects.get(id=product_id) product = GenericProduct.objects.get(id=product_id)
product_name = product.product_name product_name = product.product_name

View file

@ -42,6 +42,8 @@ from .utils import (
get_cms_integration, create_vm, clear_all_session_vars, validate_vat_number get_cms_integration, create_vm, clear_all_session_vars, validate_vat_number
) )
from datacenterlight.templatetags.custom_tags import get_product_name
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -63,23 +65,23 @@ class ContactUsView(FormView):
) )
def form_valid(self, form): def form_valid(self, form):
form.save() #form.save()
from_emails = { #from_emails = {
'glasfaser': 'glasfaser@ungleich.ch' # 'glasfaser': 'glasfaser@ungleich.ch'
} #}
from_page = self.request.POST.get('from_page') #from_page = self.request.POST.get('from_page')
email_data = { #email_data = {
'subject': "{dcl_text} Message from {sender}".format( # 'subject': "{dcl_text} Message from {sender}".format(
dcl_text=settings.DCL_TEXT, # dcl_text=settings.DCL_TEXT,
sender=form.cleaned_data.get('email') # sender=form.cleaned_data.get('email')
), # ),
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, # 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': [from_emails.get(from_page, 'support@ungleich.ch')], # 'to': [from_emails.get(from_page, 'support@ungleich.ch')],
'body': "\n".join( # 'body': "\n".join(
["%s=%s" % (k, v) for (k, v) in form.cleaned_data.items()]), # ["%s=%s" % (k, v) for (k, v) in form.cleaned_data.items()]),
'reply_to': [form.cleaned_data.get('email')], # 'reply_to': [form.cleaned_data.get('email')],
} #}
send_plain_email_task.delay(email_data) #send_plain_email_task.delay(email_data)
if self.request.is_ajax(): if self.request.is_ajax():
return self.render_to_response( return self.render_to_response(
self.get_context_data(success=True, contact_form=form)) self.get_context_data(success=True, contact_form=form))
@ -900,15 +902,21 @@ class OrderConfirmationView(DetailView, FormView):
2 2
) )
) )
plan_name = "generic-{0}-{1:.2f}".format( stripe_plan_id = "generic-{0}-{1:.2f}".format(
request.session['generic_payment_details']['product_id'], request.session['generic_payment_details']['product_id'],
amount_to_be_charged amount_to_be_charged
) )
stripe_plan_id = plan_name try:
product = GenericProduct.objects.get(id=request.session['generic_payment_details']['product_id'])
plan_name = product.product_name
except Exception as ex:
logger.debug("Error {}" % str(ex))
plan_name = get_product_name(stripe_plan_id)
recurring_interval = request.session['generic_payment_details']['recurring_interval'] recurring_interval = request.session['generic_payment_details']['recurring_interval']
if recurring_interval == "year": if recurring_interval == "year":
plan_name = "{}-yearly".format(plan_name) stripe_plan_id = "{}-yearly".format(stripe_plan_id)
stripe_plan_id = plan_name plan_name = "{} (yearly)".format(plan_name)
logger.debug("Plan name = {}, Stripe Plan id = {}".format(plan_name, stripe_plan_id))
else: else:
template = request.session.get('template') template = request.session.get('template')
specs = request.session.get('specs') specs = request.session.get('specs')

View file

@ -835,9 +835,10 @@ class ContactView(FormView):
success_message = _('Message Successfully Sent') success_message = _('Message Successfully Sent')
def form_valid(self, form): def form_valid(self, form):
form.save() print("digital glarus contactusform")
form.send_email() #form.save()
messages.add_message(self.request, messages.SUCCESS, self.success_message) #form.send_email()
#messages.add_message(self.request, messages.SUCCESS, self.success_message)
return super(ContactView, self).form_valid(form) return super(ContactView, self).form_valid(form)

View file

@ -56,6 +56,9 @@ dotenv.load_dotenv("{0}/.env".format(PROJECT_DIR))
from multisite import SiteID from multisite import SiteID
RECAPTCHA_PUBLIC_KEY = env('RECAPTCHA_PUBLIC_KEY')
RECAPTCHA_PRIVATE_KEY = env('RECAPTCHA_PRIVATE_KEY')
UNGLEICH_BLOG_SITE_ID = int_env("UNGLEICH_BLOG_SITE_ID") UNGLEICH_BLOG_SITE_ID = int_env("UNGLEICH_BLOG_SITE_ID")
SITE_ID = SiteID(default=(UNGLEICH_BLOG_SITE_ID if SITE_ID = SiteID(default=(UNGLEICH_BLOG_SITE_ID if
UNGLEICH_BLOG_SITE_ID > 0 else 1)) UNGLEICH_BLOG_SITE_ID > 0 else 1))
@ -125,6 +128,7 @@ INSTALLED_APPS = (
'djangocms_file', 'djangocms_file',
'djangocms_picture', 'djangocms_picture',
'djangocms_video', 'djangocms_video',
'django_recaptcha',
# 'djangocms_flash', # 'djangocms_flash',
# 'djangocms_googlemap', # 'djangocms_googlemap',
# 'djangocms_inherit', # 'djangocms_inherit',
@ -773,3 +777,9 @@ if DEBUG:
from .local import * # flake8: noqa from .local import * # flake8: noqa
else: else:
from .prod import * # flake8: noqa from .prod import * # flake8: noqa
# Try to load dynamic configuration, if it exists
try:
from .dynamic import * # flake8: noqa
except ImportError:
pass

19
entrypoint.sh Executable file
View file

@ -0,0 +1,19 @@
#!/bin/sh
set -uex
cd /usr/src/app/
cat > dynamicweb/settings/dynamic.py <<EOF
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': '${POSTGRES_DB}',
'USER': '${POSTGRES_USER}',
'PASSWORD': '${POSTGRES_PASSWORD}',
'HOST': '${POSTGRES_HOST}',
'PORT': '5432',
}
}
EOF
exec "$@"

View file

@ -0,0 +1,144 @@
from django.core.management.base import BaseCommand
import datetime
import csv
import logging
import stripe
from hosting.models import VATRates
from utils.hosting_utils import get_vat_rate_for_country
from django.conf import settings
from membership.models import CustomUser, StripeCustomer
stripe.api_key = settings.STRIPE_API_PRIVATE_KEY
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = '''CH vat rate changes on 2024-01-01 from 7.7% to 8.1%. This commands makes the necessary changes'''
def handle(self, *args, **options):
MAKE_MODIFS=False
try:
country_to_change = 'CH'
currency_to_change = 'CHF'
new_rate = 0.081
user_country_vat_rate = get_vat_rate_for_country(country_to_change)
logger.debug("Existing VATRate for %s %s " % (country_to_change, user_country_vat_rate))
vat_rate = VATRates.objects.get(
territory_codes=country_to_change, start_date__isnull=False, stop_date=None
)
logger.debug("VAT rate for %s is %s" % (country_to_change, vat_rate.rate))
logger.debug("vat_rate object = %s" % vat_rate)
logger.debug("Create end date for the VATRate %s" % vat_rate.id)
# if MAKE_MODIFS:
# vat_rate.stop_date = datetime.date(2023, 12, 31)
# vat_rate.save()
# print("Creating a new VATRate for CH")
# obj, created = VATRates.objects.get_or_create(
# start_date=datetime.date(2024, 1, 1),
# stop_date=None,
# territory_codes=country_to_change,
# currency_code=currency_to_change,
# rate=new_rate,
# rate_type="standard",
# description="Switzerland standard VAT (added manually on %s)" % datetime.datetime.now()
# )
# if created:
# logger.debug("Created new VAT Rate for %s with the new rate %s" % (country_to_change, new_rate))
# logger.debug(obj)
# else:
# logger.debug("VAT Rate for %s already exists with the rate %s" % (country_to_change, new_rate))
#
logger.debug("Getting all subscriptions of %s that need a VAT Rate change")
subscriptions = stripe.Subscription.list(limit=100) # Increase the limit to 100 per page (maximum)
ch_subs = []
while subscriptions:
for subscription in subscriptions:
if len(subscription.default_tax_rates) > 0 and subscription.default_tax_rates[0].jurisdiction and subscription.default_tax_rates[0].jurisdiction.lower() == 'ch':
ch_subs.append(subscription)
elif len(subscription.default_tax_rates) > 0:
print("subscription %s belongs to %s" % (subscription.id, subscription.default_tax_rates[0].jurisdiction))
else:
print("subscription %s does not have a tax rate" % subscription.id)
if subscriptions.has_more:
print("FETCHING MORE")
subscriptions = stripe.Subscription.list(limit=100, starting_after=subscriptions.data[-1])
else:
break
logger.debug("There are %s ch subscription that need VAT rate update" % len(ch_subs))
# CSV column headers
csv_headers = [
"customer_name",
"customer_email",
"stripe_customer_id",
"subscription_id",
"subscription_name",
"amount",
"vat_rate"
]
# CSV file name
csv_filename = "ch_subscriptions_change_2024.csv"
# Write subscription data to CSV file
with open(csv_filename, mode='w', newline='') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=csv_headers)
writer.writeheader()
for subscription in ch_subs:
subscription_id = subscription["id"]
stripe_customer_id = subscription.get("customer", "")
vat_rate = subscription.get("tax_percent", "")
c_user = CustomUser.objects.get(
id=StripeCustomer.objects.filter(stripe_id=stripe_customer_id)[0].user.id)
if c_user:
customer_name = c_user.name.encode('utf-8')
customer_email = c_user.email
items = subscription.get("items", {}).get("data", [])
for item in items:
subscription_name = item.get("plan", {}).get("id", "")
amount = item.get("plan", {}).get("amount", "")
# Convert amount to a proper format (e.g., cents to dollars)
amount_in_chf = amount / 100 # Adjust this conversion as needed
# Writing to CSV
writer.writerow({
"customer_name": customer_name,
"customer_email": customer_email,
"stripe_customer_id": stripe_customer_id,
"subscription_id": subscription_id,
"subscription_name": subscription_name,
"amount": amount_in_chf,
"vat_rate": vat_rate # Fill in VAT rate if available
})
else:
print("No customuser for %s %s" % (stripe_customer_id, subscription_id))
if MAKE_MODIFS:
print("Making modifications now")
tax_rate_obj = stripe.TaxRate.create(
display_name="VAT",
description="VAT for %s" % country_to_change,
jurisdiction=country_to_change,
percentage=new_rate,
inclusive=False,
)
stripe_tax_rate = StripeTaxRate.objects.create(
display_name=tax_rate_obj.display_name,
description=tax_rate_obj.description,
jurisdiction=tax_rate_obj.jurisdiction,
percentage=tax_rate_obj.percentage,
inclusive=False,
tax_rate_id=tax_rate_obj.id
)
for ch_sub in ch_subs:
ch_sub.default_tax_rates = [stripe_tax_rate.tax_rate_id]
ch_sub.save()
logger.debug("Default tax rate updated for %s" % ch_sub.id)
else:
print("Not making any modifications because MAKE_MODIFS=False")
except Exception as e:
print(" *** Error occurred. Details {}".format(str(e)))

View file

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2023-07-27 08:12
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0065_auto_20201231_1041'),
]
operations = [
migrations.AlterField(
model_name='genericproduct',
name='product_price',
field=models.DecimalField(decimal_places=2, max_digits=10),
),
migrations.AlterField(
model_name='genericproduct',
name='product_vat',
field=models.DecimalField(decimal_places=4, default=0, max_digits=10),
),
]

View file

@ -75,8 +75,8 @@ class GenericProduct(AssignPermissionsMixin, models.Model):
) )
product_description = models.CharField(max_length=500, default="") product_description = models.CharField(max_length=500, default="")
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
product_price = models.DecimalField(max_digits=6, decimal_places=2) product_price = models.DecimalField(max_digits=10, decimal_places=2)
product_vat = models.DecimalField(max_digits=6, decimal_places=4, default=0) product_vat = models.DecimalField(max_digits=10, decimal_places=4, default=0)
product_is_subscription = models.BooleanField(default=True) product_is_subscription = models.BooleanField(default=True)
product_subscription_interval = models.CharField( product_subscription_interval = models.CharField(
max_length=10, default="month", max_length=10, default="month",

View file

@ -87,7 +87,7 @@
<tbody> <tbody>
{% for ho, stripe_charge_data in invs_charge %} {% for ho, stripe_charge_data in invs_charge %}
<tr> <tr>
{{ ho.id | get_line_item_from_hosting_order_charge }} {{ ho | get_line_item_from_hosting_order_charge }}
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View file

@ -46,7 +46,7 @@
<div class="vm-vmid"> <div class="vm-vmid">
<div class="vm-item-subtitle">{% trans "Current Pricing" %}</div> <div class="vm-item-subtitle">{% trans "Current Pricing" %}</div>
<div class="vm-item-lg">{{order.price|floatformat:2|intcomma}} CHF/{% if order.generic_product %}{% trans order.generic_product.product_subscription_interval %}{% else %}{% trans "Month" %}{% endif %}</div> <div class="vm-item-lg">{{order.price|floatformat:2|intcomma}} CHF/{% if order.generic_product %}{% trans order.generic_product.product_subscription_interval %}{% else %}{% trans "Month" %}{% endif %}</div>
<a class="btn btn-vm-invoice" href="{{inv_url}}" target="_blank">{% trans "See Invoice" %}</a> {% if inv_url %}<a class="btn btn-vm-invoice" href="{{inv_url}}" target="_blank">{% trans "See Invoice" %}</a>{%else%}{% trans "No invoice as of now" %}{% endif %}
</div> </div>
</div> </div>
<div class="vm-detail-item"> <div class="vm-detail-item">

View file

@ -554,14 +554,23 @@ class SettingsView(LoginRequiredMixin, FormView):
Check if the user already saved contact details. If so, then show Check if the user already saved contact details. If so, then show
the form populated with those details, to let user change them. the form populated with those details, to let user change them.
""" """
username = self.request.GET.get('username')
if self.request.user.is_admin and username:
user = CustomUser.objects.get(username=username)
else:
user = self.request.user
return form_class( return form_class(
instance=self.request.user.billing_addresses.first(), instance=user.billing_addresses.first(),
**self.get_form_kwargs()) **self.get_form_kwargs())
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(SettingsView, self).get_context_data(**kwargs) context = super(SettingsView, self).get_context_data(**kwargs)
# Get user # Get user
user = self.request.user username = self.request.GET.get('username')
if self.request.user.is_admin and username:
user = CustomUser.objects.get(username=username)
else:
user = self.request.user
stripe_customer = None stripe_customer = None
if hasattr(user, 'stripecustomer'): if hasattr(user, 'stripecustomer'):
stripe_customer = user.stripecustomer stripe_customer = user.stripecustomer
@ -1535,7 +1544,12 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
ordering = '-id' ordering = '-id'
def get_queryset(self): def get_queryset(self):
owner = self.request.user username = self.request.GET.get('username')
if self.request.user.is_admin and username:
user = CustomUser.objects.get(username=username)
else:
user = self.request.user
owner = user
manager = OpenNebulaManager(email=owner.username, manager = OpenNebulaManager(email=owner.username,
password=owner.password) password=owner.password)
try: try:
@ -1694,7 +1708,11 @@ class VirtualMachineView(LoginRequiredMixin, View):
login_url = reverse_lazy('hosting:login') login_url = reverse_lazy('hosting:login')
def get_object(self): def get_object(self):
owner = self.request.user username = self.request.GET.get('username')
if self.request.user.is_admin and username:
owner = CustomUser.objects.get(username=username)
else:
owner = self.request.user
vm = None vm = None
manager = OpenNebulaManager( manager = OpenNebulaManager(
email=owner.username, email=owner.username,
@ -1750,7 +1768,10 @@ class VirtualMachineView(LoginRequiredMixin, View):
subscription=hosting_order.subscription_id, subscription=hosting_order.subscription_id,
count=1 count=1
) )
inv_url = stripe_obj.data[0].hosted_invoice_url if stripe_obj.data:
inv_url = stripe_obj.data[0].hosted_invoice_url
else:
inv_url = ''
elif hosting_order.stripe_charge_id: elif hosting_order.stripe_charge_id:
stripe_obj = stripe.Charge.retrieve( stripe_obj = stripe.Charge.retrieve(
hosting_order.stripe_charge_id hosting_order.stripe_charge_id

View file

@ -25,7 +25,7 @@ django-compressor==2.0
django-debug-toolbar==1.4 django-debug-toolbar==1.4
python-dotenv==0.10.3 python-dotenv==0.10.3
django-extensions==1.6.7 django-extensions==1.6.7
django-filer==2.1.2 django-filer==1.2.0
django-filter==0.13.0 django-filter==0.13.0
django-formtools==1.0 django-formtools==1.0
django-guardian==1.4.4 django-guardian==1.4.4

View file

@ -19,13 +19,15 @@
<script> <script>
$( document ).ready(function() { document.addEventListener("DOMContentLoaded", function() {
var equalizer = ".sameheight-{{product_instance.pk}}" var equalizer = ".sameheight-{{product_instance.pk}}";
var heights = $(equalizer).map(function() { var elements = document.querySelectorAll(equalizer);
return $(this).height(); var heights = Array.from(elements).map(function(el) {
}).get(), return el.offsetHeight;
});
maxHeight = Math.max.apply(null, heights); var maxHeight = Math.max(...heights);
$(equalizer).height(maxHeight); Array.from(elements).forEach(function(el) {
el.style.height = maxHeight + "px";
});
}); });
</script> </script>

View file

@ -25,9 +25,10 @@ class ContactView(FormView):
success_message = _('Message Successfully Sent') success_message = _('Message Successfully Sent')
def form_valid(self, form): def form_valid(self, form):
form.save() print("ungleich_page contactusform")
form.send_email() #form.save()
messages.add_message(self.request, messages.SUCCESS, self.success_message) #form.send_email()
#messages.add_message(self.request, messages.SUCCESS, self.success_message)
return super(ContactView, self).form_valid(form) return super(ContactView, self).form_valid(form)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):

View file

@ -1,7 +1,8 @@
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.db import models from django.db import models
# http://xml.coverpages.org/country3166.html # Old: http://xml.coverpages.org/country3166.html
# 2023-12-29: Updated list of countries from https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
COUNTRIES = ( COUNTRIES = (
('AD', _('Andorra')), ('AD', _('Andorra')),
('AE', _('United Arab Emirates')), ('AE', _('United Arab Emirates')),
@ -10,7 +11,6 @@ COUNTRIES = (
('AI', _('Anguilla')), ('AI', _('Anguilla')),
('AL', _('Albania')), ('AL', _('Albania')),
('AM', _('Armenia')), ('AM', _('Armenia')),
('AN', _('Netherlands Antilles')),
('AO', _('Angola')), ('AO', _('Angola')),
('AQ', _('Antarctica')), ('AQ', _('Antarctica')),
('AR', _('Argentina')), ('AR', _('Argentina')),
@ -18,6 +18,7 @@ COUNTRIES = (
('AT', _('Austria')), ('AT', _('Austria')),
('AU', _('Australia')), ('AU', _('Australia')),
('AW', _('Aruba')), ('AW', _('Aruba')),
('AX', _('Aland Islands')),
('AZ', _('Azerbaijan')), ('AZ', _('Azerbaijan')),
('BA', _('Bosnia and Herzegovina')), ('BA', _('Bosnia and Herzegovina')),
('BB', _('Barbados')), ('BB', _('Barbados')),
@ -28,11 +29,13 @@ COUNTRIES = (
('BH', _('Bahrain')), ('BH', _('Bahrain')),
('BI', _('Burundi')), ('BI', _('Burundi')),
('BJ', _('Benin')), ('BJ', _('Benin')),
('BL', _('St. Barts')),
('BM', _('Bermuda')), ('BM', _('Bermuda')),
('BN', _('Brunei Darussalam')), ('BN', _('Brunei')),
('BO', _('Bolivia')), ('BO', _('Bolivia')),
('BQ', _('Caribbean Netherlands')),
('BR', _('Brazil')), ('BR', _('Brazil')),
('BS', _('Bahama')), ('BS', _('Bahamas')),
('BT', _('Bhutan')), ('BT', _('Bhutan')),
('BV', _('Bouvet Island')), ('BV', _('Bouvet Island')),
('BW', _('Botswana')), ('BW', _('Botswana')),
@ -40,11 +43,12 @@ COUNTRIES = (
('BZ', _('Belize')), ('BZ', _('Belize')),
('CA', _('Canada')), ('CA', _('Canada')),
('CC', _('Cocos (Keeling) Islands')), ('CC', _('Cocos (Keeling) Islands')),
('CD', _('Congo - Kinshasa')),
('CF', _('Central African Republic')), ('CF', _('Central African Republic')),
('CG', _('Congo')), ('CG', _('Congo - Brazzaville')),
('CH', _('Switzerland')), ('CH', _('Switzerland')),
('CI', _('Ivory Coast')), ('CI', _('Ivory Coast')),
('CK', _('Cook Iislands')), ('CK', _('Cook Islands')),
('CL', _('Chile')), ('CL', _('Chile')),
('CM', _('Cameroon')), ('CM', _('Cameroon')),
('CN', _('China')), ('CN', _('China')),
@ -52,9 +56,10 @@ COUNTRIES = (
('CR', _('Costa Rica')), ('CR', _('Costa Rica')),
('CU', _('Cuba')), ('CU', _('Cuba')),
('CV', _('Cape Verde')), ('CV', _('Cape Verde')),
('CW', _('Curacao')),
('CX', _('Christmas Island')), ('CX', _('Christmas Island')),
('CY', _('Cyprus')), ('CY', _('Cyprus')),
('CZ', _('Czech Republic')), ('CZ', _('Czechia')),
('DE', _('Germany')), ('DE', _('Germany')),
('DJ', _('Djibouti')), ('DJ', _('Djibouti')),
('DK', _('Denmark')), ('DK', _('Denmark')),
@ -70,16 +75,16 @@ COUNTRIES = (
('ET', _('Ethiopia')), ('ET', _('Ethiopia')),
('FI', _('Finland')), ('FI', _('Finland')),
('FJ', _('Fiji')), ('FJ', _('Fiji')),
('FK', _('Falkland Islands (Malvinas)')), ('FK', _('Falkland Islands')),
('FM', _('Micronesia')), ('FM', _('Micronesia')),
('FO', _('Faroe Islands')), ('FO', _('Faroe Islands')),
('FR', _('France')), ('FR', _('France')),
('FX', _('France, Metropolitan')),
('GA', _('Gabon')), ('GA', _('Gabon')),
('GB', _('United Kingdom (Great Britain)')), ('GB', _('United Kingdom')),
('GD', _('Grenada')), ('GD', _('Grenada')),
('GE', _('Georgia')), ('GE', _('Georgia')),
('GF', _('French Guiana')), ('GF', _('French Guiana')),
('GG', _('Guernsey')),
('GH', _('Ghana')), ('GH', _('Ghana')),
('GI', _('Gibraltar')), ('GI', _('Gibraltar')),
('GL', _('Greenland')), ('GL', _('Greenland')),
@ -93,7 +98,7 @@ COUNTRIES = (
('GU', _('Guam')), ('GU', _('Guam')),
('GW', _('Guinea-Bissau')), ('GW', _('Guinea-Bissau')),
('GY', _('Guyana')), ('GY', _('Guyana')),
('HK', _('Hong Kong')), ('HK', _('Hong Kong SAR China')),
('HM', _('Heard & McDonald Islands')), ('HM', _('Heard & McDonald Islands')),
('HN', _('Honduras')), ('HN', _('Honduras')),
('HR', _('Croatia')), ('HR', _('Croatia')),
@ -102,12 +107,14 @@ COUNTRIES = (
('ID', _('Indonesia')), ('ID', _('Indonesia')),
('IE', _('Ireland')), ('IE', _('Ireland')),
('IL', _('Israel')), ('IL', _('Israel')),
('IM', _('Isle of Man')),
('IN', _('India')), ('IN', _('India')),
('IO', _('British Indian Ocean Territory')), ('IO', _('British Indian Ocean Territory')),
('IQ', _('Iraq')), ('IQ', _('Iraq')),
('IR', _('Islamic Republic of Iran')), ('IR', _('Iran')),
('IS', _('Iceland')), ('IS', _('Iceland')),
('IT', _('Italy')), ('IT', _('Italy')),
('JE', _('Jersey')),
('JM', _('Jamaica')), ('JM', _('Jamaica')),
('JO', _('Jordan')), ('JO', _('Jordan')),
('JP', _('Japan')), ('JP', _('Japan')),
@ -117,14 +124,14 @@ COUNTRIES = (
('KI', _('Kiribati')), ('KI', _('Kiribati')),
('KM', _('Comoros')), ('KM', _('Comoros')),
('KN', _('St. Kitts and Nevis')), ('KN', _('St. Kitts and Nevis')),
('KP', _('Korea, Democratic People\'s Republic of')), ('KP', _('North Korea')),
('KR', _('Korea, Republic of')), ('KR', _('South Korea')),
('KW', _('Kuwait')), ('KW', _('Kuwait')),
('KY', _('Cayman Islands')), ('KY', _('Cayman Islands')),
('KZ', _('Kazakhstan')), ('KZ', _('Kazakhstan')),
('LA', _('Lao People\'s Democratic Republic')), ('LA', _('Laos')),
('LB', _('Lebanon')), ('LB', _('Lebanon')),
('LC', _('Saint Lucia')), ('LC', _('St. Lucia')),
('LI', _('Liechtenstein')), ('LI', _('Liechtenstein')),
('LK', _('Sri Lanka')), ('LK', _('Sri Lanka')),
('LR', _('Liberia')), ('LR', _('Liberia')),
@ -132,20 +139,23 @@ COUNTRIES = (
('LT', _('Lithuania')), ('LT', _('Lithuania')),
('LU', _('Luxembourg')), ('LU', _('Luxembourg')),
('LV', _('Latvia')), ('LV', _('Latvia')),
('LY', _('Libyan Arab Jamahiriya')), ('LY', _('Libya')),
('MA', _('Morocco')), ('MA', _('Morocco')),
('MC', _('Monaco')), ('MC', _('Monaco')),
('MD', _('Moldova, Republic of')), ('MD', _('Moldova')),
('ME', _('Montenegro')),
('MF', _('St. Martin')),
('MG', _('Madagascar')), ('MG', _('Madagascar')),
('MH', _('Marshall Islands')), ('MH', _('Marshall Islands')),
('MK', _('North Macedonia')),
('ML', _('Mali')), ('ML', _('Mali')),
('MM', _('Myanmar (Burma)')),
('MN', _('Mongolia')), ('MN', _('Mongolia')),
('MM', _('Myanmar')), ('MO', _('Macao SAR China')),
('MO', _('Macau')),
('MP', _('Northern Mariana Islands')), ('MP', _('Northern Mariana Islands')),
('MQ', _('Martinique')), ('MQ', _('Martinique')),
('MR', _('Mauritania')), ('MR', _('Mauritania')),
('MS', _('Monserrat')), ('MS', _('Montserrat')),
('MT', _('Malta')), ('MT', _('Malta')),
('MU', _('Mauritius')), ('MU', _('Mauritius')),
('MV', _('Maldives')), ('MV', _('Maldives')),
@ -174,15 +184,17 @@ COUNTRIES = (
('PK', _('Pakistan')), ('PK', _('Pakistan')),
('PL', _('Poland')), ('PL', _('Poland')),
('PM', _('St. Pierre & Miquelon')), ('PM', _('St. Pierre & Miquelon')),
('PN', _('Pitcairn')), ('PN', _('Pitcairn Islands')),
('PR', _('Puerto Rico')), ('PR', _('Puerto Rico')),
('PS', _('Palestinian Territories')),
('PT', _('Portugal')), ('PT', _('Portugal')),
('PW', _('Palau')), ('PW', _('Palau')),
('PY', _('Paraguay')), ('PY', _('Paraguay')),
('QA', _('Qatar')), ('QA', _('Qatar')),
('RE', _('Reunion')), ('RE', _('Reunion')),
('RO', _('Romania')), ('RO', _('Romania')),
('RU', _('Russian Federation')), ('RS', _('Serbia')),
('RU', _('Russia')),
('RW', _('Rwanda')), ('RW', _('Rwanda')),
('SA', _('Saudi Arabia')), ('SA', _('Saudi Arabia')),
('SB', _('Solomon Islands')), ('SB', _('Solomon Islands')),
@ -192,17 +204,19 @@ COUNTRIES = (
('SG', _('Singapore')), ('SG', _('Singapore')),
('SH', _('St. Helena')), ('SH', _('St. Helena')),
('SI', _('Slovenia')), ('SI', _('Slovenia')),
('SJ', _('Svalbard & Jan Mayen Islands')), ('SJ', _('Svalbard and Jan Mayen')),
('SK', _('Slovakia')), ('SK', _('Slovakia')),
('SL', _('Sierra Leone')), ('SL', _('Sierra Leone')),
('SM', _('San Marino')), ('SM', _('San Marino')),
('SN', _('Senegal')), ('SN', _('Senegal')),
('SO', _('Somalia')), ('SO', _('Somalia')),
('SR', _('Suriname')), ('SR', _('Suriname')),
('SS', _('South Sudan')),
('ST', _('Sao Tome & Principe')), ('ST', _('Sao Tome & Principe')),
('SV', _('El Salvador')), ('SV', _('El Salvador')),
('SY', _('Syrian Arab Republic')), ('SX', _('Sint Maarten')),
('SZ', _('Swaziland')), ('SY', _('Syria')),
('SZ', _('Eswatini')),
('TC', _('Turks & Caicos Islands')), ('TC', _('Turks & Caicos Islands')),
('TD', _('Chad')), ('TD', _('Chad')),
('TF', _('French Southern Territories')), ('TF', _('French Southern Territories')),
@ -210,36 +224,34 @@ COUNTRIES = (
('TH', _('Thailand')), ('TH', _('Thailand')),
('TJ', _('Tajikistan')), ('TJ', _('Tajikistan')),
('TK', _('Tokelau')), ('TK', _('Tokelau')),
('TL', _('Timor-Leste')),
('TM', _('Turkmenistan')), ('TM', _('Turkmenistan')),
('TN', _('Tunisia')), ('TN', _('Tunisia')),
('TO', _('Tonga')), ('TO', _('Tonga')),
('TP', _('East Timor')),
('TR', _('Turkey')), ('TR', _('Turkey')),
('TT', _('Trinidad & Tobago')), ('TT', _('Trinidad & Tobago')),
('TV', _('Tuvalu')), ('TV', _('Tuvalu')),
('TW', _('Taiwan, Province of China')), ('TW', _('Taiwan')),
('TZ', _('Tanzania, United Republic of')), ('TZ', _('Tanzania')),
('UA', _('Ukraine')), ('UA', _('Ukraine')),
('UG', _('Uganda')), ('UG', _('Uganda')),
('UM', _('United States Minor Outlying Islands')), ('UM', _('U.S. Outlying Islands')),
('US', _('United States of America')), ('US', _('United States')),
('UY', _('Uruguay')), ('UY', _('Uruguay')),
('UZ', _('Uzbekistan')), ('UZ', _('Uzbekistan')),
('VA', _('Vatican City State (Holy See)')), ('VA', _('Vatican City')),
('VC', _('St. Vincent & the Grenadines')), ('VC', _('St. Vincent & Grenadines')),
('VE', _('Venezuela')), ('VE', _('Venezuela')),
('VG', _('British Virgin Islands')), ('VG', _('British Virgin Islands')),
('VI', _('United States Virgin Islands')), ('VI', _('U.S. Virgin Islands')),
('VN', _('Viet Nam')), ('VN', _('Vietnam')),
('VU', _('Vanuatu')), ('VU', _('Vanuatu')),
('WF', _('Wallis & Futuna Islands')), ('WF', _('Wallis & Futuna')),
('WS', _('Samoa')), ('WS', _('Samoa')),
('YE', _('Yemen')), ('YE', _('Yemen')),
('YT', _('Mayotte')), ('YT', _('Mayotte')),
('YU', _('Yugoslavia')),
('ZA', _('South Africa')), ('ZA', _('South Africa')),
('ZM', _('Zambia')), ('ZM', _('Zambia')),
('ZR', _('Zaire')),
('ZW', _('Zimbabwe')), ('ZW', _('Zimbabwe')),
) )

View file

@ -4,6 +4,8 @@ from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django_recaptcha.fields import ReCaptchaField
from membership.models import CustomUser from membership.models import CustomUser
from .models import ContactMessage, BillingAddress, UserBillingAddress from .models import ContactMessage, BillingAddress, UserBillingAddress
@ -188,6 +190,7 @@ class UserBillingAddressForm(forms.ModelForm):
class ContactUsForm(forms.ModelForm): class ContactUsForm(forms.ModelForm):
error_css_class = 'autofocus' error_css_class = 'autofocus'
captcha = ReCaptchaField()
class Meta: class Meta:
model = ContactMessage model = ContactMessage
@ -206,11 +209,12 @@ class ContactUsForm(forms.ModelForm):
} }
def send_email(self, email_to='info@digitalglarus.ch'): def send_email(self, email_to='info@digitalglarus.ch'):
text_content = render_to_string( pass
'emails/contact.txt', {'data': self.cleaned_data}) #text_content = render_to_string(
html_content = render_to_string( # 'emails/contact.txt', {'data': self.cleaned_data})
'emails/contact.html', {'data': self.cleaned_data}) #html_content = render_to_string(
email = EmailMultiAlternatives('Subject', text_content) # 'emails/contact.html', {'data': self.cleaned_data})
email.attach_alternative(html_content, "text/html") #email = EmailMultiAlternatives('Subject', text_content)
email.to = [email_to] #email.attach_alternative(html_content, "text/html")
email.send() #email.to = [email_to]
#email.send()