From 72741f21881ea7900c7736db134cb5331debf631 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 4 Nov 2019 11:59:44 +0530
Subject: [PATCH 01/45] Fix bugs
- Use correct attribute created_at instead of created_on
- Convert yet another date to str (missed earlier)
---
datacenterlight/management/commands/dumpuser.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/management/commands/dumpuser.py b/datacenterlight/management/commands/dumpuser.py
index aa86371e..2e97e135 100644
--- a/datacenterlight/management/commands/dumpuser.py
+++ b/datacenterlight/management/commands/dumpuser.py
@@ -38,7 +38,7 @@ class Command(BaseCommand):
vm_ids.append(order.vm_id)
order_dict["VM_ID"] = order.vm_id
order_dict["Order Nr."] = order.id
- order_dict["Created On"] = str(order.created_on)
+ order_dict["Created On"] = str(order.created_at)
order_dict["Price"] = order.price
order_dict["Payment card details"] = {
"last4": order.last4,
@@ -82,7 +82,7 @@ class Command(BaseCommand):
"IPv6": vm_detail.ipv6,
"OS": vm_detail.configuration,
}
- order_dict["Terminated on"] = vm_detail.terminated_at
+ order_dict["Terminated on"] = str(vm_detail.terminated_at)
orders_dict[order.vm_id] = order_dict
From c29193f6c82d8f282723cf5c2996d5767f26207a Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 4 Nov 2019 12:05:55 +0530
Subject: [PATCH 02/45] Fix bugs
- fetch_stripe_bills:
- fix wrong assigment of strign to num_invoice_created variable
- return None (do not handle the case) if we don't have an order
---
hosting/management/commands/fetch_stripe_bills.py | 7 ++++++-
hosting/models.py | 5 ++++-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/hosting/management/commands/fetch_stripe_bills.py b/hosting/management/commands/fetch_stripe_bills.py
index 20f1cbe0..1e4d1ab3 100644
--- a/hosting/management/commands/fetch_stripe_bills.py
+++ b/hosting/management/commands/fetch_stripe_bills.py
@@ -50,7 +50,12 @@ class Command(BaseCommand):
logger.debug("Invoice %s exists already. Not importing." % invoice['invoice_id'])
except MonthlyHostingBill.DoesNotExist as dne:
logger.debug("Invoice id %s does not exist" % invoice['invoice_id'])
- num_invoice_created += 1 if MonthlyHostingBill.create(invoice) is not None else logger.error("Did not import invoice for %s" % str(invoice))
+
+ if MonthlyHostingBill.create(invoice) is not None:
+ num_invoice_created += 1
+ else:
+ logger.error("Did not import invoice for %s"
+ "" % str(invoice))
self.stdout.write(
self.style.SUCCESS("Number of invoices imported = %s" % num_invoice_created)
)
diff --git a/hosting/models.py b/hosting/models.py
index c9ca5efe..2a9bd28d 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -319,7 +319,10 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model):
logger.debug("Neither subscription id nor vm_id available")
logger.debug("Can't import invoice")
return None
-
+ if args['order'] is None:
+ logger.error(
+ "Order is None for {}".format(args['invoice_id']))
+ return None
instance = cls.objects.create(
created=datetime.utcfromtimestamp(
args['created']).replace(tzinfo=pytz.utc),
From 6faa8b82e817900d8d8856deccf27d53db54e591 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 4 Nov 2019 12:10:22 +0530
Subject: [PATCH 03/45] Remove unwanted logger/print statements
---
datacenterlight/management/commands/dumpuser.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/datacenterlight/management/commands/dumpuser.py b/datacenterlight/management/commands/dumpuser.py
index 2e97e135..3f3b7695 100644
--- a/datacenterlight/management/commands/dumpuser.py
+++ b/datacenterlight/management/commands/dumpuser.py
@@ -19,7 +19,6 @@ class Command(BaseCommand):
def handle(self, *args, **options):
try:
for email in options['customer_email']:
- logger.debug("Creating dump for the user {}".format(email))
try:
cus_user = CustomUser.objects.get(email=email)
except CustomUser.DoesNotExist as dne:
@@ -119,7 +118,6 @@ class Command(BaseCommand):
if uhk.private_key is not None:
key["Private key"] = uhk.private_key
keys[uhk.name] = key
- print("User {} dump is follows:")
output_dict = {
"User details": {
"Name": cus_user.name,
@@ -132,6 +130,5 @@ class Command(BaseCommand):
"SSH Keys": keys
}
pprint(output_dict)
- logger.debug("Dumped user {} SUCCESSFULLY.".format(email))
except Exception as e:
print(" *** Error occurred. Details {}".format(str(e)))
From 7aec4dd93818a90bc45647e0201ed4849e1f9726 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 4 Nov 2019 12:15:52 +0530
Subject: [PATCH 04/45] Convert dict to json and then dump + fix checking None
on FileField
---
datacenterlight/management/commands/dumpuser.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/management/commands/dumpuser.py b/datacenterlight/management/commands/dumpuser.py
index 3f3b7695..870c73c1 100644
--- a/datacenterlight/management/commands/dumpuser.py
+++ b/datacenterlight/management/commands/dumpuser.py
@@ -1,3 +1,4 @@
+import json
import logging
import sys
from pprint import pprint
@@ -115,7 +116,7 @@ class Command(BaseCommand):
"Name": uhk.name,
"Created on": str(uhk.created_at)
}
- if uhk.private_key is not None:
+ if uhk.private_key:
key["Private key"] = uhk.private_key
keys[uhk.name] = key
output_dict = {
@@ -129,6 +130,6 @@ class Command(BaseCommand):
"Payment cards": cards,
"SSH Keys": keys
}
- pprint(output_dict)
+ pprint(json.dumps(output_dict))
except Exception as e:
print(" *** Error occurred. Details {}".format(str(e)))
From 4174c6226fd05303ffc2ef0666fd4661e7896d01 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 4 Nov 2019 12:19:25 +0530
Subject: [PATCH 05/45] Remove pprint (does not seem to help)
---
datacenterlight/management/commands/dumpuser.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/datacenterlight/management/commands/dumpuser.py b/datacenterlight/management/commands/dumpuser.py
index 870c73c1..24857dec 100644
--- a/datacenterlight/management/commands/dumpuser.py
+++ b/datacenterlight/management/commands/dumpuser.py
@@ -1,7 +1,6 @@
import json
import logging
import sys
-from pprint import pprint
from django.core.management.base import BaseCommand
from membership.models import CustomUser
@@ -130,6 +129,6 @@ class Command(BaseCommand):
"Payment cards": cards,
"SSH Keys": keys
}
- pprint(json.dumps(output_dict))
+ print(json.dumps(output_dict, indent=4))
except Exception as e:
print(" *** Error occurred. Details {}".format(str(e)))
From 270a03e7c510301f6b4ee99d0895deaffa4eba83 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 4 Nov 2019 17:09:40 +0530
Subject: [PATCH 06/45] Improve deleteuser
Do not delete order, bill and vm_detail
---
.../management/commands/deleteuser.py | 86 +++----------------
1 file changed, 11 insertions(+), 75 deletions(-)
diff --git a/datacenterlight/management/commands/deleteuser.py b/datacenterlight/management/commands/deleteuser.py
index 1d57aa41..d4ccba29 100644
--- a/datacenterlight/management/commands/deleteuser.py
+++ b/datacenterlight/management/commands/deleteuser.py
@@ -1,14 +1,17 @@
import logging
-import oca
import sys
-import stripe
+import uuid
+import oca
+import stripe
from django.core.management.base import BaseCommand
-from membership.models import CustomUser, DeletedUser
+
from hosting.models import (
- HostingOrder, HostingBill, VMDetail, UserCardDetail, UserHostingKey
+ UserCardDetail, UserHostingKey
)
+from membership.models import CustomUser, DeletedUser
from opennebula_api.models import OpenNebulaManager
+
logger = logging.getLogger(__name__)
@@ -79,75 +82,6 @@ class Command(BaseCommand):
else:
logger.error("Error while deleting the StripeCustomer")
- hosting_orders = HostingOrder.objects.filter(
- customer=stripe_customer.id
- )
-
- vm_ids = []
- for order in hosting_orders:
- vm_ids.append(order.vm_id)
-
- # Delete Billing Address
- if order.billing_address is not None:
- logger.debug(
- "Billing Address {} associated with {} deleted"
- "".format(order.billing_address.id, email)
- )
- order.billing_address.delete()
- else:
- logger.error(
- "Error while deleting the billing_address")
-
- # Delete order
- if order is not None:
- logger.debug(
- "Order {} associated with {} deleted"
- "".format(order.id, email)
- )
- order.delete()
- else:
- logger.error(
- "Error while deleting the Order")
-
- hosting_bills = HostingBill.objects.filter(
- customer=stripe_customer.id
- )
-
- # delete hosting bills
- for bill in hosting_bills:
- if bill.billing_address is not None:
- logger.debug(
- "HostingBills billing address {} associated with {} deleted"
- "".format(bill.billing_address.id, email)
- )
- bill.billing_address.delete()
- else:
- logger.error(
- "Error while deleting the HostingBill's Billing address")
-
- if bill is not None:
- logger.debug(
- "HostingBill {} associated with {} deleted"
- "".format(bill.id, email)
- )
- bill.delete()
- else:
- logger.error(
- "Error while deleting the HostingBill")
-
- # delete VMDetail
- for vm_id in vm_ids:
- vm_detail = VMDetail.objects.get(vm_id=vm_id)
- if vm_detail is not None:
- logger.debug(
- "vm_detail {} associated with {} deleted"
- "".format(vm_detail.id, email)
- )
- vm_detail.delete()
- else:
- logger.error(
- "Error while deleting the vm_detail")
-
# delete UserCardDetail
ucds = UserCardDetail.objects.filter(
stripe_customer=stripe_customer
@@ -179,8 +113,10 @@ class Command(BaseCommand):
user_id = cus_user.id
)
- # delete CustomUser
- cus_user.delete()
+ # reset CustomUser
+ cus_user.email = str(uuid.uuid4())
+ cus_user.validated = 0
+ cus_user.save()
# remove user from OpenNebula
manager = OpenNebulaManager()
From 2d916936d6aba6933be5273ba8c14588d7baed5e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 4 Nov 2019 17:17:27 +0530
Subject: [PATCH 07/45] Update Changelog
---
Changelog | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Changelog b/Changelog
index a219bbee..7023cae5 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,11 @@
+2.6.7: 2019-11-04
+ * bugfix: [admin] Improve dumpuser: show proper dates + bugfix
+ * bugfix: [admin] Improve fetch_stripe_bills:
+ - fix wrong assigment of string to num_invoice_created
+ variable,
+ - return None (do not handle the case) if we don't have an
+ order
+ * bugfix: [admin] Improve deleteuser: do not delete order, bill and vm_detail
2.6.6: 2019-11-04
* feature: [admin] Add dumpuser management command that dumps a user's data in json (MR!716)
2.6.5: 2019-09-24
From c56d6bd62708b49071fb406270e889930c7564be Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 11:01:49 +0530
Subject: [PATCH 08/45] Add VATRates model
---
hosting/models.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/hosting/models.py b/hosting/models.py
index 2a9bd28d..5f0ec3ef 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -707,3 +707,13 @@ class UserCardDetail(AssignPermissionsMixin, models.Model):
return ucd
except UserCardDetail.DoesNotExist:
return None
+
+
+class VATRates(AssignPermissionsMixin, models.Model):
+ start_date = models.DateField(blank=True, null=True)
+ stop_date = models.DateField(blank=True, null=True)
+ territory_codes = models.TextField(blank=True, default='')
+ currency_code = models.CharField(max_length=10)
+ rate = models.FloatField()
+ rate_type = models.TextField(blank=True, default='')
+ description = models.TextField(blank=True, default='')
\ No newline at end of file
From 7038a36b4df0c2cbed1638df2b825d93f4b386a4 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 11:02:06 +0530
Subject: [PATCH 09/45] Add vat_rates csv
---
vat_rates.csv | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 314 insertions(+)
create mode 100644 vat_rates.csv
diff --git a/vat_rates.csv b/vat_rates.csv
new file mode 100644
index 00000000..b82f252b
--- /dev/null
+++ b/vat_rates.csv
@@ -0,0 +1,314 @@
+start_date,stop_date,territory_codes,currency_code,rate,rate_type,description
+2011-01-04,,AI,XCD,0,standard,Anguilla (British overseas territory) is exempted of VAT.
+1984-01-01,,AT,EUR,0.2,standard,Austria (member state) standard VAT rate.
+1976-01-01,1984-01-01,AT,EUR,0.18,standard,
+1973-01-01,1976-01-01,AT,EUR,0.16,standard,
+1984-01-01,,"AT-6691
+DE-87491",EUR,0.19,standard,Jungholz (Austrian town) special VAT rate.
+1984-01-01,,"AT-6991
+AT-6992
+AT-6993
+DE-87567
+DE-87568
+DE-87569",EUR,0.19,standard,Mittelberg (Austrian town) special VAT rate.
+1996-01-01,,BE,EUR,0.21,standard,Belgium (member state) standard VAT rate.
+1994-01-01,1996-01-01,BE,EUR,0.205,standard,
+1992-04-01,1994-01-01,BE,EUR,0.195,standard,
+1983-01-01,1992-04-01,BE,EUR,0.19,standard,
+1981-07-01,1983-01-01,BE,EUR,0.17,standard,
+1978-07-01,1981-07-01,BE,EUR,0.16,standard,
+1971-07-01,1978-07-01,BE,EUR,0.18,standard,
+1999-01-01,,BG,BGN,0.2,standard,Bulgaria (member state) standard VAT rate.
+1996-07-01,1999-01-01,BG,BGN,0.22,standard,
+1994-04-01,1996-07-01,BG,BGN,0.18,standard,
+2011-01-04,,BM,BMD,0,standard,Bermuda (British overseas territory) is exempted of VAT.
+2014-01-13,,"CY
+GB-BFPO 57
+GB-BFPO 58
+GB-BFPO 59
+UK-BFPO 57
+UK-BFPO 58
+UK-BFPO 59",EUR,0.19,standard,"Cyprus (member state) standard VAT rate.
+Akrotiri and Dhekelia (British overseas territory) is subjected to Cyprus' standard VAT rate."
+2013-01-14,2014-01-13,CY,EUR,0.18,standard,
+2012-03-01,2013-01-14,CY,EUR,0.17,standard,
+2003-01-01,2012-03-01,CY,EUR,0.15,standard,
+2002-07-01,2003-01-01,CY,EUR,0.13,standard,
+2000-07-01,2002-07-01,CY,EUR,0.1,standard,
+1993-10-01,2000-07-01,CY,EUR,0.08,standard,
+1992-07-01,1993-10-01,CY,EUR,0.05,standard,
+2013-01-01,,CZ,CZK,0.21,standard,Czech Republic (member state) standard VAT rate.
+2010-01-01,2013-01-01,CZ,CZK,0.2,standard,
+2004-05-01,2010-01-01,CZ,CZK,0.19,standard,
+1995-01-01,2004-05-01,CZ,CZK,0.22,standard,
+1993-01-01,1995-01-01,CZ,CZK,0.23,standard,
+2007-01-01,,DE,EUR,0.19,standard,Germany (member state) standard VAT rate.
+1998-04-01,2007-01-01,DE,EUR,0.16,standard,
+1993-01-01,1998-04-01,DE,EUR,0.15,standard,
+1983-07-01,1993-01-01,DE,EUR,0.14,standard,
+1979-07-01,1983-07-01,DE,EUR,0.13,standard,
+1978-01-01,1979-07-01,DE,EUR,0.12,standard,
+1968-07-01,1978-01-01,DE,EUR,0.11,standard,
+1968-01-01,1968-07-01,DE,EUR,0.1,standard,
+2007-01-01,,DE-27498,EUR,0,standard,Heligoland (German island) is exempted of VAT.
+2007-01-01,,"DE-78266
+CH-8238",EUR,0,standard,Busingen am Hochrhein (German territory) is exempted of VAT.
+1992-01-01,,DK,DKK,0.25,standard,Denmark (member state) standard VAT rate.
+1980-06-30,1992-01-01,DK,DKK,0.22,standard,
+1978-10-30,1980-06-30,DK,DKK,0.2025,standard,
+1977-10-03,1978-10-30,DK,DKK,0.18,standard,
+1970-06-29,1977-10-03,DK,DKK,0.15,standard,
+1968-04-01,1970-06-29,DK,DKK,0.125,standard,
+1967-07-03,1968-04-01,DK,DKK,0.1,standard,
+2009-07-01,,EE,EUR,0.2,standard,Estonia (member state) standard VAT rate.
+1993-01-01,2009-07-01,EE,EUR,0.18,standard,
+1991-01-01,1993-01-01,EE,EUR,0.1,standard,
+2016-06-01,,"GR
+EL",EUR,0.24,standard,Greece (member state) standard VAT rate.
+2010-07-01,2016-06-01,"GR
+EL",EUR,0.23,standard,
+2010-03-15,2010-07-01,"GR
+EL",EUR,0.21,standard,
+2005-04-01,2010-03-15,"GR
+EL",EUR,0.19,standard,
+1990-04-28,2005-04-01,"GR
+EL",EUR,0.18,standard,
+1988-01-01,1990-04-28,"GR
+EL",EUR,0.16,standard,
+1987-01-01,1988-01-01,"GR
+EL",EUR,0.18,standard,
+2012-09-01,,ES,EUR,0.21,standard,Spain (member state) standard VAT rate.
+2010-07-01,2012-09-01,ES,EUR,0.18,standard,
+1995-01-01,2010-07-01,ES,EUR,0.16,standard,
+1992-08-01,1995-01-01,ES,EUR,0.15,standard,
+1992-01-01,1992-08-01,ES,EUR,0.13,standard,
+1986-01-01,1992-01-01,ES,EUR,0.12,standard,
+2012-09-01,,"ES-CN
+ES-GC
+ES-TF
+IC",EUR,0,standard,Canary Islands (Spanish autonomous community) is exempted of VAT.
+2012-09-01,,"ES-ML
+ES-CE
+EA",EUR,0,standard,Ceuta and Melilla (Spanish autonomous cities) is exempted of VAT.
+2013-01-01,,FI,EUR,0.24,standard,Finland (member state) standard VAT rate.
+2010-07-01,2013-01-01,FI,EUR,0.23,standard,
+1994-06-01,2010-07-01,FI,EUR,0.22,standard,
+2013-01-01,,"FI-01
+AX",EUR,0,standard,Aland Islands (Finish autonomous region) is exempted of VAT.
+2011-01-04,,FK,FKP,0,standard,Falkland Islands (British overseas territory) is exempted of VAT.
+1992-01-01,,FO,DKK,0,standard,Faroe Islands (Danish autonomous country) is exempted of VAT.
+2014-01-01,,"FR
+MC",EUR,0.2,standard,"France (member state) standard VAT rate.
+Monaco (sovereign city-state) is member of the EU VAT area and subjected to France's standard VAT rate."
+2000-04-01,2014-01-01,"FR
+MC",EUR,0.196,standard,
+1995-08-01,2000-04-01,"FR
+MC",EUR,0.206,standard,
+1982-07-01,1995-08-01,"FR
+MC",EUR,0.186,standard,
+1977-01-01,1982-07-01,"FR
+MC",EUR,0.176,standard,
+1973-01-01,1977-01-01,"FR
+MC",EUR,0.2,standard,
+1970-01-01,1973-01-01,"FR
+MC",EUR,0.23,standard,
+1968-12-01,1970-01-01,"FR
+MC",EUR,0.19,standard,
+1968-01-01,1968-12-01,"FR
+MC",EUR,0.1666,standard,
+2014-01-01,,"FR-BL
+BL",EUR,0,standard,Saint Barthelemy (French overseas collectivity) is exempted of VAT.
+2014-01-01,,"FR-GF
+GF",EUR,0,standard,Guiana (French overseas department) is exempted of VAT.
+2014-01-01,,"FR-GP
+GP",EUR,0.085,standard,Guadeloupe (French overseas department) special VAT rate.
+2014-01-01,,"FR-MF
+MF",EUR,0,standard,Saint Martin (French overseas collectivity) is subjected to France's standard VAT rate.
+2014-01-01,,"FR-MQ
+MQ",EUR,0.085,standard,Martinique (French overseas department) special VAT rate.
+2014-01-01,,"FR-NC
+NC",XPF,0,standard,New Caledonia (French special collectivity) is exempted of VAT.
+2014-01-01,,"FR-PF
+PF",XPF,0,standard,French Polynesia (French overseas collectivity) is exempted of VAT.
+2014-01-01,,"FR-PM
+PM",EUR,0,standard,Saint Pierre and Miquelon (French overseas collectivity) is exempted of VAT.
+2014-01-01,,"FR-RE
+RE",EUR,0.085,standard,Reunion (French overseas department) special VAT rate.
+2014-01-01,,"FR-TF
+TF",EUR,0,standard,French Southern and Antarctic Lands (French overseas territory) is exempted of VAT.
+2014-01-01,,"FR-WF
+WF",XPF,0,standard,Wallis and Futuna (French overseas collectivity) is exempted of VAT.
+2014-01-01,,"FR-YT
+YT",EUR,0,standard,Mayotte (French overseas department) is exempted of VAT.
+2011-01-04,,GG,GBP,0,standard,Guernsey (British Crown dependency) is exempted of VAT.
+2011-01-04,,GI,GIP,0,standard,Gibraltar (British overseas territory) is exempted of VAT.
+1992-01-01,,GL,DKK,0,standard,Greenland (Danish autonomous country) is exempted of VAT.
+2010-07-01,2016-06-01,"GR-34007
+EL-34007",EUR,0.16,standard,Skyros (Greek island) special VAT rate.
+2010-07-01,2016-06-01,"GR-37002
+GR-37003
+GR-37005
+EL-37002
+EL-37003
+EL-37005",EUR,0.16,standard,Northern Sporades (Greek islands) special VAT rate.
+2010-07-01,2016-06-01,"GR-64004
+EL-64004",EUR,0.16,standard,Thasos (Greek island) special VAT rate.
+2010-07-01,2016-06-01,"GR-68002
+EL-68002",EUR,0.16,standard,Samothrace (Greek island) special VAT rate.
+2010-07-01,,"GR-69
+EL-69",EUR,0,standard,Mount Athos (Greek self-governed part) is exempted of VAT.
+2010-07-01,2016-06-01,"GR-81
+EL-81",EUR,0.16,standard,Dodecanese (Greek department) special VAT rate.
+2010-07-01,2016-06-01,"GR-82
+EL-82",EUR,0.16,standard,Cyclades (Greek department) special VAT rate.
+2010-07-01,2016-06-01,"GR-83
+EL-83",EUR,0.16,standard,Lesbos (Greek department) special VAT rate.
+2010-07-01,2016-06-01,"GR-84
+EL-84",EUR,0.16,standard,Samos (Greek department) special VAT rate.
+2010-07-01,2016-06-01,"GR-85
+EL-85",EUR,0.16,standard,Chios (Greek department) special VAT rate.
+2011-01-04,,GS,GBP,0,standard,South Georgia and the South Sandwich Islands (British overseas territory) is exempted of VAT.
+2012-03-01,,HR,HRK,0.25,standard,Croatia (member state) standard VAT rate.
+2009-08-01,2012-03-01,HR,HRK,0.23,standard,
+1998-08-01,2009-08-01,HR,HRK,0.22,standard,
+2012-01-01,,HU,HUF,0.27,standard,Hungary (member state) standard VAT rate.
+2009-07-01,2012-01-01,HU,HUF,0.25,standard,
+2006-01-01,2009-07-01,HU,HUF,0.2,standard,
+1988-01-01,2006-01-01,HU,HUF,0.25,standard,
+2012-01-01,,IE,EUR,0.23,standard,Republic of Ireland (member state) standard VAT rate.
+2010-01-01,2012-01-01,IE,EUR,0.21,standard,
+2008-12-01,2010-01-01,IE,EUR,0.215,standard,
+2002-03-01,2008-12-01,IE,EUR,0.21,standard,
+2001-01-01,2002-03-01,IE,EUR,0.2,standard,
+1991-03-01,2001-01-01,IE,EUR,0.21,standard,
+1990-03-01,1991-03-01,IE,EUR,0.23,standard,
+1986-03-01,1990-03-01,IE,EUR,0.25,standard,
+1983-05-01,1986-03-01,IE,EUR,0.23,standard,
+1983-03-01,1983-05-01,IE,EUR,0.35,standard,
+1982-05-01,1983-03-01,IE,EUR,0.3,standard,
+1980-05-01,1982-05-01,IE,EUR,0.25,standard,
+1976-03-01,1980-05-01,IE,EUR,0.2,standard,
+1973-09-03,1976-03-01,IE,EUR,0.195,standard,
+1972-11-01,1973-09-03,IE,EUR,0.1637,standard,
+2011-01-04,,IO,GBP,0,standard,British Indian Ocean Territory (British overseas territory) is exempted of VAT.
+2013-10-01,,IT,EUR,0.22,standard,Italy (member state) standard VAT rate.
+2011-09-17,2013-10-01,IT,EUR,0.21,standard,
+1997-10-01,2011-09-17,IT,EUR,0.2,standard,
+1988-08-01,1997-10-01,IT,EUR,0.19,standard,
+1982-08-05,1988-08-01,IT,EUR,0.18,standard,
+1981-01-01,1982-08-05,IT,EUR,0.15,standard,
+1980-11-01,1981-01-01,IT,EUR,0.14,standard,
+1980-07-03,1980-11-01,IT,EUR,0.15,standard,
+1977-02-08,1980-07-03,IT,EUR,0.14,standard,
+1973-01-01,1977-02-08,IT,EUR,0.12,standard,
+2013-10-01,,"IT-22060
+CH-6911",CHF,0,standard,Campione (Italian town) is exempted of VAT.
+2013-10-01,,IT-23030,EUR,0,standard,Livigno (Italian town) is exempted of VAT.
+2011-01-04,,JE,GBP,0,standard,Jersey (British Crown dependency) is exempted of VAT.
+2011-01-04,,KY,KYD,0,standard,Cayman Islands (British overseas territory) is exempted of VAT.
+2009-09-01,,LT,EUR,0.21,standard,Lithuania (member state) standard VAT rate.
+2009-01-01,2009-09-01,LT,EUR,0.19,standard,
+1994-05-01,2009-01-01,LT,EUR,0.18,standard,
+2015-01-01,,LU,EUR,0.17,standard,Luxembourg (member state) standard VAT rate.
+1992-01-01,2015-01-01,LU,EUR,0.15,standard,
+1983-07-01,1992-01-01,LU,EUR,0.12,standard,
+1971-01-01,1983-07-01,LU,EUR,0.1,standard,
+1970-01-01,1971-01-01,LU,EUR,0.8,standard,
+2012-07-01,,LV,EUR,0.21,standard,Latvia (member state) standard VAT rate.
+2011-01-01,2012-07-01,LV,EUR,0.22,standard,
+2009-01-01,2011-01-01,LV,EUR,0.21,standard,
+1995-05-01,2009-01-01,LV,EUR,0.18,standard,
+2011-01-04,,MS,XCD,0,standard,Montserrat (British overseas territory) is exempted of VAT.
+2004-01-01,,MT,EUR,0.18,standard,Malta (member state) standard VAT rate.
+1995-01-01,2004-01-01,MT,EUR,0.15,standard,
+2012-10-01,,NL,EUR,0.21,standard,Netherlands (member state) standard VAT rate.
+2001-01-01,2012-10-01,NL,EUR,0.19,standard,
+1992-10-01,2001-01-01,NL,EUR,0.175,standard,
+1989-01-01,1992-10-01,NL,EUR,0.185,standard,
+1986-10-01,1989-01-01,NL,EUR,0.2,standard,
+1984-01-01,1986-10-01,NL,EUR,0.19,standard,
+1976-01-01,1984-01-01,NL,EUR,0.18,standard,
+1973-01-01,1976-01-01,NL,EUR,0.16,standard,
+1971-01-01,1973-01-01,NL,EUR,0.14,standard,
+1969-01-01,1971-01-01,NL,EUR,0.12,standard,
+2012-10-01,,"NL-AW
+AW",AWG,0,standard,Aruba (Dutch country) are exempted of VAT.
+2012-10-01,,"NL-CW
+NL-SX
+CW
+SX",ANG,0,standard,Curacao and Sint Maarten (Dutch countries) are exempted of VAT.
+2012-10-01,,"NL-BQ1
+NL-BQ2
+NL-BQ3
+BQ
+BQ-BO
+BQ-SA
+BQ-SE",USD,0,standard,"Bonaire, Saba and Sint Eustatius (Dutch special municipalities) are exempted of VAT."
+2011-01-01,,PL,PLN,0.23,standard,Poland (member state) standard VAT rate.
+1993-01-08,2011-01-01,PL,PLN,0.22,standard,
+2011-01-04,,PN,NZD,0,standard,Pitcairn Islands (British overseas territory) is exempted of VAT.
+2011-01-01,,PT,EUR,0.23,standard,Portugal (member state) standard VAT rate.
+2010-07-01,2011-01-01,PT,EUR,0.21,standard,
+2008-07-01,2010-07-01,PT,EUR,0.2,standard,
+2005-07-01,2008-07-01,PT,EUR,0.21,standard,
+2002-06-05,2005-07-01,PT,EUR,0.19,standard,
+1995-01-01,2002-06-05,PT,EUR,0.17,standard,
+1992-03-24,1995-01-01,PT,EUR,0.16,standard,
+1988-02-01,1992-03-24,PT,EUR,0.17,standard,
+1986-01-01,1988-02-01,PT,EUR,0.16,standard,
+2011-01-01,,PT-20,EUR,0.18,standard,Azores (Portuguese autonomous region) special VAT rate.
+2011-01-01,,PT-30,EUR,0.22,standard,Madeira (Portuguese autonomous region) special VAT rate.
+2017-01-01,,RO,RON,0.19,standard,Romania (member state) standard VAT rate.
+2016-01-01,2017-01-01,RO,RON,0.2,standard,Romania (member state) standard VAT rate.
+2010-07-01,2016-01-01,RO,RON,0.24,standard,
+2000-01-01,2010-07-01,RO,RON,0.19,standard,
+1998-02-01,2000-01-01,RO,RON,0.22,standard,
+1993-07-01,1998-02-01,RO,RON,0.18,standard,
+1990-07-01,,SE,SEK,0.25,standard,Sweden (member state) standard VAT rate.
+1983-01-01,1990-07-01,SE,SEK,0.2346,standard,
+1981-11-16,1983-01-01,SE,SEK,0.2151,standard,
+1980-09-08,1981-11-16,SE,SEK,0.2346,standard,
+1977-06-01,1980-09-08,SE,SEK,0.2063,standard,
+1971-01-01,1977-06-01,SE,SEK,0.1765,standard,
+1969-01-01,1971-01-01,SE,SEK,0.1111,standard,
+2011-01-04,,"AC
+SH
+SH-AC
+SH-HL",SHP,0,standard,Ascension and Saint Helena (British overseas territory) is exempted of VAT.
+2011-01-04,,"TA
+SH-TA",GBP,0,standard,Tristan da Cunha (British oversea territory) is exempted of VAT.
+2013-07-01,,SI,EUR,0.22,standard,Slovenia (member state) standard VAT rate.
+2002-01-01,2013-07-01,SI,EUR,0.2,standard,
+1999-07-01,2002-01-01,SI,EUR,0.19,standard,
+2011-01-01,,SK,EUR,0.2,standard,Slovakia (member state) standard VAT rate.
+2004-01-01,2011-01-01,SK,EUR,0.19,standard,
+2003-01-01,2004-01-01,SK,EUR,0.2,standard,
+1996-01-01,2003-01-01,SK,EUR,0.23,standard,
+1993-08-01,1996-01-01,SK,EUR,0.25,standard,
+1993-01-01,1993-08-01,SK,EUR,0.23,standard,
+2011-01-04,,TC,USD,0,standard,Turks and Caicos Islands (British overseas territory) is exempted of VAT.
+2011-01-04,,"GB
+UK
+IM",GBP,0.2,standard,"United Kingdom (member state) standard VAT rate.
+Isle of Man (British self-governing dependency) is member of the EU VAT area and subjected to UK's standard VAT rate."
+2010-01-01,2011-01-04,"GB
+UK
+IM",GBP,0.175,standard,
+2008-12-01,2010-01-01,"GB
+UK
+IM",GBP,0.15,standard,
+1991-04-01,2008-12-01,"GB
+UK
+IM",GBP,0.175,standard,
+1979-06-18,1991-04-01,"GB
+UK
+IM",GBP,0.15,standard,
+1974-07-29,1979-06-18,"GB
+UK
+IM",GBP,0.08,standard,
+1973-04-01,1974-07-29,"GB
+UK
+IM",GBP,0.1,standard,
+2011-01-04,,VG,USD,0,standard,British Virgin Islands (British overseas territory) is exempted of VAT.
+2014-01-01,,CP,EUR,0,standard,Clipperton Island (French overseas possession) is exempted of VAT.
+
From b3dd57f1899a7ba3182d1901f9e045f2abf32df8 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 11:02:45 +0530
Subject: [PATCH 10/45] Add vatrates migration
---
hosting/migrations/0057_vatrates.py | 30 +++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 hosting/migrations/0057_vatrates.py
diff --git a/hosting/migrations/0057_vatrates.py b/hosting/migrations/0057_vatrates.py
new file mode 100644
index 00000000..494974c6
--- /dev/null
+++ b/hosting/migrations/0057_vatrates.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2019-11-15 05:16
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import utils.mixins
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('hosting', '0056_auto_20191026_0454'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='VATRates',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('start_date', models.DateField(blank=True, null=True)),
+ ('stop_date', models.DateField(blank=True, null=True)),
+ ('territory_codes', models.TextField(blank=True, default='')),
+ ('currency_code', models.CharField(max_length=10)),
+ ('rate', models.FloatField()),
+ ('rate_type', models.TextField(blank=True, default='')),
+ ('description', models.TextField(blank=True, default='')),
+ ],
+ bases=(utils.mixins.AssignPermissionsMixin, models.Model),
+ ),
+ ]
From 7040d908dde68a45159036c24e673782bb5a3737 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 11:03:09 +0530
Subject: [PATCH 11/45] Add import_vat_rates management command
---
.../management/commands/import_vat_rates.py | 44 +++++++++++++++++++
1 file changed, 44 insertions(+)
create mode 100644 hosting/management/commands/import_vat_rates.py
diff --git a/hosting/management/commands/import_vat_rates.py b/hosting/management/commands/import_vat_rates.py
new file mode 100644
index 00000000..f779133d
--- /dev/null
+++ b/hosting/management/commands/import_vat_rates.py
@@ -0,0 +1,44 @@
+from django.core.management.base import BaseCommand
+import csv
+from hosting.models import VATRates
+
+
+class Command(BaseCommand):
+ help = '''Imports VAT Rates. Assume vat rates of format https://github.com/kdeldycke/vat-rates/blob/master/vat_rates.csv'''
+
+ def add_arguments(self, parser):
+ parser.add_argument('csv_file', nargs='+', type=str)
+
+ def handle(self, *args, **options):
+ try:
+ for c_file in options['csv_file']:
+ print("c_file = %s" % c_file)
+ with open(c_file, mode='r') as csv_file:
+ csv_reader = csv.DictReader(csv_file)
+ line_count = 0
+ for row in csv_reader:
+ if line_count == 0:
+ line_count += 1
+ obj, created = VATRates.objects.get_or_create(
+ start_date=row["start_date"],
+ stop_date=row["stop_date"] if row["stop_date"] is not "" else None,
+ territory_codes=row["territory_codes"],
+ currency_code=row["currency_code"],
+ rate=row["rate"],
+ rate_type=row["rate_type"],
+ description=row["description"]
+ )
+ if created:
+ self.stdout.write(self.style.SUCCESS(
+ '%s. %s - %s - %s - %s' % (
+ line_count,
+ obj.start_date,
+ obj.stop_date,
+ obj.territory_codes,
+ obj.rate
+ )
+ ))
+ line_count+=1
+
+ except Exception as e:
+ print(" *** Error occurred. Details {}".format(str(e)))
From e0b2a0b6e226dec773834ac6b1f12e88171e1508 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 11:25:50 +0530
Subject: [PATCH 12/45] Add CH VAT rate
---
vat_rates.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/vat_rates.csv b/vat_rates.csv
index b82f252b..e6aa31af 100644
--- a/vat_rates.csv
+++ b/vat_rates.csv
@@ -311,4 +311,4 @@ UK
IM",GBP,0.1,standard,
2011-01-04,,VG,USD,0,standard,British Virgin Islands (British overseas territory) is exempted of VAT.
2014-01-01,,CP,EUR,0,standard,Clipperton Island (French overseas possession) is exempted of VAT.
-
+2019-11-15,,CH,CHF,0.077,standard,Switzerland standard VAT (added manually)
From 44a20a5029af1836896daa14a7e5fd554bc4f108 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 11:58:15 +0530
Subject: [PATCH 13/45] Apply country specific VAT rates for Generic Products
---
datacenterlight/views.py | 9 ++++++---
hosting/models.py | 5 +++--
utils/hosting_utils.py | 14 +++++++++++++-
3 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index ae649623..e13a31a6 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -26,7 +26,9 @@ from utils.forms import (
BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm,
BillingAddress
)
-from utils.hosting_utils import get_vm_price_with_vat, get_all_public_keys
+from utils.hosting_utils import (
+ get_vm_price_with_vat, get_all_public_keys, get_vat_rate_for_country
+)
from utils.stripe_utils import StripeUtils
from utils.tasks import send_plain_email_task
from .cms_models import DCLCalculatorPluginModel
@@ -414,8 +416,9 @@ class PaymentOrderView(FormView):
)
gp_details = {
"product_name": product.product_name,
- "amount": generic_payment_form.cleaned_data.get(
- 'amount'
+ "amount": product.get_actual_price(
+ explicit_vat=get_vat_rate_for_country(
+ address_form["country"])
),
"recurring": generic_payment_form.cleaned_data.get(
'recurring'
diff --git a/hosting/models.py b/hosting/models.py
index 5f0ec3ef..00c89e11 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -82,9 +82,10 @@ class GenericProduct(AssignPermissionsMixin, models.Model):
def __str__(self):
return self.product_name
- def get_actual_price(self):
+ def get_actual_price(self, vat_rate=None):
+ VAT = vat_rate if vat_rate is not None else self.product_vat
return round(
- self.product_price + (self.product_price * self.product_vat), 2
+ self.product_price + (self.product_price * VAT), 2
)
diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py
index b3c47e6e..9492e1de 100644
--- a/utils/hosting_utils.py
+++ b/utils/hosting_utils.py
@@ -5,7 +5,7 @@ import subprocess
from oca.pool import WrongIdError
from datacenterlight.models import VMPricing
-from hosting.models import UserHostingKey, VMDetail
+from hosting.models import UserHostingKey, VMDetail, VATRates
from opennebula_api.serializers import VirtualMachineSerializer
logger = logging.getLogger(__name__)
@@ -150,6 +150,18 @@ def ping_ok(host_ipv6):
return True
+def get_vat_rate_for_country(country):
+ vat_rate = VATRates.objects.get(
+ territory_codes=country, start_date__isnull=False, stop_date=None
+ )
+ if vat_rate:
+ logger.debug("VAT rate for %s is %s" % (country, vat_rate.rate))
+ return vat_rate.rate
+ else:
+ logger.debug("Did not find VAT rate for %s, returning 0" % country)
+ return 0
+
+
class HostingUtils:
@staticmethod
def clear_items_from_list(from_list, items_list):
From 76c2b9d16caa47f55e8b9e93e73160c317e1466f Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 12:23:44 +0530
Subject: [PATCH 14/45] Fix bug: change arg name
---
datacenterlight/views.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index e13a31a6..b2e04bf6 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -417,8 +417,8 @@ class PaymentOrderView(FormView):
gp_details = {
"product_name": product.product_name,
"amount": product.get_actual_price(
- explicit_vat=get_vat_rate_for_country(
- address_form["country"])
+ vat_rate=get_vat_rate_for_country(
+ address_form.cleaned_data["country"])
),
"recurring": generic_payment_form.cleaned_data.get(
'recurring'
From 582e95218700074f82706ec864825c1fe11226ef Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 12:24:24 +0530
Subject: [PATCH 15/45] Convert VAT rate to decimal to be consistent
---
hosting/models.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/hosting/models.py b/hosting/models.py
index 00c89e11..3fbf3f66 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -1,3 +1,4 @@
+import decimal
import json
import logging
import os
@@ -83,7 +84,7 @@ class GenericProduct(AssignPermissionsMixin, models.Model):
return self.product_name
def get_actual_price(self, vat_rate=None):
- VAT = vat_rate if vat_rate is not None else self.product_vat
+ VAT = decimal.Decimal(vat_rate) if vat_rate is not None else self.product_vat
return round(
self.product_price + (self.product_price * VAT), 2
)
From d399fe6e7915eaa2305e397572716afb1937a7d5 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 12:24:39 +0530
Subject: [PATCH 16/45] Handle DoesNotExist better
---
utils/hosting_utils.py | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py
index 9492e1de..d3492a64 100644
--- a/utils/hosting_utils.py
+++ b/utils/hosting_utils.py
@@ -151,17 +151,20 @@ def ping_ok(host_ipv6):
def get_vat_rate_for_country(country):
- vat_rate = VATRates.objects.get(
- territory_codes=country, start_date__isnull=False, stop_date=None
- )
- if vat_rate:
+ vat_rate = None
+ try:
+ vat_rate = VATRates.objects.get(
+ territory_codes=country, start_date__isnull=False, stop_date=None
+ )
logger.debug("VAT rate for %s is %s" % (country, vat_rate.rate))
return vat_rate.rate
- else:
+ except VATRates.DoesNotExist as dne:
+ logger.debug(str(dne))
logger.debug("Did not find VAT rate for %s, returning 0" % country)
return 0
+
class HostingUtils:
@staticmethod
def clear_items_from_list(from_list, items_list):
From 940eaf3a07bf2e587ce5b9d4f68804d7df3354b8 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 12:39:03 +0530
Subject: [PATCH 17/45] Process prices as floats
---
hosting/models.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hosting/models.py b/hosting/models.py
index 3fbf3f66..b03b833b 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -84,9 +84,9 @@ class GenericProduct(AssignPermissionsMixin, models.Model):
return self.product_name
def get_actual_price(self, vat_rate=None):
- VAT = decimal.Decimal(vat_rate) if vat_rate is not None else self.product_vat
+ VAT = vat_rate if vat_rate is not None else self.product_vat
return round(
- self.product_price + (self.product_price * VAT), 2
+ float(self.product_price) + float(self.product_price * VAT), 2
)
From efe411933f1f6fa1af103ca8a9249c1a29760482 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 12:41:27 +0530
Subject: [PATCH 18/45] Missing float conversions
---
hosting/models.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hosting/models.py b/hosting/models.py
index b03b833b..34360f4d 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -86,7 +86,7 @@ class GenericProduct(AssignPermissionsMixin, models.Model):
def get_actual_price(self, vat_rate=None):
VAT = vat_rate if vat_rate is not None else self.product_vat
return round(
- float(self.product_price) + float(self.product_price * VAT), 2
+ float(self.product_price) + float(self.product_price) * float(VAT), 2
)
From 3599f0bff46ad6d37ac407f01cc0ea38f051d09e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 13:11:11 +0530
Subject: [PATCH 19/45] Show VAT elegantly
---
.../templates/datacenterlight/order_detail.html | 15 +++++++++++++++
datacenterlight/views.py | 10 ++++++++++
2 files changed, 25 insertions(+)
diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html
index 8a444bef..2aae3391 100644
--- a/datacenterlight/templates/datacenterlight/order_detail.html
+++ b/datacenterlight/templates/datacenterlight/order_detail.html
@@ -55,10 +55,25 @@
+ {% if generic_payment_details.vat_rate > 0 %}
+
+ {% trans "Price" %}:
+ CHF {{generic_payment_details.amount_before_vat|floatformat:2|intcomma}}
+
+
+ {% trans "VAT for" %} {{generic_payment_details.vat_country}} ({{generic_payment_details.vat_rate}}%) :
+ CHF {{generic_payment_details.vat_amount|floatformat:2|intcomma}}
+
+
+ {% trans "Total Amount" %} :
+ CHF {{generic_payment_details.amount|floatformat:2|intcomma}}
+
+ {% else %}
{% trans "Amount" %}:
CHF {{generic_payment_details.amount|floatformat:2|intcomma}}
+ {% endif %}
{% if generic_payment_details.description %}
{% trans "Description" %}:
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index b2e04bf6..6f7da18e 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -414,8 +414,18 @@ class PaymentOrderView(FormView):
product = generic_payment_form.cleaned_data.get(
'product_name'
)
+ user_country_vat_rate = get_vat_rate_for_country(
+ address_form.cleaned_data["country"]
+ )
gp_details = {
"product_name": product.product_name,
+ "vat_rate": user_country_vat_rate * 100,
+ "vat_amount": round(
+ float(product.product_price) *
+ user_country_vat_rate, 2),
+ "vat_country": address_form.cleaned_data["country"],
+ "amount_before_vat": round(
+ float(product.product_price), 2),
"amount": product.get_actual_price(
vat_rate=get_vat_rate_for_country(
address_form.cleaned_data["country"])
From f5372ecd1e622a41ac1701783a8a7804c06a9dfe Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 13:20:14 +0530
Subject: [PATCH 20/45] Fix bug: use country startswith instead of exact
matching
Countries like FR are represented as FR
MC
---
utils/hosting_utils.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py
index d3492a64..75e6c3de 100644
--- a/utils/hosting_utils.py
+++ b/utils/hosting_utils.py
@@ -154,7 +154,7 @@ def get_vat_rate_for_country(country):
vat_rate = None
try:
vat_rate = VATRates.objects.get(
- territory_codes=country, start_date__isnull=False, stop_date=None
+ territory_codes__startswith=country, start_date__isnull=False, stop_date=None
)
logger.debug("VAT rate for %s is %s" % (country, vat_rate.rate))
return vat_rate.rate
@@ -164,7 +164,6 @@ def get_vat_rate_for_country(country):
return 0
-
class HostingUtils:
@staticmethod
def clear_items_from_list(from_list, items_list):
From 069556d9b6053c2d67cf3466a8b18f779aa2049c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 13:31:21 +0530
Subject: [PATCH 21/45] Add monaco vat rate
---
vat_rates.csv | 1 +
1 file changed, 1 insertion(+)
diff --git a/vat_rates.csv b/vat_rates.csv
index e6aa31af..2a11ff81 100644
--- a/vat_rates.csv
+++ b/vat_rates.csv
@@ -312,3 +312,4 @@ IM",GBP,0.1,standard,
2011-01-04,,VG,USD,0,standard,British Virgin Islands (British overseas territory) is exempted of VAT.
2014-01-01,,CP,EUR,0,standard,Clipperton Island (French overseas possession) is exempted of VAT.
2019-11-15,,CH,CHF,0.077,standard,Switzerland standard VAT (added manually)
+2019-11-15,,MC,EUR,0.196,standard,Monaco standard VAT (added manually)
From 595409399928a9a04aa04b4cb88bad31a35f6811 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 13:32:04 +0530
Subject: [PATCH 22/45] Add France VAT rate
---
vat_rates.csv | 1 +
1 file changed, 1 insertion(+)
diff --git a/vat_rates.csv b/vat_rates.csv
index 2a11ff81..5ce73b1b 100644
--- a/vat_rates.csv
+++ b/vat_rates.csv
@@ -313,3 +313,4 @@ IM",GBP,0.1,standard,
2014-01-01,,CP,EUR,0,standard,Clipperton Island (French overseas possession) is exempted of VAT.
2019-11-15,,CH,CHF,0.077,standard,Switzerland standard VAT (added manually)
2019-11-15,,MC,EUR,0.196,standard,Monaco standard VAT (added manually)
+2019-11-15,,FR,EUR,0.2,standard,France standard VAT (added manually)
From f6feb8870866aef5cc7ec3c7727933e49241127c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 13:32:49 +0530
Subject: [PATCH 23/45] Revert back starts with logic
---
utils/hosting_utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py
index 75e6c3de..9c0243e4 100644
--- a/utils/hosting_utils.py
+++ b/utils/hosting_utils.py
@@ -154,7 +154,7 @@ def get_vat_rate_for_country(country):
vat_rate = None
try:
vat_rate = VATRates.objects.get(
- territory_codes__startswith=country, start_date__isnull=False, stop_date=None
+ territory_codes=country, start_date__isnull=False, stop_date=None
)
logger.debug("VAT rate for %s is %s" % (country, vat_rate.rate))
return vat_rate.rate
From 89418ca0089cb5bd68acdb7e4d20e143688cb1e5 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 13:47:24 +0530
Subject: [PATCH 24/45] Add Greece VAT rate
---
vat_rates.csv | 1 +
1 file changed, 1 insertion(+)
diff --git a/vat_rates.csv b/vat_rates.csv
index 5ce73b1b..8d7832c0 100644
--- a/vat_rates.csv
+++ b/vat_rates.csv
@@ -314,3 +314,4 @@ IM",GBP,0.1,standard,
2019-11-15,,CH,CHF,0.077,standard,Switzerland standard VAT (added manually)
2019-11-15,,MC,EUR,0.196,standard,Monaco standard VAT (added manually)
2019-11-15,,FR,EUR,0.2,standard,France standard VAT (added manually)
+2019-11-15,,GR,EUR,0.24,standard,Greece standard VAT (added manually)
From 871cccc2ae6b004df8b7b78a185de7ffb43aa4bc Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 16:55:39 +0530
Subject: [PATCH 25/45] Add UK VAT manually
---
vat_rates.csv | 1 +
1 file changed, 1 insertion(+)
diff --git a/vat_rates.csv b/vat_rates.csv
index 8d7832c0..4a3ec440 100644
--- a/vat_rates.csv
+++ b/vat_rates.csv
@@ -315,3 +315,4 @@ IM",GBP,0.1,standard,
2019-11-15,,MC,EUR,0.196,standard,Monaco standard VAT (added manually)
2019-11-15,,FR,EUR,0.2,standard,France standard VAT (added manually)
2019-11-15,,GR,EUR,0.24,standard,Greece standard VAT (added manually)
+2019-11-15,,GB,EUR,0.2,standard,UK standard VAT (added manually)
From a33a344b4095a1dd240e75cf1a1b631d267ffbbb Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 17:08:05 +0530
Subject: [PATCH 26/45] Update Changelog for 2.6.8
---
Changelog | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Changelog b/Changelog
index 7023cae5..8709d151 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,10 @@
+2.6.8: 2019-11-15
+ * feature: [EU VAT] Add EU VAT feature for generic products (MR!717)
+ Notes for deployment:
+ - do a db migrate a to create VATRates table
+ ./manage.py migrate hosting
+ - load vat_rates.csv
+ ./manage.py import_vat_rates vat_rates.csv
2.6.7: 2019-11-04
* bugfix: [admin] Improve dumpuser: show proper dates + bugfix
* bugfix: [admin] Improve fetch_stripe_bills:
From f0b604c6dcf7fa70e3115c35cd397a3e2510426c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 19:40:53 +0530
Subject: [PATCH 27/45] Update Generic product model to include
product_subscription_interval
---
hosting/models.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/hosting/models.py b/hosting/models.py
index 34360f4d..6050339a 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -79,6 +79,9 @@ class GenericProduct(AssignPermissionsMixin, models.Model):
product_price = models.DecimalField(max_digits=6, decimal_places=2)
product_vat = models.DecimalField(max_digits=6, decimal_places=4, default=0)
product_is_subscription = models.BooleanField(default=True)
+ product_subscription_interval = models.CharField(
+ max_length=10, default="month",
+ help_text="Choose between `year` and `month`")
def __str__(self):
return self.product_name
From 3bf2654b50e068bfbf1386077ecdc4346705185d Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 19:45:35 +0530
Subject: [PATCH 28/45] Update ProductPaymentForm for yearly subscription
---
hosting/forms.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/hosting/forms.py b/hosting/forms.py
index d98d258f..947cee44 100644
--- a/hosting/forms.py
+++ b/hosting/forms.py
@@ -109,9 +109,14 @@ class ProductPaymentForm(GenericPaymentForm):
)
)
if self.product.product_is_subscription:
+ payment_type = "month"
+ if self.product.product_subscription_interval == "month":
+ payment_type = _('Monthly subscription')
+ elif self.product.product_subscription_interval == "year":
+ payment_type = _('Yearly subscription')
self.fields['amount'].label = "{amt} ({payment_type})".format(
amt=_('Amount in CHF'),
- payment_type=_('Monthly subscription')
+ payment_type=payment_type
)
else:
self.fields['amount'].label = "{amt} ({payment_type})".format(
From e493a9f3d10cff5bcf064e1f3b5cde763a96f6f7 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 19:47:11 +0530
Subject: [PATCH 29/45] Allow creating yearly/monthly Stripe plans
---
datacenterlight/views.py | 13 +++++++++++--
utils/stripe_utils.py | 10 ++++++++--
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 6f7da18e..af6627ec 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -437,7 +437,9 @@ class PaymentOrderView(FormView):
'description'
),
"product_id": product.id,
- "product_slug": product.product_slug
+ "product_slug": product.product_slug,
+ "recurring_interval":
+ product.product_subscription_interval
}
request.session["generic_payment_details"] = (
gp_details
@@ -756,6 +758,7 @@ class OrderConfirmationView(DetailView, FormView):
if ('generic_payment_type' not in request.session or
(request.session['generic_payment_details']['recurring'])):
+ recurring_interval = 'month'
if 'generic_payment_details' in request.session:
amount_to_be_charged = (
round(
@@ -768,6 +771,10 @@ class OrderConfirmationView(DetailView, FormView):
amount_to_be_charged
)
stripe_plan_id = plan_name
+ recurring_interval = request.session['generic_payment_details']['recurring_interval']
+ if recurring_interval == "year":
+ plan_name = "{}-yearly".format(plan_name)
+ stripe_plan_id = plan_name
else:
template = request.session.get('template')
specs = request.session.get('specs')
@@ -794,7 +801,9 @@ class OrderConfirmationView(DetailView, FormView):
stripe_plan = stripe_utils.get_or_create_stripe_plan(
amount=amount_to_be_charged,
name=plan_name,
- stripe_plan_id=stripe_plan_id)
+ stripe_plan_id=stripe_plan_id,
+ interval=recurring_interval
+ )
subscription_result = stripe_utils.subscribe_customer_to_plan(
stripe_api_cus_id,
[{"plan": stripe_plan.get(
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index 4334d6cf..e2bdb983 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -226,7 +226,8 @@ class StripeUtils(object):
return charge
@handleStripeError
- def get_or_create_stripe_plan(self, amount, name, stripe_plan_id):
+ def get_or_create_stripe_plan(self, amount, name, stripe_plan_id,
+ interval=""):
"""
This function checks if a StripePlan with the given
stripe_plan_id already exists. If it exists then the function
@@ -238,6 +239,10 @@ class StripeUtils(object):
:param stripe_plan_id: The id of the Stripe plan to be
created. Use get_stripe_plan_id_string function to
obtain the name of the plan to be created
+ :param interval: str representing the interval of the Plan
+ Specifies billing frequency. Either day, week, month or year.
+ Ref: https://stripe.com/docs/api/plans/create#create_plan-interval
+ The default is month
:return: The StripePlan object if it exists else creates a
Plan object in Stripe and a local StripePlan and
returns it. Returns None in case of Stripe error
@@ -245,6 +250,7 @@ class StripeUtils(object):
_amount = float(amount)
amount = int(_amount * 100) # stripe amount unit, in cents
stripe_plan_db_obj = None
+ plan_interval = interval if interval is not "" else self.INTERVAL
try:
stripe_plan_db_obj = StripePlan.objects.get(
stripe_plan_id=stripe_plan_id)
@@ -252,7 +258,7 @@ class StripeUtils(object):
try:
self.stripe.Plan.create(
amount=amount,
- interval=self.INTERVAL,
+ interval=plan_interval,
name=name,
currency=self.CURRENCY,
id=stripe_plan_id)
From 435cfa46a6efcc03988a98e0b420e0edf5a69cf2 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 19:57:53 +0530
Subject: [PATCH 30/45] Change interval to year for that case in order
confirmation
---
datacenterlight/templates/datacenterlight/order_detail.html | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html
index 2aae3391..bc8e7562 100644
--- a/datacenterlight/templates/datacenterlight/order_detail.html
+++ b/datacenterlight/templates/datacenterlight/order_detail.html
@@ -154,7 +154,11 @@
{% if generic_payment_details %}
{% if generic_payment_details.recurring %}
-
{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{total_price}} CHF/month{% endblocktrans %}.
+ {% if generic_payment_details.recurring_interval == 'year' %}
+
{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{total_price}} CHF/year{% endblocktrans %}.
+ {% else %}
+
{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{total_price}} CHF/month{% endblocktrans %}.
+ {% endif %}
{% else %}
{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this payment will charge your credit card account with a one time amount of {{total_price}} CHF{% endblocktrans %}.
{% endif %}
From b790676940728b1976bc51d5845e527e7b73e17c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 20:07:26 +0530
Subject: [PATCH 31/45] Update datacenterlight's django.po
---
datacenterlight/locale/de/LC_MESSAGES/django.po | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po
index b5ff3ca5..165b527b 100644
--- a/datacenterlight/locale/de/LC_MESSAGES/django.po
+++ b/datacenterlight/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-07-03 11:18+0000\n"
+"POT-Creation-Date: 2019-11-15 14:29+0000\n"
"PO-Revision-Date: 2018-03-30 23:22+0000\n"
"Last-Translator: b'Anonymous User '\n"
"Language-Team: LANGUAGE \n"
@@ -434,12 +434,20 @@ msgstr "Mehrwertsteuer"
#| msgid ""
#| "By clicking \"Place order\" this plan will charge your credit card "
#| "account with %(total_price)s CHF/month"
+msgid ""
+"By clicking \"Place order\" this plan will charge your credit card account "
+"with %(total_price)s CHF/year"
+msgstr ""
+"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
+"%(total_price)s CHF pro Jahr berechnet"
+
+
msgid ""
"By clicking \"Place order\" this plan will charge your credit card account "
"with %(total_price)s CHF/month"
msgstr ""
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
-"%(vm_total_price)s CHF pro Monat belastet"
+"%(total_price)s CHF pro Monat belastet"
#, fuzzy, python-format
#| msgid ""
From 93527fdc02aed0b70475dd1be5cf6443a55033ed Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 20:22:14 +0530
Subject: [PATCH 32/45] Update datacenterlight's django.po
---
.../locale/de/LC_MESSAGES/django.po | 21 ++++++++++++-------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po
index 165b527b..a704c53b 100644
--- a/datacenterlight/locale/de/LC_MESSAGES/django.po
+++ b/datacenterlight/locale/de/LC_MESSAGES/django.po
@@ -375,6 +375,9 @@ msgstr "Letzten"
msgid "Type"
msgstr "Typ"
+msgid "Expiry"
+msgstr "Ablaufdatum"
+
msgid "SELECT"
msgstr "AUSWÄHLEN"
@@ -415,6 +418,15 @@ msgstr "Bestellungsübersicht"
msgid "Product"
msgstr "Produkt"
+msgid "Price"
+msgstr "Preise"
+
+msgid "VAT for"
+msgstr ""
+
+msgid "Total Amount"
+msgstr "Gesamtsumme"
+
msgid "Amount"
msgstr ""
@@ -439,8 +451,7 @@ msgid ""
"with %(total_price)s CHF/year"
msgstr ""
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
-"%(total_price)s CHF pro Jahr berechnet"
-
+"%(total_price)s CHF pro Jahr belastet"
msgid ""
"By clicking \"Place order\" this plan will charge your credit card account "
@@ -652,9 +663,6 @@ msgstr ""
#~ msgid "Card Number"
#~ msgstr "Kreditkartennummer"
-#~ msgid "Expiry Date"
-#~ msgstr "Ablaufdatum"
-
#~ msgid ""
#~ "You are not making any payment yet. After placing your order, you will be "
#~ "taken to the Submit Payment Page."
@@ -663,9 +671,6 @@ msgstr ""
#~ "ausgelöst, nachdem Du die Bestellung auf der nächsten Seite bestätigt "
#~ "hast."
-#~ msgid "Pricing"
-#~ msgstr "Preise"
-
#~ msgid "Order VM"
#~ msgstr "VM bestellen"
From 6eef592cd80376d491ddbe6a8e58ae1144d7ed77 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 20:28:00 +0530
Subject: [PATCH 33/45] Add migration file
---
...icproduct_product_subscription_interval.py | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 hosting/migrations/0058_genericproduct_product_subscription_interval.py
diff --git a/hosting/migrations/0058_genericproduct_product_subscription_interval.py b/hosting/migrations/0058_genericproduct_product_subscription_interval.py
new file mode 100644
index 00000000..c994ef16
--- /dev/null
+++ b/hosting/migrations/0058_genericproduct_product_subscription_interval.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2019-11-15 14:57
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('hosting', '0057_vatrates'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='genericproduct',
+ name='product_subscription_interval',
+ field=models.CharField(default='month', help_text='Choose between `year` and `month`', max_length=10),
+ ),
+ ]
From a423dd9f49858e1f226245ebb63c6af3ee026a0f Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 20:43:54 +0530
Subject: [PATCH 34/45] Correct invoice for yearly subscription
---
hosting/templates/hosting/invoice_detail.html | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html
index d757d476..a18af342 100644
--- a/hosting/templates/hosting/invoice_detail.html
+++ b/hosting/templates/hosting/invoice_detail.html
@@ -195,8 +195,13 @@
{% if invoice.order.subscription_id %}
{% trans "Recurring" %}:
- {{invoice.order.created_at|date:'d'|ordinal}}
+ {% if invoice.order.generic_product.product_subscription_interval == 'year' %}
+ {{invoice.order.created_at|date:'d'|ordinal}} {{invoice.order.created_at|date:'F'}}
+ {% trans "of every year" %}
+ {% else %}
+ {{invoice.order.created_at|date:'d'|ordinal}}
{% trans "of every month" %}
+ {% endif %}
{% endif %}
From 1e57eb5faeb0bb3160cbef226856c51950efe4ba Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 21:10:12 +0530
Subject: [PATCH 35/45] Handle TypeError raised in an invoice for generic
product
Case: No VM_ID exists and hence int(vm_id) raises TypeError
---
hosting/views.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/hosting/views.py b/hosting/views.py
index bb00978d..25303b99 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -1268,6 +1268,10 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView):
context['vm']['total_price'] = (
price + vat - discount['amount']
)
+ except TypeError:
+ logger.error("Type error. Probably we "
+ "came from a generic product. "
+ "Invoice ID %s" % obj.invoice_id)
except WrongIdError:
logger.error("WrongIdError while accessing "
"invoice {}".format(obj.invoice_id))
From 5697e313df291806871dc57b63916e17aa35a73a Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 21:11:26 +0530
Subject: [PATCH 36/45] Improve yearly recurring date text
---
hosting/templates/hosting/invoice_detail.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html
index a18af342..1a0421f2 100644
--- a/hosting/templates/hosting/invoice_detail.html
+++ b/hosting/templates/hosting/invoice_detail.html
@@ -196,8 +196,8 @@
{% trans "Recurring" %}:
{% if invoice.order.generic_product.product_subscription_interval == 'year' %}
- {{invoice.order.created_at|date:'d'|ordinal}} {{invoice.order.created_at|date:'F'}}
- {% trans "of every year" %}
+ {{invoice.order.created_at|date:'d'|ordinal}} {% trans "of" %}{{invoice.order.created_at|date:'F'}}
+ {% trans "each year" %}
{% else %}
{{invoice.order.created_at|date:'d'|ordinal}}
{% trans "of every month" %}
From e726f953a448b4eb8f31bf65a29bcea70187e8de Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 21:13:08 +0530
Subject: [PATCH 37/45] Improve yearly recurring date text
---
hosting/templates/hosting/invoice_detail.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html
index 1a0421f2..5e206070 100644
--- a/hosting/templates/hosting/invoice_detail.html
+++ b/hosting/templates/hosting/invoice_detail.html
@@ -196,7 +196,7 @@
{% trans "Recurring" %}:
{% if invoice.order.generic_product.product_subscription_interval == 'year' %}
- {{invoice.order.created_at|date:'d'|ordinal}} {% trans "of" %}{{invoice.order.created_at|date:'F'}}
+ {{invoice.order.created_at|date:'d'|ordinal}} {% trans "of" %} {{invoice.order.created_at|date:'M'}}
{% trans "each year" %}
{% else %}
{{invoice.order.created_at|date:'d'|ordinal}}
From 530e47586e4e764ffbbdd231d524be1c62b824f2 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 21:23:04 +0530
Subject: [PATCH 38/45] Fix month name
---
hosting/templates/hosting/invoice_detail.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html
index 5e206070..b9a3e742 100644
--- a/hosting/templates/hosting/invoice_detail.html
+++ b/hosting/templates/hosting/invoice_detail.html
@@ -196,7 +196,7 @@
{% trans "Recurring" %}:
{% if invoice.order.generic_product.product_subscription_interval == 'year' %}
- {{invoice.order.created_at|date:'d'|ordinal}} {% trans "of" %} {{invoice.order.created_at|date:'M'}}
+ {{invoice.order.created_at|date:'d'|ordinal}} {% trans "of" %} {{invoice.order.created_at|date:'b'|title}}
{% trans "each year" %}
{% else %}
{{invoice.order.created_at|date:'d'|ordinal}}
From 7dd57fb1166ab813118724226a8374beceb8efea Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 22:04:37 +0530
Subject: [PATCH 39/45] Fix old order detail page
---
hosting/templates/hosting/order_detail.html | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html
index a84e4e4f..2775882d 100644
--- a/hosting/templates/hosting/order_detail.html
+++ b/hosting/templates/hosting/order_detail.html
@@ -186,7 +186,13 @@
{% if order.subscription_id %}
{% trans "Recurring" %}:
- {{order.created_at|date:'d'|ordinal}} {% trans "of every month" %}
+ {% if order.generic_product.product_subscription_interval == 'year' %}
+ {{order.created_at|date:'d'|ordinal}} {% trans "of" %} {{order.created_at|date:'b'|title}}
+ {% trans "each year" %}
+ {% else %}
+ {{order.created_at|date:'d'|ordinal}}
+ {% trans "of every month" %}
+ {% endif %}
{% endif %}
From aec2002a9f4b2c846a6aa7c76c1cc7dd89374fce Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 22:11:15 +0530
Subject: [PATCH 40/45] Update django.po
---
hosting/locale/de/LC_MESSAGES/django.po | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po
index 5c719457..2e65bb71 100644
--- a/hosting/locale/de/LC_MESSAGES/django.po
+++ b/hosting/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-09-15 03:39+0000\n"
+"POT-Creation-Date: 2019-11-15 16:40+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -48,6 +48,9 @@ msgstr "Produkt"
msgid "Monthly subscription"
msgstr ""
+msgid "Yearly subscription"
+msgstr ""
+
msgid "One time payment"
msgstr ""
@@ -239,7 +242,8 @@ msgid "You can view your VM detail by clicking the button below."
msgstr "Um die Rechnung zu sehen, klicke auf den Button unten."
msgid "You can log in to your VM by the username puffy."
-msgstr "Du kannst Dich auf Deiner VM mit dem user puffy einloggen."
+msgstr ""
+"Du kannst Dich auf Deiner VM mit dem user puffy einloggen."
msgid "View Detail"
msgstr "Details anzeigen"
@@ -405,6 +409,12 @@ msgstr ""
msgid "Recurring"
msgstr ""
+msgid "of"
+msgstr ""
+
+msgid "each year"
+msgstr ""
+
msgid "of every month"
msgstr ""
@@ -426,9 +436,6 @@ msgstr "Siehe Rechnung"
msgid "Page"
msgstr ""
-msgid "of"
-msgstr ""
-
msgid "Log in"
msgstr "Anmelden"
From dc507396ebe3aaa3ebc2984dc22a31587f5c6fbe Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 22:16:11 +0530
Subject: [PATCH 41/45] Update Changelog for 2.6.9
---
Changelog | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Changelog b/Changelog
index 8709d151..04b699a9 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,8 @@
+2.6.9: 2019-11-15
+ * feature: Allow creating yearly subscriptions for Generic Products (MR!718)
+ Notes for deployment:
+ - do a db migrate for new column added to Generic Product model
+ ./manage.py migrate hosting
2.6.8: 2019-11-15
* feature: [EU VAT] Add EU VAT feature for generic products (MR!717)
Notes for deployment:
From 15ef20dbc10c6187cc1af7bde4c99e3203ff0306 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 22:58:47 +0530
Subject: [PATCH 42/45] Fix yearly email
---
datacenterlight/views.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index af6627ec..44226abb 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -994,6 +994,9 @@ class OrderConfirmationView(DetailView, FormView):
'reply_to': [context['email']],
}
send_plain_email_task.delay(email_data)
+ recurring_text = _(" This is a monthly recurring plan.")
+ if gp_details['recurring_interval'] == "year":
+ recurring_text = _(" This is an yearly recurring plan.")
email_data = {
'subject': _("Confirmation of your payment"),
@@ -1007,7 +1010,7 @@ class OrderConfirmationView(DetailView, FormView):
name=user.get('name'),
amount=gp_details['amount'],
recurring=(
- _(' This is a monthly recurring plan.')
+ recurring_text
if gp_details['recurring'] else ''
)
)
From 1ff577ddcdc32e2db59987cd4f82e87c4f12d90c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 15 Nov 2019 23:06:29 +0530
Subject: [PATCH 43/45] Update django.po
---
.../locale/de/LC_MESSAGES/django.po | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po
index a704c53b..7b381c16 100644
--- a/datacenterlight/locale/de/LC_MESSAGES/django.po
+++ b/datacenterlight/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-11-15 14:29+0000\n"
+"POT-Creation-Date: 2019-11-15 17:33+0000\n"
"PO-Revision-Date: 2018-03-30 23:22+0000\n"
"Last-Translator: b'Anonymous User '\n"
"Language-Team: LANGUAGE \n"
@@ -450,15 +450,16 @@ msgid ""
"By clicking \"Place order\" this plan will charge your credit card account "
"with %(total_price)s CHF/year"
msgstr ""
-"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
-"%(total_price)s CHF pro Jahr belastet"
+"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(total_price)s "
+"CHF pro Jahr belastet"
+
msgid ""
"By clicking \"Place order\" this plan will charge your credit card account "
"with %(total_price)s CHF/month"
msgstr ""
-"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
-"%(total_price)s CHF pro Monat belastet"
+"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(total_price)s "
+"CHF pro Monat belastet"
#, fuzzy, python-format
#| msgid ""
@@ -617,10 +618,13 @@ msgid "An error occurred while associating the card. Details: {details}"
msgstr ""
"Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: {details}"
-msgid "Confirmation of your payment"
+msgid " This is a monthly recurring plan."
msgstr ""
-msgid " This is a monthly recurring plan."
+msgid " This is an yearly recurring plan."
+msgstr ""
+
+msgid "Confirmation of your payment"
msgstr ""
#, python-brace-format
From f82ed81b332a3f895035945c04862d0e898d739d Mon Sep 17 00:00:00 2001
From: _moep_
Date: Sat, 16 Nov 2019 08:30:44 +0100
Subject: [PATCH 44/45] add german translation
---
.../locale/de/LC_MESSAGES/django.po | 53 ++++++++++---------
1 file changed, 27 insertions(+), 26 deletions(-)
diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po
index 7b381c16..6d58673a 100644
--- a/datacenterlight/locale/de/LC_MESSAGES/django.po
+++ b/datacenterlight/locale/de/LC_MESSAGES/django.po
@@ -20,7 +20,7 @@ msgstr ""
"X-Translated-Using: django-rosetta 0.8.1\n"
msgid "CMS Favicon"
-msgstr ""
+msgstr "CMS Favicon"
#, python-format
msgid "Your New VM %(vm_name)s at Data Center Light"
@@ -52,7 +52,7 @@ msgid "Login"
msgstr "Anmelden"
msgid "Dashboard"
-msgstr ""
+msgstr "Dashboard"
msgid "Thank you for contacting us."
msgstr "Nachricht gesendet."
@@ -64,7 +64,7 @@ msgid "Get in touch with us!"
msgstr "Sende uns eine Nachricht."
msgid "Name"
-msgstr ""
+msgstr "Name"
msgid "Please enter your name."
msgstr "Bitte gib Deinen Namen ein."
@@ -108,7 +108,7 @@ msgid "Your account details are as follows"
msgstr "Deine Account Details sind unten aufgelistet"
msgid "Username"
-msgstr "Username"
+msgstr "Benusername"
msgid "Your email address"
msgstr "Deine E-Mail-Adresse"
@@ -155,7 +155,7 @@ msgid "Please enter a value in range %(min_ram)s - 200."
msgstr "Bitte gib einen Wert von %(min_ram)s bis 200 ein."
msgid "VM hosting"
-msgstr ""
+msgstr "VM Hosting"
msgid "month"
msgstr "Monat"
@@ -207,14 +207,14 @@ msgstr ""
msgid "Only wants you to pay for what you actually need."
msgstr ""
-"Möchte, dass du nur bezahlst, was du auch wirklich brauchst: Wähle deine "
+"Du möchtest nur das bezahlen, was du auch wirklich brauchst: Wähle deine "
"Ressourcen individuell aus!
"
msgid ""
"Is creative, using a modern and alternative design for a data center in "
"order to make it more sustainable and affordable at the same time."
msgstr ""
-"Ist kreativ, indem es sich ein modernes und alternatives Layout zu Nutze "
+"Es ist kreativ, da es sich ein modernes und alternatives Layout zu Nutze"
"macht um Nachhaltigkeit zu fördern und somit erschwingliche Preise bieten zu "
"können.
"
@@ -222,9 +222,9 @@ msgid ""
"Cuts down the costs for you by using FOSS (Free Open Source Software) "
"exclusively, wherefore we can save money from paying licenses."
msgstr ""
-"Sorgt dafür, dass unnötige Kosten erspart werden, indem es ausschliesslich "
-"mit FOSS (Free Open Source Software) arbeitet und wir daher auf "
-"Lizenzgebühren verzichten können.
"
+"Um unnötige Kosten zu sparen werden, wird ausschliesslich Software auf"
+"Basis von FOSS (Free Open Source Software) eingesetzt und dadurch können auf "
+"Lizenzgebühren verzichtet werden.
"
msgid "Scale out"
msgstr "Skalierung"
@@ -311,7 +311,7 @@ msgid "Billing Address"
msgstr "Rechnungsadresse"
msgid "Make a payment"
-msgstr ""
+msgstr "Tätige eine Bezahlung"
msgid "Your Order"
msgstr "Deine Bestellung"
@@ -422,19 +422,19 @@ msgid "Price"
msgstr "Preise"
msgid "VAT for"
-msgstr ""
+msgstr "MwSt für"
msgid "Total Amount"
msgstr "Gesamtsumme"
msgid "Amount"
-msgstr ""
+msgstr "Betrag"
msgid "Description"
-msgstr ""
+msgstr "Beschreibung"
msgid "Recurring"
-msgstr ""
+msgstr "Wiederholend"
msgid "Subtotal"
msgstr "Zwischensumme"
@@ -490,10 +490,10 @@ msgid "Hold tight, we are processing your request"
msgstr "Bitte warten - wir verarbeiten Deine Anfrage gerade"
msgid "OK"
-msgstr ""
+msgstr "Ok"
msgid "Close"
-msgstr ""
+msgstr "Schliessen"
msgid "Some problem encountered. Please try again later."
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
@@ -505,7 +505,7 @@ msgid "Tech Stack"
msgstr "Tech Stack"
msgid "We are seriously open source."
-msgstr "Wir sind vollends opensource."
+msgstr "Wir sind vollends Open Source."
msgid ""
" Our full software stack is open source – We don't use anything that isn't "
@@ -575,13 +575,13 @@ msgid "Starting from only 15CHF per month. Try now."
msgstr "Unser Angebot beginnt bei 15 CHF pro Monat. Probier's jetzt aus!"
msgid "Actions speak louder than words. Let's do it, try our VM now."
-msgstr "Tagen sagen mehr als Worte – Teste jetzt unsere VM!"
+msgstr "Taten sagen mehr als Worte – Teste jetzt unsere VM!"
msgid "Invalid number of cores"
msgstr "Ungültige Anzahle CPU-Kerne"
msgid "Invalid calculator properties"
-msgstr ""
+msgstr "Ungültige Berechnungseigenschaften"
msgid "Invalid RAM size"
msgstr "Ungültige RAM-Grösse"
@@ -591,7 +591,7 @@ msgstr "Ungültige Speicher-Grösse"
#, python-brace-format
msgid "Incorrect pricing name. Please contact support{support_email}"
-msgstr ""
+msgstr "Ungültige Preisbezeichnung. Bitte kontaktiere den Support{support_email}"
#, python-brace-format
msgid "{user} does not have permission to access the card"
@@ -619,13 +619,13 @@ msgstr ""
"Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: {details}"
msgid " This is a monthly recurring plan."
-msgstr ""
+msgstr "Dies ist ein monatlich wiederkehrender Plan."
msgid " This is an yearly recurring plan."
-msgstr ""
+msgstr "Dies ist ein jährlich wiederkehrender Plan."
msgid "Confirmation of your payment"
-msgstr ""
+msgstr "Bestätigung deiner Zahlung"
#, python-brace-format
msgid ""
@@ -636,7 +636,8 @@ msgid ""
"\n"
"Cheers,\n"
"Your Data Center Light team"
-msgstr ""
+msgstr "Hallo {name},\n" "\n" "vielen Dank für deine Bestellung!\n" "Wir haben deine Bezahlung in Höhe von {amount:.2f} CHF erhalten. {recurring}\n" "\n" "Grüsse\n"
+"Dein Data Center Light Team"
msgid "Thank you for the payment."
msgstr "Danke für Deine Bestellung."
@@ -644,7 +645,7 @@ msgstr "Danke für Deine Bestellung."
msgid ""
"You will soon receive a confirmation email of the payment. You can always "
"contact us at info@ungleich.ch for any question that you may have."
-msgstr ""
+msgstr "Du wirst bald eine Bestätigungs-E-Mail über die Zahlung erhalten. Du kannst jederzeit unter info@ungleich.ch kontaktieren."
msgid "Thank you for the order."
msgstr "Danke für Deine Bestellung."
From 49ef761b2e2d856d88b63ccec5db524a73945f00 Mon Sep 17 00:00:00 2001
From: _moep_
Date: Sat, 16 Nov 2019 08:45:47 +0100
Subject: [PATCH 45/45] translate it, too
---
hosting/locale/de/LC_MESSAGES/django.po | 36 ++++++++++++-------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po
index 2e65bb71..08bcdd7a 100644
--- a/hosting/locale/de/LC_MESSAGES/django.po
+++ b/hosting/locale/de/LC_MESSAGES/django.po
@@ -28,31 +28,31 @@ msgid "User does not exist"
msgstr "Der Benutzer existiert nicht"
msgid "Choose a product"
-msgstr ""
+msgstr "Wähle ein Produkt"
msgid "Amount in CHF"
msgstr "Betrag"
msgid "Recurring monthly"
-msgstr ""
+msgstr "monatlich wiederkehrend"
msgid "Amount field does not match"
-msgstr ""
+msgstr "Betragsfeld stimmt nicht überein"
msgid "Recurring field does not match"
-msgstr ""
+msgstr "Betragsfeld stimmt nicht überein"
msgid "Product name"
msgstr "Produkt"
msgid "Monthly subscription"
-msgstr ""
+msgstr "Monatliches Abonnement"
msgid "Yearly subscription"
-msgstr ""
+msgstr "Jährliches Abonnement"
msgid "One time payment"
-msgstr ""
+msgstr "Einmalzahlung"
msgid "Confirm Password"
msgstr "Passwort Bestätigung"
@@ -76,7 +76,7 @@ msgid "Please input a proper SSH key"
msgstr "Bitte verwende einen gültigen SSH-Key"
msgid "Comma not accepted in the name of the key"
-msgstr ""
+msgstr "Komma im Namen des Keys wird nicht akzeptiert"
msgid "All Rights Reserved"
msgstr "Alle Rechte vorbehalten"
@@ -404,19 +404,19 @@ msgid "Amount"
msgstr "Betrag"
msgid "Description"
-msgstr ""
+msgstr "Beschreibung"
msgid "Recurring"
-msgstr ""
+msgstr "wiederkehrend"
msgid "of"
-msgstr ""
+msgstr "von"
msgid "each year"
-msgstr ""
+msgstr "jedes Jahr"
msgid "of every month"
-msgstr ""
+msgstr "jeden Monat"
msgid "BACK TO LIST"
msgstr "ZURÜCK ZUR LISTE"
@@ -428,13 +428,13 @@ msgid "VM ID"
msgstr ""
msgid "IP Address"
-msgstr ""
+msgstr "IP-Adresse"
msgid "See Invoice"
msgstr "Siehe Rechnung"
msgid "Page"
-msgstr ""
+msgstr "Seite"
msgid "Log in"
msgstr "Anmelden"
@@ -496,7 +496,7 @@ msgid "Hold tight, we are processing your request"
msgstr "Bitte warten - wir bearbeiten Deine Anfrage gerade"
msgid "OK"
-msgstr ""
+msgstr "Ok"
msgid "Close"
msgstr "Schliessen"
@@ -852,7 +852,7 @@ msgstr "Ungültige Speicher-Grösse"
#, python-brace-format
msgid "Incorrect pricing name. Please contact support{support_email}"
-msgstr ""
+msgstr "Ungültige Preisbezeichnung. Bitte kontaktiere den Support{support_email}"
msgid ""
"We could not find the requested VM. Please "
@@ -871,7 +871,7 @@ msgstr "Fehler beenden VM"
msgid ""
"VM terminate action timed out. Please contact support@datacenterlight.ch for "
"further information."
-msgstr ""
+msgstr "VM beendet wegen Zeitüberschreitung. Bitte kontaktiere support@datacenterlight.ch für weitere Informationen."
#, python-format
msgid "Virtual Machine %(vm_name)s Cancelled"