From a92088710080f842630ccc14df08a8d42e055081 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Sun, 17 Jan 2021 15:53:30 +0100 Subject: [PATCH] ++bridge update --- bin/make-migrations-from-scratch.sh | 0 doc/uncloud-manual-2020-08-01.org | 26 +++- uncloud/migrations/0004_auto_20210101_1308.py | 19 +++ uncloud/models.py | 2 +- uncloud/static/uncloud/uncloud.css | 4 + uncloud/templates/uncloud/base.html | 29 +++- uncloud/templates/uncloud/index.html | 137 +++++++++++++----- uncloud/urls.py | 10 +- .../templates/uncloud_auth/login.html | 13 +- uncloud_pay/admin.py | 12 +- .../migrations/0011_auto_20210101_1308.py | 19 +++ uncloud_pay/models.py | 85 +++-------- uncloud_pay/selectors.py | 3 + uncloud_pay/serializers.py | 10 +- uncloud_pay/services.py | 32 ++++ .../templates/uncloud_pay/bill.html.j2 | 12 +- 16 files changed, 275 insertions(+), 138 deletions(-) mode change 100644 => 100755 bin/make-migrations-from-scratch.sh create mode 100644 uncloud/migrations/0004_auto_20210101_1308.py create mode 100644 uncloud/static/uncloud/uncloud.css create mode 100644 uncloud_pay/migrations/0011_auto_20210101_1308.py create mode 100644 uncloud_pay/services.py diff --git a/bin/make-migrations-from-scratch.sh b/bin/make-migrations-from-scratch.sh old mode 100644 new mode 100755 diff --git a/doc/uncloud-manual-2020-08-01.org b/doc/uncloud-manual-2020-08-01.org index 381bb62..b997600 100644 --- a/doc/uncloud-manual-2020-08-01.org +++ b/doc/uncloud-manual-2020-08-01.org @@ -1,4 +1,4 @@ -* Bootstrap / Installation +* Bootstrap / Installation / Deployment ** Pre-requisites by operating system *** General To run uncloud you need: @@ -150,7 +150,6 @@ g #+END_SRC Workers usually should have an "uncloud" user account, even though strictly speaking the username can be any. - *** WireGuardVPN Server - Allow write access to /etc/wireguard for uncloud user - Allow sudo access to "ip" and "wg" @@ -161,7 +160,11 @@ g #+END_SRC app ALL=(ALL) NOPASSWD:/sbin/ip app ALL=(ALL) NOPASSWD:/usr/bin/wg #+END_SRC - +** Typical source code based deployment + - Deploy using bin/deploy.sh on a remote server + - Remote server should have + - postgresql running, accessible via TLS from outside + - rabbitmq-configured [in progress] * Testing / CLI Access Access via the commandline (CLI) can be done using curl or @@ -462,6 +465,21 @@ Q vpn-2a0ae5c1200.ungleich.ch - query on that flag - verify it every time - ***** TODO Generating bill for admins/staff - + + + + +**** Bill fixes needed +***** TODO Double bill in bill id +***** TODO Name the currency +***** TODO Maybe remove the chromium pdf rendering artefacts + - date on the top + - title on the top + - filename bottom left + - page number could even stay +***** TODO Try to shorten the timestamp (remove time zone?) +***** TODO Bill date might be required +***** TODO Total and VAT are empty +***** TODO Line below detail/ heading diff --git a/uncloud/migrations/0004_auto_20210101_1308.py b/uncloud/migrations/0004_auto_20210101_1308.py new file mode 100644 index 0000000..8385b16 --- /dev/null +++ b/uncloud/migrations/0004_auto_20210101_1308.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1 on 2021-01-01 13:08 + +from django.db import migrations +import uncloud.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('uncloud', '0003_auto_20201220_1728'), + ] + + operations = [ + migrations.AlterField( + model_name='uncloudprovider', + name='country', + field=uncloud.models.CountryField(choices=[('AD', 'Andorra'), ('AE', 'United Arab Emirates'), ('AF', 'Afghanistan'), ('AG', 'Antigua & Barbuda'), ('AI', 'Anguilla'), ('AL', 'Albania'), ('AM', 'Armenia'), ('AN', 'Netherlands Antilles'), ('AO', 'Angola'), ('AQ', 'Antarctica'), ('AR', 'Argentina'), ('AS', 'American Samoa'), ('AT', 'Austria'), ('AU', 'Australia'), ('AW', 'Aruba'), ('AZ', 'Azerbaijan'), ('BA', 'Bosnia and Herzegovina'), ('BB', 'Barbados'), ('BD', 'Bangladesh'), ('BE', 'Belgium'), ('BF', 'Burkina Faso'), ('BG', 'Bulgaria'), ('BH', 'Bahrain'), ('BI', 'Burundi'), ('BJ', 'Benin'), ('BM', 'Bermuda'), ('BN', 'Brunei Darussalam'), ('BO', 'Bolivia'), ('BR', 'Brazil'), ('BS', 'Bahama'), ('BT', 'Bhutan'), ('BV', 'Bouvet Island'), ('BW', 'Botswana'), ('BY', 'Belarus'), ('BZ', 'Belize'), ('CA', 'Canada'), ('CC', 'Cocos (Keeling) Islands'), ('CF', 'Central African Republic'), ('CG', 'Congo'), ('CH', 'Switzerland'), ('CI', 'Ivory Coast'), ('CK', 'Cook Iislands'), ('CL', 'Chile'), ('CM', 'Cameroon'), ('CN', 'China'), ('CO', 'Colombia'), ('CR', 'Costa Rica'), ('CU', 'Cuba'), ('CV', 'Cape Verde'), ('CX', 'Christmas Island'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DE', 'Germany'), ('DJ', 'Djibouti'), ('DK', 'Denmark'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('DZ', 'Algeria'), ('EC', 'Ecuador'), ('EE', 'Estonia'), ('EG', 'Egypt'), ('EH', 'Western Sahara'), ('ER', 'Eritrea'), ('ES', 'Spain'), ('ET', 'Ethiopia'), ('FI', 'Finland'), ('FJ', 'Fiji'), ('FK', 'Falkland Islands (Malvinas)'), ('FM', 'Micronesia'), ('FO', 'Faroe Islands'), ('FR', 'France'), ('FX', 'France, Metropolitan'), ('GA', 'Gabon'), ('GB', 'United Kingdom (Great Britain)'), ('GD', 'Grenada'), ('GE', 'Georgia'), ('GF', 'French Guiana'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GL', 'Greenland'), ('GM', 'Gambia'), ('GN', 'Guinea'), ('GP', 'Guadeloupe'), ('GQ', 'Equatorial Guinea'), ('GR', 'Greece'), ('GS', 'South Georgia and the South Sandwich Islands'), ('GT', 'Guatemala'), ('GU', 'Guam'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HK', 'Hong Kong'), ('HM', 'Heard & McDonald Islands'), ('HN', 'Honduras'), ('HR', 'Croatia'), ('HT', 'Haiti'), ('HU', 'Hungary'), ('ID', 'Indonesia'), ('IE', 'Ireland'), ('IL', 'Israel'), ('IN', 'India'), ('IO', 'British Indian Ocean Territory'), ('IQ', 'Iraq'), ('IR', 'Islamic Republic of Iran'), ('IS', 'Iceland'), ('IT', 'Italy'), ('JM', 'Jamaica'), ('JO', 'Jordan'), ('JP', 'Japan'), ('KE', 'Kenya'), ('KG', 'Kyrgyzstan'), ('KH', 'Cambodia'), ('KI', 'Kiribati'), ('KM', 'Comoros'), ('KN', 'St. Kitts and Nevis'), ('KP', "Korea, Democratic People's Republic of"), ('KR', 'Korea, Republic of'), ('KW', 'Kuwait'), ('KY', 'Cayman Islands'), ('KZ', 'Kazakhstan'), ('LA', "Lao People's Democratic Republic"), ('LB', 'Lebanon'), ('LC', 'Saint Lucia'), ('LI', 'Liechtenstein'), ('LK', 'Sri Lanka'), ('LR', 'Liberia'), ('LS', 'Lesotho'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('LV', 'Latvia'), ('LY', 'Libyan Arab Jamahiriya'), ('MA', 'Morocco'), ('MC', 'Monaco'), ('MD', 'Moldova, Republic of'), ('MG', 'Madagascar'), ('MH', 'Marshall Islands'), ('ML', 'Mali'), ('MN', 'Mongolia'), ('MM', 'Myanmar'), ('MO', 'Macau'), ('MP', 'Northern Mariana Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MS', 'Monserrat'), ('MT', 'Malta'), ('MU', 'Mauritius'), ('MV', 'Maldives'), ('MW', 'Malawi'), ('MX', 'Mexico'), ('MY', 'Malaysia'), ('MZ', 'Mozambique'), ('NA', 'Namibia'), ('NC', 'New Caledonia'), ('NE', 'Niger'), ('NF', 'Norfolk Island'), ('NG', 'Nigeria'), ('NI', 'Nicaragua'), ('NL', 'Netherlands'), ('NO', 'Norway'), ('NP', 'Nepal'), ('NR', 'Nauru'), ('NU', 'Niue'), ('NZ', 'New Zealand'), ('OM', 'Oman'), ('PA', 'Panama'), ('PE', 'Peru'), ('PF', 'French Polynesia'), ('PG', 'Papua New Guinea'), ('PH', 'Philippines'), ('PK', 'Pakistan'), ('PL', 'Poland'), ('PM', 'St. Pierre & Miquelon'), ('PN', 'Pitcairn'), ('PR', 'Puerto Rico'), ('PT', 'Portugal'), ('PW', 'Palau'), ('PY', 'Paraguay'), ('QA', 'Qatar'), ('RE', 'Reunion'), ('RO', 'Romania'), ('RU', 'Russian Federation'), ('RW', 'Rwanda'), ('SA', 'Saudi Arabia'), ('SB', 'Solomon Islands'), ('SC', 'Seychelles'), ('SD', 'Sudan'), ('SE', 'Sweden'), ('SG', 'Singapore'), ('SH', 'St. Helena'), ('SI', 'Slovenia'), ('SJ', 'Svalbard & Jan Mayen Islands'), ('SK', 'Slovakia'), ('SL', 'Sierra Leone'), ('SM', 'San Marino'), ('SN', 'Senegal'), ('SO', 'Somalia'), ('SR', 'Suriname'), ('ST', 'Sao Tome & Principe'), ('SV', 'El Salvador'), ('SY', 'Syrian Arab Republic'), ('SZ', 'Swaziland'), ('TC', 'Turks & Caicos Islands'), ('TD', 'Chad'), ('TF', 'French Southern Territories'), ('TG', 'Togo'), ('TH', 'Thailand'), ('TJ', 'Tajikistan'), ('TK', 'Tokelau'), ('TM', 'Turkmenistan'), ('TN', 'Tunisia'), ('TO', 'Tonga'), ('TP', 'East Timor'), ('TR', 'Turkey'), ('TT', 'Trinidad & Tobago'), ('TV', 'Tuvalu'), ('TW', 'Taiwan, Province of China'), ('TZ', 'Tanzania, United Republic of'), ('UA', 'Ukraine'), ('UG', 'Uganda'), ('UM', 'United States Minor Outlying Islands'), ('US', 'United States of America'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VA', 'Vatican City State (Holy See)'), ('VC', 'St. Vincent & the Grenadines'), ('VE', 'Venezuela'), ('VG', 'British Virgin Islands'), ('VI', 'United States Virgin Islands'), ('VN', 'Viet Nam'), ('VU', 'Vanuatu'), ('WF', 'Wallis & Futuna Islands'), ('WS', 'Samoa'), ('YE', 'Yemen'), ('YT', 'Mayotte'), ('YU', 'Yugoslavia'), ('ZA', 'South Africa'), ('ZM', 'Zambia'), ('ZR', 'Zaire'), ('ZW', 'Zimbabwe')], default='CH', max_length=2), + ), + ] diff --git a/uncloud/models.py b/uncloud/models.py index 535d920..d956637 100644 --- a/uncloud/models.py +++ b/uncloud/models.py @@ -61,7 +61,7 @@ class UncloudAddress(models.Model): street = models.CharField(max_length=256) city = models.CharField(max_length=256) postal_code = models.CharField(max_length=64) - country = CountryField(blank=True) + country = CountryField(blank=False, null=False) class Meta: abstract = True diff --git a/uncloud/static/uncloud/uncloud.css b/uncloud/static/uncloud/uncloud.css new file mode 100644 index 0000000..51d93ef --- /dev/null +++ b/uncloud/static/uncloud/uncloud.css @@ -0,0 +1,4 @@ +#content { + width: 400px; + margin: auto; +} diff --git a/uncloud/templates/uncloud/base.html b/uncloud/templates/uncloud/base.html index 2273591..44b5008 100644 --- a/uncloud/templates/uncloud/base.html +++ b/uncloud/templates/uncloud/base.html @@ -2,7 +2,6 @@ {% load bootstrap4 %} - @@ -15,7 +14,33 @@ {% block header %}{% endblock %} -{% block bootstrap4_content %} + {% block bootstrap4_content %} + + +
{% block body %}{% endblock %}
diff --git a/uncloud/templates/uncloud/index.html b/uncloud/templates/uncloud/index.html index 6a88f99..97fda34 100644 --- a/uncloud/templates/uncloud/index.html +++ b/uncloud/templates/uncloud/index.html @@ -3,51 +3,109 @@ {% block body %}
-

Welcome to uncloud

-
- Welcome to uncloud, the Open Source cloud management - system by ungleich. - It is an API driven system with - some convience views provided by - the Django Rest - Framework. You can - freely access - the source code of uncloud. +
+
+

Welcome to uncloud

+
+
+
+

About uncloud

+
+

+ Welcome to uncloud, the Open Source cloud management + system by ungleich. + It is an API driven system with + some convience views provided by + the Django Rest + Framework. You can + freely access + the source code of uncloud. +

+
+
+
+

Getting started

+
+

uncloud is designed to be as easy as possible to use. However, + there are some "real world" requirements that need to be met to + start using uncloud: -

-

Credit cards

-
+
    +
  • First you need + to register an + account. If you already have one, you can + login. +
  • If you have forgotten your password or other issues with + logging in, you can contact the ungleich support + via support at ungleich.ch. + +
  • Secondy you will need to + create a billing + address. This is required for determining the correct + tax. +
  • Next you will need to + register a credit card + from which payments can be made. Your credit card will not + be charged without your consent. +
+
+
+ +
+

Credit cards

+
+

Credit cards are registered with stripe. We only save a the last 4 digits and the expiry date of the card to make identification for you easier. -

-
-
    -
  • Register a credit card - (this is required to be done via Javascript so that we never see - your credit card, but it is sent directly to stripe) -
  • You can list your - credit cards - By default the first credit card is used for charging - ("active: true") and later added cards will not be - used. To change this, first disable the active flag and - then set it on another credit card. -
-
-
-

Payments and Balance

- To trigger a payment - +

+
  • Register a credit card + (this is required to be done via Javascript so that we never see + your credit card, but it is sent directly to stripe) +
  • You can list your + credit cards + By default the first credit card is used for charging + ("active: true") and later added cards will not be + used. To change this, first disable the active flag and + then set it on another credit card. +
  • -
    -

    Networking

    - With uncloud you can use a variety of network related services. +
    +

    Billing Address, Payments and Balance

    +
    +

    Billing addresses behave similar to credit cards: you can + have many of them, but only one can be active. The active + billing address is taken for creating new orders.

    + +

    In uncloud we use the pre-paid model: you can add money to + your account via payments. You can always check your + balance. The products you use will automatically be charged from + your existing balance. +

    + +

    In the future you will be able opt-in to automatically + recharging your account at a certain time frame or whenever it + is below a certain amount

    + + + +
    +
    + +
    +

    Networking

    +
    +

    + With uncloud you can use a variety of network related + services. +

    +
    - -
    {% endblock %} diff --git a/uncloud/urls.py b/uncloud/urls.py index 82bce86..bf3672c 100644 --- a/uncloud/urls.py +++ b/uncloud/urls.py @@ -37,7 +37,7 @@ router.register(r'beta/vm', vmviews.NicoVMProductViewSet, basename='nicovmproduc # Pay -# router.register(r'v1/my/address', payviews.BillingAddressViewSet, basename='billingaddress') + # router.register(r'v1/my/bill', payviews.BillViewSet, basename='bill') # router.register(r'v1/my/order', payviews.OrderViewSet, basename='order') # router.register(r'v1/my/payment-method', payviews.PaymentMethodViewSet, basename='payment-method') @@ -49,9 +49,9 @@ router.register(r'beta/vm', vmviews.NicoVMProductViewSet, basename='nicovmproduc # router.register(r'v1/admin/vmcluster', vmviews.VMClusterViewSet) # User/Account -router.register(r'v1/my/user', authviews.UserViewSet, basename='user') -router.register(r'v1/admin/user', authviews.AdminUserViewSet, basename='useradmin') -router.register(r'v1/user/register', authviews.AccountManagementViewSet, basename='user/register') +# router.register(r'v1/my/user', authviews.UserViewSet, basename='user') +# router.register(r'v1/admin/user', authviews.AdminUserViewSet, basename='useradmin') +# router.register(r'v1/user/register', authviews.AccountManagementViewSet, basename='user/register') ################################################################################ @@ -65,7 +65,7 @@ router.register(r'v2/net/wireguardvpnsizes', netviews.WireGuardVPNSizes, basenam router.register(r'v2/payment/credit-card', payviews.CreditCardViewSet, basename='stripecreditcard') router.register(r'v2/payment/payment', payviews.PaymentViewSet, basename='payment') router.register(r'v2/payment/balance', payviews.BalanceViewSet, basename='payment-balance') - +router.register(r'v2/payment/address', payviews.BillingAddressViewSet, basename='billingaddress') urlpatterns = [ path(r'api/', include(router.urls), name='api'), diff --git a/uncloud_auth/templates/uncloud_auth/login.html b/uncloud_auth/templates/uncloud_auth/login.html index 04f9a15..887467b 100644 --- a/uncloud_auth/templates/uncloud_auth/login.html +++ b/uncloud_auth/templates/uncloud_auth/login.html @@ -1,13 +1,14 @@ {% extends 'uncloud/base.html' %} +{% load bootstrap4 %} {% block body %} -
    - -
    +

    Login to uncloud

    + {% csrf_token %} - {{ form }} - + {% bootstrap_form form %} + {% buttons %} + + {% endbuttons %}
    -
    {% endblock %} diff --git a/uncloud_pay/admin.py b/uncloud_pay/admin.py index d8b09da..f604283 100644 --- a/uncloud_pay/admin.py +++ b/uncloud_pay/admin.py @@ -47,9 +47,13 @@ class BillAdmin(admin.ModelAdmin): raise self._get_404_exception(object_id) output_file = NamedTemporaryFile() - bill_html = render_to_string("bill.html.j2", {'bill': bill, - 'bill_records': bill.billrecord_set.all() - }) + bill_html = render_to_string( + "uncloud_pay/bill.html.j2", + { + 'bill': bill, + 'bill_records': bill.billrecord_set.all() + } + ) bytestring_to_pdf(bill_html.encode('utf-8'), output_file) response = FileResponse(output_file, content_type="application/pdf") @@ -63,7 +67,7 @@ class BillAdmin(admin.ModelAdmin): if bill is None: raise self._get_404_exception(object_id) - return render(request, 'bill.html.j2', + return render(request, 'uncloud_pay/bill.html.j2', {'bill': bill, 'bill_records': bill.billrecord_set.all() }) diff --git a/uncloud_pay/migrations/0011_auto_20210101_1308.py b/uncloud_pay/migrations/0011_auto_20210101_1308.py new file mode 100644 index 0000000..942f430 --- /dev/null +++ b/uncloud_pay/migrations/0011_auto_20210101_1308.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1 on 2021-01-01 13:08 + +from django.db import migrations +import uncloud.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('uncloud_pay', '0010_auto_20201229_0042'), + ] + + operations = [ + migrations.AlterField( + model_name='billingaddress', + name='country', + field=uncloud.models.CountryField(choices=[('AD', 'Andorra'), ('AE', 'United Arab Emirates'), ('AF', 'Afghanistan'), ('AG', 'Antigua & Barbuda'), ('AI', 'Anguilla'), ('AL', 'Albania'), ('AM', 'Armenia'), ('AN', 'Netherlands Antilles'), ('AO', 'Angola'), ('AQ', 'Antarctica'), ('AR', 'Argentina'), ('AS', 'American Samoa'), ('AT', 'Austria'), ('AU', 'Australia'), ('AW', 'Aruba'), ('AZ', 'Azerbaijan'), ('BA', 'Bosnia and Herzegovina'), ('BB', 'Barbados'), ('BD', 'Bangladesh'), ('BE', 'Belgium'), ('BF', 'Burkina Faso'), ('BG', 'Bulgaria'), ('BH', 'Bahrain'), ('BI', 'Burundi'), ('BJ', 'Benin'), ('BM', 'Bermuda'), ('BN', 'Brunei Darussalam'), ('BO', 'Bolivia'), ('BR', 'Brazil'), ('BS', 'Bahama'), ('BT', 'Bhutan'), ('BV', 'Bouvet Island'), ('BW', 'Botswana'), ('BY', 'Belarus'), ('BZ', 'Belize'), ('CA', 'Canada'), ('CC', 'Cocos (Keeling) Islands'), ('CF', 'Central African Republic'), ('CG', 'Congo'), ('CH', 'Switzerland'), ('CI', 'Ivory Coast'), ('CK', 'Cook Iislands'), ('CL', 'Chile'), ('CM', 'Cameroon'), ('CN', 'China'), ('CO', 'Colombia'), ('CR', 'Costa Rica'), ('CU', 'Cuba'), ('CV', 'Cape Verde'), ('CX', 'Christmas Island'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DE', 'Germany'), ('DJ', 'Djibouti'), ('DK', 'Denmark'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('DZ', 'Algeria'), ('EC', 'Ecuador'), ('EE', 'Estonia'), ('EG', 'Egypt'), ('EH', 'Western Sahara'), ('ER', 'Eritrea'), ('ES', 'Spain'), ('ET', 'Ethiopia'), ('FI', 'Finland'), ('FJ', 'Fiji'), ('FK', 'Falkland Islands (Malvinas)'), ('FM', 'Micronesia'), ('FO', 'Faroe Islands'), ('FR', 'France'), ('FX', 'France, Metropolitan'), ('GA', 'Gabon'), ('GB', 'United Kingdom (Great Britain)'), ('GD', 'Grenada'), ('GE', 'Georgia'), ('GF', 'French Guiana'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GL', 'Greenland'), ('GM', 'Gambia'), ('GN', 'Guinea'), ('GP', 'Guadeloupe'), ('GQ', 'Equatorial Guinea'), ('GR', 'Greece'), ('GS', 'South Georgia and the South Sandwich Islands'), ('GT', 'Guatemala'), ('GU', 'Guam'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HK', 'Hong Kong'), ('HM', 'Heard & McDonald Islands'), ('HN', 'Honduras'), ('HR', 'Croatia'), ('HT', 'Haiti'), ('HU', 'Hungary'), ('ID', 'Indonesia'), ('IE', 'Ireland'), ('IL', 'Israel'), ('IN', 'India'), ('IO', 'British Indian Ocean Territory'), ('IQ', 'Iraq'), ('IR', 'Islamic Republic of Iran'), ('IS', 'Iceland'), ('IT', 'Italy'), ('JM', 'Jamaica'), ('JO', 'Jordan'), ('JP', 'Japan'), ('KE', 'Kenya'), ('KG', 'Kyrgyzstan'), ('KH', 'Cambodia'), ('KI', 'Kiribati'), ('KM', 'Comoros'), ('KN', 'St. Kitts and Nevis'), ('KP', "Korea, Democratic People's Republic of"), ('KR', 'Korea, Republic of'), ('KW', 'Kuwait'), ('KY', 'Cayman Islands'), ('KZ', 'Kazakhstan'), ('LA', "Lao People's Democratic Republic"), ('LB', 'Lebanon'), ('LC', 'Saint Lucia'), ('LI', 'Liechtenstein'), ('LK', 'Sri Lanka'), ('LR', 'Liberia'), ('LS', 'Lesotho'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('LV', 'Latvia'), ('LY', 'Libyan Arab Jamahiriya'), ('MA', 'Morocco'), ('MC', 'Monaco'), ('MD', 'Moldova, Republic of'), ('MG', 'Madagascar'), ('MH', 'Marshall Islands'), ('ML', 'Mali'), ('MN', 'Mongolia'), ('MM', 'Myanmar'), ('MO', 'Macau'), ('MP', 'Northern Mariana Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MS', 'Monserrat'), ('MT', 'Malta'), ('MU', 'Mauritius'), ('MV', 'Maldives'), ('MW', 'Malawi'), ('MX', 'Mexico'), ('MY', 'Malaysia'), ('MZ', 'Mozambique'), ('NA', 'Namibia'), ('NC', 'New Caledonia'), ('NE', 'Niger'), ('NF', 'Norfolk Island'), ('NG', 'Nigeria'), ('NI', 'Nicaragua'), ('NL', 'Netherlands'), ('NO', 'Norway'), ('NP', 'Nepal'), ('NR', 'Nauru'), ('NU', 'Niue'), ('NZ', 'New Zealand'), ('OM', 'Oman'), ('PA', 'Panama'), ('PE', 'Peru'), ('PF', 'French Polynesia'), ('PG', 'Papua New Guinea'), ('PH', 'Philippines'), ('PK', 'Pakistan'), ('PL', 'Poland'), ('PM', 'St. Pierre & Miquelon'), ('PN', 'Pitcairn'), ('PR', 'Puerto Rico'), ('PT', 'Portugal'), ('PW', 'Palau'), ('PY', 'Paraguay'), ('QA', 'Qatar'), ('RE', 'Reunion'), ('RO', 'Romania'), ('RU', 'Russian Federation'), ('RW', 'Rwanda'), ('SA', 'Saudi Arabia'), ('SB', 'Solomon Islands'), ('SC', 'Seychelles'), ('SD', 'Sudan'), ('SE', 'Sweden'), ('SG', 'Singapore'), ('SH', 'St. Helena'), ('SI', 'Slovenia'), ('SJ', 'Svalbard & Jan Mayen Islands'), ('SK', 'Slovakia'), ('SL', 'Sierra Leone'), ('SM', 'San Marino'), ('SN', 'Senegal'), ('SO', 'Somalia'), ('SR', 'Suriname'), ('ST', 'Sao Tome & Principe'), ('SV', 'El Salvador'), ('SY', 'Syrian Arab Republic'), ('SZ', 'Swaziland'), ('TC', 'Turks & Caicos Islands'), ('TD', 'Chad'), ('TF', 'French Southern Territories'), ('TG', 'Togo'), ('TH', 'Thailand'), ('TJ', 'Tajikistan'), ('TK', 'Tokelau'), ('TM', 'Turkmenistan'), ('TN', 'Tunisia'), ('TO', 'Tonga'), ('TP', 'East Timor'), ('TR', 'Turkey'), ('TT', 'Trinidad & Tobago'), ('TV', 'Tuvalu'), ('TW', 'Taiwan, Province of China'), ('TZ', 'Tanzania, United Republic of'), ('UA', 'Ukraine'), ('UG', 'Uganda'), ('UM', 'United States Minor Outlying Islands'), ('US', 'United States of America'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VA', 'Vatican City State (Holy See)'), ('VC', 'St. Vincent & the Grenadines'), ('VE', 'Venezuela'), ('VG', 'British Virgin Islands'), ('VI', 'United States Virgin Islands'), ('VN', 'Viet Nam'), ('VU', 'Vanuatu'), ('WF', 'Wallis & Futuna Islands'), ('WS', 'Samoa'), ('YE', 'Yemen'), ('YT', 'Mayotte'), ('YU', 'Yugoslavia'), ('ZA', 'South Africa'), ('ZM', 'Zambia'), ('ZR', 'Zaire'), ('ZW', 'Zimbabwe')], default='CH', max_length=2), + ), + ] diff --git a/uncloud_pay/models.py b/uncloud_pay/models.py index 4eb6698..adaabef 100644 --- a/uncloud_pay/models.py +++ b/uncloud_pay/models.py @@ -1,24 +1,23 @@ import logging -import itertools import datetime + from math import ceil from calendar import monthrange from decimal import Decimal -from functools import reduce -from django.db import models -from django.db.models import Q -from django.contrib.auth import get_user_model -from django.contrib.contenttypes.fields import GenericForeignKey -from django.contrib.contenttypes.models import ContentType -from django.utils.translation import gettext_lazy as _ -from django.core.validators import MinValueValidator -from django.utils import timezone -from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.conf import settings +from django.contrib.auth import get_user_model +from django.core.validators import MinValueValidator +from django.db import models +from django.utils.translation import gettext_lazy as _ +from django.utils import timezone + +# Verify whether or not to use them here +from django.core.exceptions import ObjectDoesNotExist, ValidationError from uncloud import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS from uncloud.models import UncloudAddress +from .services import * # Used to generate bill due dates. BILL_PAYMENT_DELAY=datetime.timedelta(days=10) @@ -26,36 +25,6 @@ BILL_PAYMENT_DELAY=datetime.timedelta(days=10) # Initialize logger. logger = logging.getLogger(__name__) -def start_of_month(a_day): - """ Returns first of the month of a given datetime object""" - return a_day.replace(day=1,hour=0,minute=0,second=0, microsecond=0) - -def end_of_month(a_day): - """ Returns first of the month of a given datetime object""" - - _, last_day = monthrange(a_day.year, a_day.month) - return a_day.replace(day=last_day,hour=23,minute=59,second=59, microsecond=0) - -def start_of_this_month(): - """ Returns first of this month""" - a_day = timezone.now() - return a_day.replace(day=1,hour=0,minute=0,second=0, microsecond=0) - -def end_of_this_month(): - """ Returns first of this month""" - a_day = timezone.now() - - _, last_day = monthrange(a_day.year, a_day.month) - return a_day.replace(day=last_day,hour=23,minute=59,second=59, microsecond=0) - -def end_before(a_date): - """ Return suitable datetimefield for ending just before a_date """ - return a_date - datetime.timedelta(seconds=1) - -def start_after(a_date): - """ Return suitable datetimefield for starting just after a_date """ - return a_date + datetime.timedelta(seconds=1) - def default_payment_delay(): return timezone.now() + BILL_PAYMENT_DELAY @@ -68,7 +37,6 @@ class Currency(models.TextChoices): # USD = 'USD', _('US Dollar') - ### # Stripe @@ -95,7 +63,7 @@ class StripeCreditCard(models.Model): class Meta: constraints = [ models.UniqueConstraint(fields=['owner'], - condition=Q(active=True), + condition=models.Q(active=True), name='one_active_card_per_user') ] @@ -117,9 +85,7 @@ class Payment(models.Model): ('stripe', 'Stripe'), ('voucher', 'Voucher'), ('referral', 'Referral'), - ('unknown', 'Unknown') - ), - default='unknown') + )) timestamp = models.DateTimeField(default=timezone.now) @@ -135,6 +101,11 @@ class Payment(models.Model): class PaymentMethod(models.Model): + """ + Not sure if this is still in use + + """ + owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, editable=False) @@ -151,15 +122,6 @@ class PaymentMethod(models.Model): stripe_payment_method_id = models.CharField(max_length=32, blank=True, null=True) stripe_setup_intent_id = models.CharField(max_length=32, blank=True, null=True) - # @property - # def stripe_card_last4(self): - # if self.source == 'stripe' and self.active: - # payment_method = uncloud_pay.stripe.get_payment_method( - # self.stripe_payment_method_id) - # return payment_method.card.last4 - # else: - # return None - @property def active(self): if self.source == 'stripe' and self.stripe_payment_method_id != None: @@ -276,7 +238,7 @@ class BillingAddress(UncloudAddress): class Meta: constraints = [ models.UniqueConstraint(fields=['owner'], - condition=Q(active=True), + condition=models.Q(active=True), name='one_active_billing_address_per_user') ] @@ -297,18 +259,13 @@ class BillingAddress(UncloudAddress): if not billing_address: billing_address = cls.objects.create(owner=owner, organization="uncloud admins", - name="Uncloud Admin", + full_name="Uncloud Admin", street="Uncloudstreet. 42", city="Luchsingen", postal_code="8775", country="CH", active=True) - - @staticmethod - def get_address_for(user): - return BillingAddress.objects.get(owner=user, active=True) - def __str__(self): return "{} - {}, {}, {} {}, {}".format( self.owner, @@ -1186,7 +1143,7 @@ class Bill(models.Model): return bill def __str__(self): - return f"Bill {self.owner}-{self.id}" + return f"{self.owner}-{self.id}" class BillRecord(models.Model): @@ -1256,7 +1213,7 @@ class ProductToRecurringPeriod(models.Model): class Meta: constraints = [ models.UniqueConstraint(fields=['product'], - condition=Q(is_default=True), + condition=models.Q(is_default=True), name='one_default_recurring_period_per_product'), models.UniqueConstraint(fields=['product', 'recurring_period'], name='recurring_period_once_per_product') diff --git a/uncloud_pay/selectors.py b/uncloud_pay/selectors.py index ba53c74..5f86657 100644 --- a/uncloud_pay/selectors.py +++ b/uncloud_pay/selectors.py @@ -21,3 +21,6 @@ def get_spendings_for_user(user): @transaction.atomic def get_balance_for_user(user): return get_payments_for_user(user) - get_spendings_for_user(user) + +def get_billing_address_for_user(user): + return BillingAddress.objects.get(owner=user, active=True) diff --git a/uncloud_pay/serializers.py b/uncloud_pay/serializers.py index 00e969b..3906482 100644 --- a/uncloud_pay/serializers.py +++ b/uncloud_pay/serializers.py @@ -36,6 +36,11 @@ class PaymentSerializer(serializers.ModelSerializer): class BalanceSerializer(serializers.Serializer): balance = serializers.DecimalField(max_digits=AMOUNT_MAX_DIGITS, decimal_places=AMOUNT_DECIMALS) +class BillingAddressSerializer(serializers.ModelSerializer): + class Meta: + model = BillingAddress + exclude = [ "owner" ] + ################################################################################ # Unchecked code @@ -96,11 +101,6 @@ class BillRecordSerializer(serializers.Serializer): amount = serializers.DecimalField(AMOUNT_MAX_DIGITS, AMOUNT_DECIMALS) total = serializers.DecimalField(AMOUNT_MAX_DIGITS, AMOUNT_DECIMALS) -class BillingAddressSerializer(serializers.ModelSerializer): - class Meta: - model = BillingAddress - fields = ['uuid', 'organization', 'name', 'street', 'city', 'postal_code', 'country', 'vat_number'] - class BillSerializer(serializers.ModelSerializer): billing_address = BillingAddressSerializer(read_only=True) records = BillRecordSerializer(many=True, read_only=True) diff --git a/uncloud_pay/services.py b/uncloud_pay/services.py new file mode 100644 index 0000000..ed97c39 --- /dev/null +++ b/uncloud_pay/services.py @@ -0,0 +1,32 @@ +from django.utils import timezone + + +def start_of_month(a_day): + """ Returns first of the month of a given datetime object""" + return a_day.replace(day=1,hour=0,minute=0,second=0, microsecond=0) + +def end_of_month(a_day): + """ Returns first of the month of a given datetime object""" + + _, last_day = monthrange(a_day.year, a_day.month) + return a_day.replace(day=last_day,hour=23,minute=59,second=59, microsecond=0) + +def start_of_this_month(): + """ Returns first of this month""" + a_day = timezone.now() + return a_day.replace(day=1,hour=0,minute=0,second=0, microsecond=0) + +def end_of_this_month(): + """ Returns first of this month""" + a_day = timezone.now() + + _, last_day = monthrange(a_day.year, a_day.month) + return a_day.replace(day=last_day,hour=23,minute=59,second=59, microsecond=0) + +def end_before(a_date): + """ Return suitable datetimefield for ending just before a_date """ + return a_date - datetime.timedelta(seconds=1) + +def start_after(a_date): + """ Return suitable datetimefield for starting just after a_date """ + return a_date + datetime.timedelta(seconds=1) diff --git a/uncloud_pay/templates/uncloud_pay/bill.html.j2 b/uncloud_pay/templates/uncloud_pay/bill.html.j2 index c227f43..7cf10f8 100644 --- a/uncloud_pay/templates/uncloud_pay/bill.html.j2 +++ b/uncloud_pay/templates/uncloud_pay/bill.html.j2 @@ -680,11 +680,9 @@ oAsAAAAAAACGQNAFAAAAAAAAQyDoAgAAAAAAgCEQdAEAAAAAAMAQCLoAAAAAAABgCP83AL6WQ1Y7
    - {{ bill.starting_date|date:"c" }} - - {{ bill.ending_date|date:"c" }} -
    Bill id: {{ bill }} -
    Due: {{ bill.due_date }} - + Bill id: {{ bill }} +
    {{ bill.starting_date|date:"Ymd" }} - + {{ bill.ending_date|date:"Ymd" }}
    @@ -703,8 +701,8 @@ oAsAAAAAAACGQNAFAAAAAAAAQyDoAgAAAAAAgCEQdAEAAAAAAMAQCLoAAAAAAABgCP83AL6WQ1Y7 {% for record in bill_records %} - {{ record.starting_date|date:"c" }} - - {{ record.ending_date|date:"c" }} + {{ record.starting_date|date:"Ymd-H:i:s" }} + - {{ record.ending_date|date:"Ymd-H:i:s" }} {{ record.order }} {{ record.price|floatformat:2 }}