From fe0bc14ac10185945799f45b258ed6ec2c078a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Tue, 5 Jul 2016 18:46:32 +0200 Subject: [PATCH 01/26] Adding content to . Fix #62 --- bornhack/templates/base.html | 2 +- bornhack/templates/coc.html | 4 ++++ bornhack/templates/contact.html | 4 ++++ bornhack/templates/info.html | 4 ++++ bornhack/templates/legal/general_terms_and_conditions.html | 4 ++++ bornhack/templates/legal/privacy_policy.html | 4 ++++ bornhack/templates/speakers.html | 4 ++++ bornhack/templates/sponsors.html | 4 ++++ news/templates/news_detail.html | 4 ++++ news/templates/news_index.html | 4 ++++ shop/templates/product_detail.html | 4 ++++ shop/templates/shop_index.html | 4 ++++ 12 files changed, 45 insertions(+), 1 deletion(-) diff --git a/bornhack/templates/base.html b/bornhack/templates/base.html index 0a8a2b17..97fcefb1 100644 --- a/bornhack/templates/base.html +++ b/bornhack/templates/base.html @@ -7,7 +7,7 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> - <title>Bornhack + {% block title %}BornHack{% endblock %} diff --git a/bornhack/templates/coc.html b/bornhack/templates/coc.html index f735454b..1dccf57d 100644 --- a/bornhack/templates/coc.html +++ b/bornhack/templates/coc.html @@ -1,5 +1,9 @@ {% extends 'base.html' %} +{% block title %} +Code of Conduct | {{ block.super }} +{% endblock %} + {% block content %}

Code of Conduct

diff --git a/bornhack/templates/contact.html b/bornhack/templates/contact.html index 0b1e2e31..e9f5dafb 100644 --- a/bornhack/templates/contact.html +++ b/bornhack/templates/contact.html @@ -1,5 +1,9 @@ {% extends 'base.html' %} +{% block title %} +Contact | {{ block.super }} +{% endblock %} + {% block content %}

diff --git a/bornhack/templates/info.html b/bornhack/templates/info.html index 8582bfb0..2a6a9278 100644 --- a/bornhack/templates/info.html +++ b/bornhack/templates/info.html @@ -1,6 +1,10 @@ {% extends 'base.html' %} {% load static from staticfiles %} +{% block title %} +Info | {{ block.super }} +{% endblock %} + {% block extra_head %} {% endblock %} diff --git a/bornhack/templates/legal/general_terms_and_conditions.html b/bornhack/templates/legal/general_terms_and_conditions.html index 6a66eb37..0cbf8360 100644 --- a/bornhack/templates/legal/general_terms_and_conditions.html +++ b/bornhack/templates/legal/general_terms_and_conditions.html @@ -1,5 +1,9 @@ {% extends 'base.html' %} +{% block title %} +General Terms and Conditions | {{ block.super }} +{% endblock %} + {% block content %}

Terms and Conditions for BornHack IvS’ sale and delivery of (i) merchandise (ii) tickets and/or (iii) accommodation

diff --git a/bornhack/templates/legal/privacy_policy.html b/bornhack/templates/legal/privacy_policy.html index 579c48c8..10762aba 100644 --- a/bornhack/templates/legal/privacy_policy.html +++ b/bornhack/templates/legal/privacy_policy.html @@ -1,5 +1,9 @@ {% extends 'base.html' %} +{% block title %} +Privacy Policy | {{ block.super }} +{% endblock %} + {% block content %}

Privacy and cookie policy

diff --git a/bornhack/templates/speakers.html b/bornhack/templates/speakers.html index ce9f8d3f..74f17b2e 100644 --- a/bornhack/templates/speakers.html +++ b/bornhack/templates/speakers.html @@ -1,5 +1,9 @@ {% extends 'base.html' %} +{% block title %} +Call for Speakers | {{ block.super }} +{% endblock %} + {% block content %}

BornHack 2016: Call for Speakers

diff --git a/bornhack/templates/sponsors.html b/bornhack/templates/sponsors.html index e7271123..91f270ed 100644 --- a/bornhack/templates/sponsors.html +++ b/bornhack/templates/sponsors.html @@ -1,5 +1,9 @@ {% extends 'base.html' %} +{% block title %} +Call for Sponsors | {{ block.super }} +{% endblock %} + {% block content %}

Sponsoring

diff --git a/news/templates/news_detail.html b/news/templates/news_detail.html index 15eced7e..8cc0c953 100644 --- a/news/templates/news_detail.html +++ b/news/templates/news_detail.html @@ -1,6 +1,10 @@ {% extends 'base.html' %} {% load commonmark %} +{% block title %} +{{ news_item.title }} | {{ block.super }} +{% endblock %} + {% block content %}
{% if not_public %} diff --git a/news/templates/news_index.html b/news/templates/news_index.html index 18469c3c..30ee0995 100644 --- a/news/templates/news_index.html +++ b/news/templates/news_index.html @@ -1,6 +1,10 @@ {% extends 'base.html' %} {% load commonmark %} +{% block title %} +News | {{ block.super }} +{% endblock %} + {% block content %} {% for item in news_items %}
diff --git a/shop/templates/product_detail.html b/shop/templates/product_detail.html index 8841123e..a5408361 100644 --- a/shop/templates/product_detail.html +++ b/shop/templates/product_detail.html @@ -3,6 +3,10 @@ {% load commonmark %} {% load shop_tags %} +{% block title %} +{{ product.name }} | {{ block.super }} +{% endblock %} + {% block shop_content %}
diff --git a/shop/templates/shop_index.html b/shop/templates/shop_index.html index 45df7340..fc0c8ad7 100644 --- a/shop/templates/shop_index.html +++ b/shop/templates/shop_index.html @@ -2,6 +2,10 @@ {% load bootstrap3 %} {% load shop_tags %} +{% block title %} +Shop | {{ block.super }} +{% endblock %} + {% block shop_content %}
From 5fe157a8686c853952f28ab9d08161285eb0f9ef Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Tue, 5 Jul 2016 21:43:09 +0200 Subject: [PATCH 02/26] change "paid" field to readonly in django admin --- shop/admin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/shop/admin.py b/shop/admin.py index f9f20fef..0aa0c51f 100644 --- a/shop/admin.py +++ b/shop/admin.py @@ -41,6 +41,7 @@ class TicketInline(admin.TabularInline): @admin.register(models.Order) class OrderAdmin(admin.ModelAdmin): change_form_template = 'admin/change_order_form.html' + readonly_fields = ('paid',) def get_email(self, obj): return obj.user.email From 8786b5af88dcab2d891cc2a51dfaf1131c9a1c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Tue, 5 Jul 2016 23:43:18 +0200 Subject: [PATCH 03/26] Initial villages feature --- bornhack/settings/base.py | 1 + bornhack/templates/base.html | 13 +----- bornhack/urls.py | 4 ++ shop/templates/shop_base.html | 16 ++++++- villages/__init__.py | 0 villages/admin.py | 8 ++++ villages/apps.py | 7 ++++ villages/migrations/0001_initial.py | 38 +++++++++++++++++ villages/migrations/__init__.py | 0 villages/models.py | 58 ++++++++++++++++++++++++++ villages/templates/village_detail.html | 16 +++++++ villages/templates/village_form.html | 16 +++++++ villages/templates/village_list.html | 38 +++++++++++++++++ villages/tests.py | 3 ++ villages/urls.py | 9 ++++ villages/views.py | 40 ++++++++++++++++++ 16 files changed, 254 insertions(+), 13 deletions(-) create mode 100644 villages/__init__.py create mode 100644 villages/admin.py create mode 100644 villages/apps.py create mode 100644 villages/migrations/0001_initial.py create mode 100644 villages/migrations/__init__.py create mode 100644 villages/models.py create mode 100644 villages/templates/village_detail.html create mode 100644 villages/templates/village_form.html create mode 100644 villages/templates/village_list.html create mode 100644 villages/tests.py create mode 100644 villages/urls.py create mode 100644 villages/views.py diff --git a/bornhack/settings/base.py b/bornhack/settings/base.py index 4b7c2934..269257f9 100644 --- a/bornhack/settings/base.py +++ b/bornhack/settings/base.py @@ -29,6 +29,7 @@ INSTALLED_APPS = [ 'shop', 'news', 'utils', + 'villages', 'allauth', 'allauth.account', diff --git a/bornhack/templates/base.html b/bornhack/templates/base.html index 97fcefb1..cbc2ad57 100644 --- a/bornhack/templates/base.html +++ b/bornhack/templates/base.html @@ -42,19 +42,8 @@ {% if current_camp.shop_open %}
  • Shop
  • - - {% if current_order and current_order.get_number_of_items %} -
  • - - - - {{ current_order.get_number_of_items }} - - -
  • - {% endif %} - {% endif %} +
  • Villages
  • Speakers
  • Sponsors
  • Contact
  • diff --git a/bornhack/urls.py b/bornhack/urls.py index d291f52a..5cdd323b 100644 --- a/bornhack/urls.py +++ b/bornhack/urls.py @@ -74,6 +74,10 @@ urlpatterns = [ r'^news/', include('news.urls', namespace='news') ), + url( + r'^villages/', + include('villages.urls', namespace='villages') + ), url(r'^accounts/', include('allauth.urls')), url(r'^admin/', include(admin.site.urls)), ] diff --git a/shop/templates/shop_base.html b/shop/templates/shop_base.html index 71cf3a6e..9f213393 100644 --- a/shop/templates/shop_base.html +++ b/shop/templates/shop_base.html @@ -19,7 +19,21 @@ {% if has_tickets %}
  • Tickets
  • {% endif %} -
  • Orders
  • +
  • Orders
  • +
  • + + {% if current_order and current_order.get_number_of_items %} + + {% endif %} + + + + {{ current_order.get_number_of_items|default:0 }} + + {% if current_order and current_order.get_number_of_items %} + + {% endif %} +
  • {% endif %}
    diff --git a/villages/__init__.py b/villages/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/villages/admin.py b/villages/admin.py new file mode 100644 index 00000000..c276e488 --- /dev/null +++ b/villages/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin + +from .models import Village + + +@admin.register(Village) +class VillageAdmin(admin.ModelAdmin): + pass diff --git a/villages/apps.py b/villages/apps.py new file mode 100644 index 00000000..fbf4978a --- /dev/null +++ b/villages/apps.py @@ -0,0 +1,7 @@ +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class VillagesConfig(AppConfig): + name = 'villages' diff --git a/villages/migrations/0001_initial.py b/villages/migrations/0001_initial.py new file mode 100644 index 00000000..47c87f10 --- /dev/null +++ b/villages/migrations/0001_initial.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.6 on 2016-07-05 21:38 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('camps', '0005_auto_20160510_2011'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Village', + fields=[ + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('name', models.CharField(max_length=255)), + ('slug', models.SlugField(blank=True, max_length=255)), + ('description', models.TextField()), + ('open', models.BooleanField(default=False, help_text='Is this village open for others to join?')), + ('camp', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camps.Camp')), + ('contact', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['name'], + }, + ), + ] diff --git a/villages/migrations/__init__.py b/villages/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/villages/models.py b/villages/models.py new file mode 100644 index 00000000..d57c9eb7 --- /dev/null +++ b/villages/models.py @@ -0,0 +1,58 @@ +from __future__ import unicode_literals + +from django.core.urlresolvers import reverse_lazy +from django.db import models +from django.utils.text import slugify + +from camps.models import Camp +from utils.models import CreatedUpdatedModel, UUIDModel + + +class Village(CreatedUpdatedModel, UUIDModel): + + class Meta: + ordering = ['name'] + + camp = models.ForeignKey('camps.Camp') + contact = models.ForeignKey('auth.User') + + name = models.CharField(max_length=255) + slug = models.SlugField(max_length=255, blank=True) + description = models.TextField() + + open = models.BooleanField( + default=False, + help_text='Is this village open for others to join?' + ) + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse_lazy('villages:detail', kwargs={'slug': self.slug}) + + def save(self, **kwargs): + if ( + not self.pk or + not self.slug or + Village.objects.filter(slug=self.slug).count() > 1 + ): + slug = slugify(self.name) + incrementer = 1 + + # We have to make sure that the slug won't clash with current slugs + while Village.objects.filter(slug=slug).exists(): + if incrementer == 1: + slug = '{}-1'.format(slug) + else: + slug = '{}-{}'.format( + '-'.join(slug.split('-')[:-1]), + incrementer + ) + incrementer += 1 + self.slug = slug + + if not hasattr(self, 'camp'): + self.camp = Camp.objects.current() + + super(Village, self).save(**kwargs) diff --git a/villages/templates/village_detail.html b/villages/templates/village_detail.html new file mode 100644 index 00000000..3491060b --- /dev/null +++ b/villages/templates/village_detail.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} +{% load commonmark %} + +{% block content %} + +

    {{ village.name }}

    + +{{ village.description|commonmark }} + +
    + +{% if user in village.users.all %} +Edit +{% endif %} + +{% endblock %} diff --git a/villages/templates/village_form.html b/villages/templates/village_form.html new file mode 100644 index 00000000..3b5f8534 --- /dev/null +++ b/villages/templates/village_form.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} +{% load bootstrap3 %} + +{% block content %} + +
    + {% csrf_token %} + + {% bootstrap_form form %} + +
    + + +
    + +{% endblock %} \ No newline at end of file diff --git a/villages/templates/village_list.html b/villages/templates/village_list.html new file mode 100644 index 00000000..c89cbeb0 --- /dev/null +++ b/villages/templates/village_list.html @@ -0,0 +1,38 @@ +{% extends 'base.html' %} + +{% block content %} + +

    + If this is your first hackercamp the term 'Village' might be confusing but it is fairly simple: a village is just a spot on the campsite where you and a bunch of your friends/likeminded people camp together. Apart from peoples individual tents which they sleep in, many villages bring a large common tent where you can hack and hang out during the day. +

    + +{% if user.is_authenticated %} +Create a village +{% endif %} + +
    + + + + + + + + + + {% for village in villages %} + + + + + {% endfor %} + +
    NamePrice
    + + {{ village.name }} + + + {{ village.description|truncatewords:15 }} +
    + +{% endblock %} \ No newline at end of file diff --git a/villages/tests.py b/villages/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/villages/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/villages/urls.py b/villages/urls.py new file mode 100644 index 00000000..edc57aa5 --- /dev/null +++ b/villages/urls.py @@ -0,0 +1,9 @@ +from django.conf.urls import url +from views import * + +urlpatterns = [ + url(r'^$', VillageListView.as_view(), name='list'), + url(r'create/$', VillageCreateView.as_view(), name='create'), + url(r'(?P[-_\w+]+)/edit/$', VillageUpdateView.as_view(), name='update'), + url(r'(?P[-_\w+]+)/$', VillageDetailView.as_view(), name='detail'), +] diff --git a/villages/views.py b/villages/views.py new file mode 100644 index 00000000..5d4e589f --- /dev/null +++ b/villages/views.py @@ -0,0 +1,40 @@ +from django.core.urlresolvers import reverse_lazy +from django.http import HttpResponseRedirect +from django.views.generic import ListView, DetailView, CreateView, UpdateView +from .models import ( + Village, +) + + +class VillageListView(ListView): + model = Village + template_name = 'village_list.html' + context_object_name = 'villages' + + +class VillageDetailView(DetailView): + model = Village + template_name = 'village_detail.html' + context_object_name = 'village' + + +class VillageCreateView(CreateView): + model = Village + template_name = 'village_form.html' + fields = ['name', 'description', 'open'] + success_url = reverse_lazy('villages:list') + + def form_valid(self, form): + village = form.save(commit=False) + village.contact = self.request.user + village.save() + return HttpResponseRedirect(village.get_absolute_url()) + + +class VillageUpdateView(UpdateView): + model = Village + template_name = 'village_form.html' + fields = ['name', 'description', 'open'] + + def get_success_url(self): + return self.get_object().get_absolute_url() From 51c7eac4d7d98be2f7c8461aca53094177c156fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Tue, 5 Jul 2016 23:47:08 +0200 Subject: [PATCH 04/26] Use the right query --- villages/templates/village_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/villages/templates/village_detail.html b/villages/templates/village_detail.html index 3491060b..c65e40bb 100644 --- a/villages/templates/village_detail.html +++ b/villages/templates/village_detail.html @@ -9,7 +9,7 @@
    -{% if user in village.users.all %} +{% if user == village.contact %} Edit {% endif %} From 94e6ccc1012303d15e02cc85b7f320d70ab5cd11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Tue, 5 Jul 2016 23:48:04 +0200 Subject: [PATCH 05/26] Too much copy pasting going on :P --- villages/templates/village_list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/villages/templates/village_list.html b/villages/templates/village_list.html index c89cbeb0..42542fb1 100644 --- a/villages/templates/village_list.html +++ b/villages/templates/village_list.html @@ -16,7 +16,7 @@ Name - Price + Description From fdd6433af595e584b9c5d556eebd7d898629e169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Tue, 5 Jul 2016 23:55:05 +0200 Subject: [PATCH 06/26] Call "open" "private" instead. --- .../migrations/0002_auto_20160705_2154.py | 24 +++++++++++++++++++ villages/models.py | 6 ++--- 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 villages/migrations/0002_auto_20160705_2154.py diff --git a/villages/migrations/0002_auto_20160705_2154.py b/villages/migrations/0002_auto_20160705_2154.py new file mode 100644 index 00000000..020819be --- /dev/null +++ b/villages/migrations/0002_auto_20160705_2154.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.6 on 2016-07-05 21:54 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('villages', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='village', + name='open', + ), + migrations.AddField( + model_name='village', + name='private', + field=models.BooleanField(default=True, help_text='Check if your village is privately organized'), + ), + ] diff --git a/villages/models.py b/villages/models.py index d57c9eb7..ed3e1db4 100644 --- a/villages/models.py +++ b/villages/models.py @@ -20,9 +20,9 @@ class Village(CreatedUpdatedModel, UUIDModel): slug = models.SlugField(max_length=255, blank=True) description = models.TextField() - open = models.BooleanField( - default=False, - help_text='Is this village open for others to join?' + private = models.BooleanField( + default=True, + help_text='Check if your village is privately organized' ) def __str__(self): From d4acb0d434930ea5471874a85bfa4a0ea14dbbf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Tue, 5 Jul 2016 23:57:08 +0200 Subject: [PATCH 07/26] Use private instead of open. --- villages/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/villages/views.py b/villages/views.py index 5d4e589f..a6c3069a 100644 --- a/villages/views.py +++ b/villages/views.py @@ -21,7 +21,7 @@ class VillageDetailView(DetailView): class VillageCreateView(CreateView): model = Village template_name = 'village_form.html' - fields = ['name', 'description', 'open'] + fields = ['name', 'description', 'private'] success_url = reverse_lazy('villages:list') def form_valid(self, form): @@ -34,7 +34,7 @@ class VillageCreateView(CreateView): class VillageUpdateView(UpdateView): model = Village template_name = 'village_form.html' - fields = ['name', 'description', 'open'] + fields = ['name', 'description', 'private'] def get_success_url(self): return self.get_object().get_absolute_url() From cbb48a85532f85dec2a4d27a4de3512e24263d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Tue, 5 Jul 2016 23:58:46 +0200 Subject: [PATCH 08/26] Default should be false in private. --- villages/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/villages/models.py b/villages/models.py index ed3e1db4..aec882b9 100644 --- a/villages/models.py +++ b/villages/models.py @@ -21,7 +21,7 @@ class Village(CreatedUpdatedModel, UUIDModel): description = models.TextField() private = models.BooleanField( - default=True, + default=False, help_text='Check if your village is privately organized' ) From 406057f172fef185b59a1cdeecad83d924a196a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vi=CC=81=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Tue, 5 Jul 2016 23:59:34 +0200 Subject: [PATCH 09/26] Adding meaningless migration. --- .../migrations/0003_auto_20160705_2159.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 villages/migrations/0003_auto_20160705_2159.py diff --git a/villages/migrations/0003_auto_20160705_2159.py b/villages/migrations/0003_auto_20160705_2159.py new file mode 100644 index 00000000..af9a136c --- /dev/null +++ b/villages/migrations/0003_auto_20160705_2159.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.6 on 2016-07-05 21:59 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('villages', '0002_auto_20160705_2154'), + ] + + operations = [ + migrations.AlterField( + model_name='village', + name='private', + field=models.BooleanField(default=False, help_text='Check if your village is privately organized'), + ), + ] From 2fc0205710f98f943b0be3793d3e9288515ffe32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Sun, 10 Jul 2016 19:19:41 +0200 Subject: [PATCH 10/26] Adding deletion possibility and some other stuff to villages --- villages/managers.py | 9 +++++++++ villages/migrations/0004_village_deleted.py | 20 +++++++++++++++++++ villages/models.py | 16 ++++++++++++++- .../templates/village_confirm_delete.html | 13 ++++++++++++ villages/templates/village_detail.html | 3 ++- villages/templates/village_list.html | 16 ++++++++++++++- villages/urls.py | 1 + villages/views.py | 16 ++++++++++++--- 8 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 villages/managers.py create mode 100644 villages/migrations/0004_village_deleted.py create mode 100644 villages/templates/village_confirm_delete.html diff --git a/villages/managers.py b/villages/managers.py new file mode 100644 index 00000000..9966196d --- /dev/null +++ b/villages/managers.py @@ -0,0 +1,9 @@ +from django.db.models import QuerySet + + +class VillageQuerySet(QuerySet): + + def not_deleted(self): + return self.filter( + deleted=False + ) diff --git a/villages/migrations/0004_village_deleted.py b/villages/migrations/0004_village_deleted.py new file mode 100644 index 00000000..3c1e5b3a --- /dev/null +++ b/villages/migrations/0004_village_deleted.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.6 on 2016-07-10 16:58 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('villages', '0003_auto_20160705_2159'), + ] + + operations = [ + migrations.AddField( + model_name='village', + name='deleted', + field=models.BooleanField(default=False), + ), + ] diff --git a/villages/models.py b/villages/models.py index aec882b9..5104b915 100644 --- a/villages/models.py +++ b/villages/models.py @@ -7,6 +7,8 @@ from django.utils.text import slugify from camps.models import Camp from utils.models import CreatedUpdatedModel, UUIDModel +from .managers import VillageQuerySet + class Village(CreatedUpdatedModel, UUIDModel): @@ -18,13 +20,21 @@ class Village(CreatedUpdatedModel, UUIDModel): name = models.CharField(max_length=255) slug = models.SlugField(max_length=255, blank=True) - description = models.TextField() + description = models.TextField( + help_text="A descriptive text about your village. Markdown is supported." + ) private = models.BooleanField( default=False, help_text='Check if your village is privately organized' ) + deleted = models.BooleanField( + default=False, + ) + + objects = VillageQuerySet.as_manager() + def __str__(self): return self.name @@ -56,3 +66,7 @@ class Village(CreatedUpdatedModel, UUIDModel): self.camp = Camp.objects.current() super(Village, self).save(**kwargs) + + def delete(self, using=None, keep_parents=False): + self.deleted = True + self.save() diff --git a/villages/templates/village_confirm_delete.html b/villages/templates/village_confirm_delete.html new file mode 100644 index 00000000..4f28b3b2 --- /dev/null +++ b/villages/templates/village_confirm_delete.html @@ -0,0 +1,13 @@ +{% extends 'base.html' %} +{% load commonmark %} + +{% block content %} + +
    {% csrf_token %} +

    Are you sure you want to delete the village "{{ village }}"?

    + +
    +
    + Cancel +
    +{% endblock %} diff --git a/villages/templates/village_detail.html b/villages/templates/village_detail.html index c65e40bb..c5aebff5 100644 --- a/villages/templates/village_detail.html +++ b/villages/templates/village_detail.html @@ -10,7 +10,8 @@
    {% if user == village.contact %} -Edit +Edit | +Delete {% endif %} {% endblock %} diff --git a/villages/templates/village_list.html b/villages/templates/village_list.html index 42542fb1..75a3015e 100644 --- a/villages/templates/village_list.html +++ b/villages/templates/village_list.html @@ -3,7 +3,17 @@ {% block content %}

    - If this is your first hackercamp the term 'Village' might be confusing but it is fairly simple: a village is just a spot on the campsite where you and a bunch of your friends/likeminded people camp together. Apart from peoples individual tents which they sleep in, many villages bring a large common tent where you can hack and hang out during the day. + If this is your first hackercamp the term 'Village' might be confusing but it + is fairly simple: a village is just a spot on the campsite where you and a + bunch of your friends/likeminded people camp together. Apart from peoples + individual tents which they sleep in, many villages bring a large common tent + where you can hack and hang out during the day. +

    + +

    + + It is also possible to rent a tent, chairs and tables for villages here. +

    {% if user.is_authenticated %} @@ -17,6 +27,7 @@ Name Description + Public @@ -30,6 +41,9 @@ {{ village.description|truncatewords:15 }} + + + {% endfor %} diff --git a/villages/urls.py b/villages/urls.py index edc57aa5..7a5d473f 100644 --- a/villages/urls.py +++ b/villages/urls.py @@ -4,6 +4,7 @@ from views import * urlpatterns = [ url(r'^$', VillageListView.as_view(), name='list'), url(r'create/$', VillageCreateView.as_view(), name='create'), + url(r'(?P[-_\w+]+)/delete/$', VillageDeleteView.as_view(), name='delete'), url(r'(?P[-_\w+]+)/edit/$', VillageUpdateView.as_view(), name='update'), url(r'(?P[-_\w+]+)/$', VillageDetailView.as_view(), name='detail'), ] diff --git a/villages/views.py b/villages/views.py index a6c3069a..2bde344a 100644 --- a/villages/views.py +++ b/villages/views.py @@ -1,19 +1,21 @@ from django.core.urlresolvers import reverse_lazy from django.http import HttpResponseRedirect -from django.views.generic import ListView, DetailView, CreateView, UpdateView +from django.views.generic import ( + ListView, DetailView, CreateView, UpdateView, DeleteView +) from .models import ( Village, ) class VillageListView(ListView): - model = Village + queryset = Village.objects.not_deleted() template_name = 'village_list.html' context_object_name = 'villages' class VillageDetailView(DetailView): - model = Village + queryset = Village.objects.not_deleted() template_name = 'village_detail.html' context_object_name = 'village' @@ -33,8 +35,16 @@ class VillageCreateView(CreateView): class VillageUpdateView(UpdateView): model = Village + queryset = Village.objects.not_deleted() template_name = 'village_form.html' fields = ['name', 'description', 'private'] def get_success_url(self): return self.get_object().get_absolute_url() + + +class VillageDeleteView(DeleteView): + model = Village + success_url = reverse_lazy('villages:list') + template_name = 'village_confirm_delete.html' + context_object_name = 'village' From f7e2cf54b6d4bb06821092f64cdfe59ddf675eab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vi=CC=81=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Sun, 10 Jul 2016 19:44:42 +0200 Subject: [PATCH 11/26] Adding private and deleted to list_display on villages --- villages/admin.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/villages/admin.py b/villages/admin.py index c276e488..e752aaea 100644 --- a/villages/admin.py +++ b/villages/admin.py @@ -5,4 +5,13 @@ from .models import Village @admin.register(Village) class VillageAdmin(admin.ModelAdmin): - pass + list_display = [ + 'name', + 'private', + 'deleted', + ] + + list_filter = [ + 'private', + 'deleted', + ] From 25570632b2901d88a4a8a046d68e5e8c88d5830c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Sun, 10 Jul 2016 20:06:08 +0200 Subject: [PATCH 12/26] Updating info about villages. Styling buttons in the village CRUD. --- bornhack/templates/info.html | 22 +++++++++++-------- .../templates/village_confirm_delete.html | 4 ++-- villages/templates/village_detail.html | 4 ++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/bornhack/templates/info.html b/bornhack/templates/info.html index 2a6a9278..a50a5108 100644 --- a/bornhack/templates/info.html +++ b/bornhack/templates/info.html @@ -205,16 +205,21 @@ Info | {{ block.super }} confusing but it is fairly simple: a village is just a spot on the campsite where you and a bunch of your friends/likeminded people camp together. Apart from peoples individual tents which they sleep in, many - villages bring a large common tent where you can hack and hang out during the day. + villages bring a large common tent where you can hack and hang out + during the day.

    -

    Villages can also rent village tents. The details are not in place yet, - but stay tuned for the announcement. The idea is that you order and pay - for a large tent which will then be setup and ready when you arrive. The - tents have wooden floors and you can rent folding tables and chairs as needed. +

    Villages can also rent village tents via us, head over to the + + villages section of the shop. + The tents will be ready for when you arrive and will be teared down + again saturday the 3rd of September at 12:00. The tents have optional + floors and you can rent folding tables and chairs as needed. +

    +

    You can register your village registration here! + If you don't want to make your own village you will possibly be able to + find and join one that suits your interests. + Get in touch if you have any questions!

    -

    Village registration is not finished yet, but stay tuned! - If you don't want to make your own village you will likely - be able to find and join one that suits your interests.

    @@ -235,7 +240,6 @@ Info | {{ block.super }}
    Kiosk
    We sell a few beverages and sweets as well as practical things like coal for the barbeque. We will accept the same payment methods at the venue as we do on the website: cash, cards, and blockchain.
    -

    diff --git a/villages/templates/village_confirm_delete.html b/villages/templates/village_confirm_delete.html index 4f28b3b2..d4fd816c 100644 --- a/villages/templates/village_confirm_delete.html +++ b/villages/templates/village_confirm_delete.html @@ -5,9 +5,9 @@
    {% csrf_token %}

    Are you sure you want to delete the village "{{ village }}"?

    - +

    - Cancel + Cancel
    {% endblock %} diff --git a/villages/templates/village_detail.html b/villages/templates/village_detail.html index c5aebff5..aa2134a8 100644 --- a/villages/templates/village_detail.html +++ b/villages/templates/village_detail.html @@ -10,8 +10,8 @@
    {% if user == village.contact %} -Edit | -Delete +Edit +Delete {% endif %} {% endblock %} From bd089d03d3c80e08cb5eee8c601bf12df51e2ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Sun, 10 Jul 2016 20:14:00 +0200 Subject: [PATCH 13/26] Add note about not having to have a village or a concept to rent a tent --- bornhack/templates/info.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bornhack/templates/info.html b/bornhack/templates/info.html index a50a5108..8fb93f0b 100644 --- a/bornhack/templates/info.html +++ b/bornhack/templates/info.html @@ -213,7 +213,8 @@ Info | {{ block.super }} villages section of the shop. The tents will be ready for when you arrive and will be teared down again saturday the 3rd of September at 12:00. The tents have optional - floors and you can rent folding tables and chairs as needed. + floors and you can rent folding tables and chairs as needed. You do not + have to register a village or have a concept to rent a big tent.

    You can register your village registration here! If you don't want to make your own village you will possibly be able to From e5d330fa011b6f90825d1c3bd51509aa834422ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Sun, 10 Jul 2016 23:14:57 +0200 Subject: [PATCH 14/26] Move hr into the if block --- villages/templates/village_detail.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/villages/templates/village_detail.html b/villages/templates/village_detail.html index aa2134a8..7ab7a9ed 100644 --- a/villages/templates/village_detail.html +++ b/villages/templates/village_detail.html @@ -7,9 +7,8 @@ {{ village.description|commonmark }} -


    - {% if user == village.contact %} +
    Edit Delete {% endif %} From 2bd49b3539ea9800e15794b9bfef4953cea5a36d Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Mon, 11 Jul 2016 14:04:18 +0200 Subject: [PATCH 15/26] show 50 words instead of 15 --- villages/templates/village_list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/villages/templates/village_list.html b/villages/templates/village_list.html index 75a3015e..45c9e7ff 100644 --- a/villages/templates/village_list.html +++ b/villages/templates/village_list.html @@ -39,7 +39,7 @@ - {{ village.description|truncatewords:15 }} + {{ village.description|truncatewords:50 }} From 5e151da366b6ec9e309400e4f7a9b6a7d17d1861 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Mon, 11 Jul 2016 14:36:21 +0200 Subject: [PATCH 16/26] show nice title for village list and detail --- villages/templates/village_detail.html | 4 ++++ villages/templates/village_list.html | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/villages/templates/village_detail.html b/villages/templates/village_detail.html index 7ab7a9ed..79925536 100644 --- a/villages/templates/village_detail.html +++ b/villages/templates/village_detail.html @@ -1,6 +1,10 @@ {% extends 'base.html' %} {% load commonmark %} +{% block title %} +Village: {{ village.name }} | {{ block.super }} +{% endblock %} + {% block content %}

    {{ village.name }}

    diff --git a/villages/templates/village_list.html b/villages/templates/village_list.html index 45c9e7ff..baf8103d 100644 --- a/villages/templates/village_list.html +++ b/villages/templates/village_list.html @@ -1,5 +1,9 @@ {% extends 'base.html' %} +{% block title %} +Villages | {{ block.super }} +{% endblock %} + {% block content %}

    From 3ce6b70e99056633a219b19466b9ccd2f2a88b7b Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Tue, 12 Jul 2016 22:33:53 +0200 Subject: [PATCH 17/26] commit initial customorder support --- shop/management/commands/invoice-worker.py | 22 +++++-- shop/models.py | 68 +++++++++++++++++----- shop/templates/pdf/custominvoice.html | 57 ++++++++++++++++++ 3 files changed, 125 insertions(+), 22 deletions(-) create mode 100644 shop/templates/pdf/custominvoice.html diff --git a/shop/management/commands/invoice-worker.py b/shop/management/commands/invoice-worker.py index 4fe2ea34..5c8ab654 100644 --- a/shop/management/commands/invoice-worker.py +++ b/shop/management/commands/invoice-worker.py @@ -4,7 +4,7 @@ from django.conf import settings from django.utils import timezone from shop.pdf import generate_pdf_letter from shop.email import send_invoice_email, send_creditnote_email -from shop.models import Order, Invoice, CreditNote +from shop.models import Order, CustomOrder, Invoice, CreditNote from time import sleep from decimal import Decimal @@ -19,11 +19,17 @@ class Command(BaseCommand): def handle(self, *args, **options): self.output('Invoice worker running...') while True: - # check if we need to generate any invoices + # check if we need to generate any invoices for shop orders for order in Order.objects.filter(paid=True, invoice__isnull=True): # generate invoice for this Order Invoice.objects.create(order=order) - self.output('Generated Invoice object for order %s' % order) + self.output('Generated Invoice object for %s' % order) + + # check if we need to generate any invoices for custom orders + for customorder in CustomOrder.objects.filter(paid=True, invoice__isnull=True): + # generate invoice for this CustomOrder + Invoice.objects.create(customorder=customorder) + self.output('Generated Invoice object for %s' % customorder) # check if we need to generate any pdf invoices for invoice in Invoice.objects.filter(pdf=''): @@ -34,9 +40,13 @@ class Command(BaseCommand): # generate the pdf try: + if invoice.customorder: + template='pdf/custominvoice.html' + else: + template='pdf/invoice.html' pdffile = generate_pdf_letter( filename=invoice.filename, - template='pdf/invoice.html', + template=template, formatdict=formatdict, ) self.output('Generated pdf for invoice %s' % invoice) @@ -54,8 +64,8 @@ class Command(BaseCommand): invoice.save() ############################################################### - # check if we need to send out any invoices (only where pdf has been generated) - for invoice in Invoice.objects.filter(sent_to_customer=False).exclude(pdf=''): + # check if we need to send out any invoices (only for shop orders, and only where pdf has been generated) + for invoice in Invoice.objects.filter(order__isnull=False, sent_to_customer=False).exclude(pdf=''): # send the email if send_invoice_email(invoice=invoice): self.output('OK: Invoice email sent to %s' % invoice.order.user.email) diff --git a/shop/models.py b/shop/models.py index 87a22985..97ee9128 100644 --- a/shop/models.py +++ b/shop/models.py @@ -15,8 +15,34 @@ from decimal import Decimal from datetime import timedelta -class Order(CreatedUpdatedModel): +class CustomOrder(CreatedUpdatedModel): + camp = models.ForeignKey( + 'camps.Camp', + verbose_name=_('Camp'), + help_text=_('The camp this custom order is for.'), + ) + text = models.TextField() + + amount = models.IntegerField( + help_text=_('Amount of this custom order (in DKK, including VAT).') + ) + + paid = models.BooleanField( + verbose_name=_('Paid?'), + help_text=_('Whether this custom order has been paid.'), + default=False, + ) + + def __str__(self): + return 'custom order id #%s' % self.pk + + @property + def vat(self): + return Decimal(self.amount*Decimal(0.2)) + + +class Order(CreatedUpdatedModel): class Meta: unique_together = ('user', 'open') ordering = ['-created'] @@ -29,26 +55,26 @@ class Order(CreatedUpdatedModel): user = models.ForeignKey( 'auth.User', verbose_name=_('User'), - help_text=_('The user this order belongs to.'), + help_text=_('The user this shop order belongs to.'), related_name='orders', ) paid = models.BooleanField( verbose_name=_('Paid?'), - help_text=_('Whether this order has been paid.'), + help_text=_('Whether this shop order has been paid.'), default=False, ) open = models.NullBooleanField( verbose_name=_('Open?'), - help_text=_('Whether this order is open or not. "None" means closed.'), + help_text=_('Whether this shop order is open or not. "None" means closed.'), default=True, ) camp = models.ForeignKey( 'camps.Camp', verbose_name=_('Camp'), - help_text=_('The camp this order is for.'), + help_text=_('The camp this shop order is for.'), ) CREDIT_CARD = 'credit_card' @@ -84,7 +110,7 @@ class Order(CreatedUpdatedModel): objects = OrderQuerySet.as_manager() def __str__(self): - return 'order id #%s' % self.pk + return 'shop order id #%s' % self.pk def get_number_of_items(self): return self.products.aggregate( @@ -225,7 +251,7 @@ class Product(CreatedUpdatedModel, UUIDModel): slug = models.SlugField() price = models.IntegerField( - help_text=_('Price of the product (in DKK).') + help_text=_('Price of the product (in DKK, including VAT).') ) description = models.TextField() @@ -326,19 +352,29 @@ class CreditNote(CreatedUpdatedModel): class Invoice(CreatedUpdatedModel): - order = models.OneToOneField('shop.Order') + order = models.OneToOneField('shop.Order', null=True, blank=True) + customorder = models.OneToOneField('shop.CustomOrder', null=True, blank=True) pdf = models.FileField(null=True, blank=True, upload_to='invoices/') sent_to_customer = models.BooleanField(default=False) def __str__(self): - return 'invoice#%s - order %s - %s - total %s DKK (sent to %s: %s)' % ( - self.id, - self.order.id, - self.order.created, - self.order.total, - self.order.user.email, - self.sent_to_customer, - ) + if self.order: + return 'invoice#%s - shop order %s - %s - total %s DKK (sent to %s: %s)' % ( + self.id, + self.order.id, + self.order.created, + self.order.total, + self.order.user.email, + self.sent_to_customer, + ) + elif self.customorder: + return 'invoice#%s - custom order %s - %s - amount %s DKK (customer: %s)' % ( + self.id, + self.customorder.id, + self.customorder.created, + self.customorder.amount, + self.customorder.customer, + ) @property def filename(self): diff --git a/shop/templates/pdf/custominvoice.html b/shop/templates/pdf/custominvoice.html new file mode 100644 index 00000000..31df124f --- /dev/null +++ b/shop/templates/pdf/custominvoice.html @@ -0,0 +1,57 @@ +{% load static from staticfiles %} +{% load shop_tags %} + + + + + + + +
      +

    + {{ invoice.created|date:"b jS, Y" }}
    + Order CU#{{ invoice.customorder.pk }}
    + Invoice #{{ invoice.pk }} +

    +
    +

    Customer: {{ invoice.customorder.customer }}

    +
    +

    INVOICE

    + + + + + +
    + Text + + Quantity + + Price + + Total +
    + {{ invoice.customorder.text }} + + 1 + + {{ invoice.customorder.amount|currency }} + + {{ invoice.customorder.amount|currency }} + +
    + + Danish VAT (25%) + + {{ invoice.customorder.vat|currency }} + +
    + + Total + + {{ invoice.customorder.amount|currency }} +
    +
    +

    +Payment should be made by bank transfer to Arbejdernes Landsbank reg. 5371 account no. 0244504 within two weeks from {{ invoice.created|date:"b jS, Y" }} please. Thank you! +

    From 1d65ddeca62ccac429827c1448d973de33e12c71 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Tue, 12 Jul 2016 22:38:59 +0200 Subject: [PATCH 18/26] add migrations --- shop/migrations/0027_auto_20160712_2036.py | 77 +++++++++++++++++++ .../migrations/0005_auto_20160712_2036.py | 20 +++++ 2 files changed, 97 insertions(+) create mode 100644 shop/migrations/0027_auto_20160712_2036.py create mode 100644 villages/migrations/0005_auto_20160712_2036.py diff --git a/shop/migrations/0027_auto_20160712_2036.py b/shop/migrations/0027_auto_20160712_2036.py new file mode 100644 index 00000000..bb388524 --- /dev/null +++ b/shop/migrations/0027_auto_20160712_2036.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-07-12 20:36 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('camps', '0005_auto_20160510_2011'), + ('shop', '0026_order_refunded'), + ] + + operations = [ + migrations.CreateModel( + name='CustomOrder', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('text', models.TextField()), + ('amount', models.IntegerField(help_text='Amount of this custom order (in DKK, including VAT).')), + ('paid', models.BooleanField(default=False, help_text='Whether this custom order has been paid.', verbose_name='Paid?')), + ('camp', models.ForeignKey(help_text='The camp this custom order is for.', on_delete=django.db.models.deletion.CASCADE, to='camps.Camp', verbose_name='Camp')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterModelOptions( + name='creditnote', + options={'ordering': ['-created']}, + ), + migrations.AlterField( + model_name='creditnote', + name='paid', + field=models.BooleanField(default=False, help_text='Whether the amount in this creditnote has been paid back to the customer.', verbose_name='Paid?'), + ), + migrations.AlterField( + model_name='invoice', + name='order', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='shop.Order'), + ), + migrations.AlterField( + model_name='order', + name='camp', + field=models.ForeignKey(help_text='The camp this shop order is for.', on_delete=django.db.models.deletion.CASCADE, to='camps.Camp', verbose_name='Camp'), + ), + migrations.AlterField( + model_name='order', + name='open', + field=models.NullBooleanField(default=True, help_text='Whether this shop order is open or not. "None" means closed.', verbose_name='Open?'), + ), + migrations.AlterField( + model_name='order', + name='paid', + field=models.BooleanField(default=False, help_text='Whether this shop order has been paid.', verbose_name='Paid?'), + ), + migrations.AlterField( + model_name='order', + name='user', + field=models.ForeignKey(help_text='The user this shop order belongs to.', on_delete=django.db.models.deletion.CASCADE, related_name='orders', to=settings.AUTH_USER_MODEL, verbose_name='User'), + ), + migrations.AlterField( + model_name='product', + name='price', + field=models.IntegerField(help_text='Price of the product (in DKK, including VAT).'), + ), + migrations.AddField( + model_name='invoice', + name='customorder', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='shop.CustomOrder'), + ), + ] diff --git a/villages/migrations/0005_auto_20160712_2036.py b/villages/migrations/0005_auto_20160712_2036.py new file mode 100644 index 00000000..ff3f8ec2 --- /dev/null +++ b/villages/migrations/0005_auto_20160712_2036.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-07-12 20:36 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('villages', '0004_village_deleted'), + ] + + operations = [ + migrations.AlterField( + model_name='village', + name='description', + field=models.TextField(help_text='A descriptive text about your village. Markdown is supported.'), + ), + ] From ae1c15b88eafd61dff6e980681c1a5e4cc411aac Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Tue, 12 Jul 2016 22:42:30 +0200 Subject: [PATCH 19/26] add CustomOrder to admin --- shop/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shop/admin.py b/shop/admin.py index 0aa0c51f..1c9478d8 100644 --- a/shop/admin.py +++ b/shop/admin.py @@ -9,7 +9,7 @@ admin.site.register(models.CoinifyAPICallback) admin.site.register(models.Invoice) admin.site.register(models.CreditNote) admin.site.register(models.Ticket) - +admin.site.register(models.CustomOrder) @admin.register(models.ProductCategory) class ProductCategoryAdmin(admin.ModelAdmin): From be34e1a6b5cafcfacd8deee46a5362129031cfdf Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Tue, 12 Jul 2016 22:56:21 +0200 Subject: [PATCH 20/26] generate invoices for unpaid customorders --- shop/management/commands/invoice-worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shop/management/commands/invoice-worker.py b/shop/management/commands/invoice-worker.py index 5c8ab654..2ad55e88 100644 --- a/shop/management/commands/invoice-worker.py +++ b/shop/management/commands/invoice-worker.py @@ -26,7 +26,7 @@ class Command(BaseCommand): self.output('Generated Invoice object for %s' % order) # check if we need to generate any invoices for custom orders - for customorder in CustomOrder.objects.filter(paid=True, invoice__isnull=True): + for customorder in CustomOrder.objects.filter(invoice__isnull=True): # generate invoice for this CustomOrder Invoice.objects.create(customorder=customorder) self.output('Generated Invoice object for %s' % customorder) From f60d2bfdaebea1fbb1a7334238bdead02f8f8ede Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Tue, 12 Jul 2016 23:16:45 +0200 Subject: [PATCH 21/26] add customer field to customorder model, change template a bit --- shop/models.py | 8 +++++++- shop/templates/pdf/custominvoice.html | 16 +++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/shop/models.py b/shop/models.py index 97ee9128..0ed28d3b 100644 --- a/shop/models.py +++ b/shop/models.py @@ -22,7 +22,13 @@ class CustomOrder(CreatedUpdatedModel): help_text=_('The camp this custom order is for.'), ) - text = models.TextField() + text = models.TextField( + help_text=_('The invoice text') + ) + + customer = models.TextField( + help_text=_('The customer info for this order, use
    for newlines') + ) amount = models.IntegerField( help_text=_('Amount of this custom order (in DKK, including VAT).') diff --git a/shop/templates/pdf/custominvoice.html b/shop/templates/pdf/custominvoice.html index 31df124f..389508e3 100644 --- a/shop/templates/pdf/custominvoice.html +++ b/shop/templates/pdf/custominvoice.html @@ -21,37 +21,27 @@ Text - - Quantity Price - - Total {{ invoice.customorder.text }} - - 1 - - {{ invoice.customorder.amount|currency }} {{ invoice.customorder.amount|currency }} - - + Danish VAT (25%) {{ invoice.customorder.vat|currency }} - - + Total {{ invoice.customorder.amount|currency }}

    -Payment should be made by bank transfer to Arbejdernes Landsbank reg. 5371 account no. 0244504 within two weeks from {{ invoice.created|date:"b jS, Y" }} please. Thank you! +Payment should be made by bank transfer to our account in Arbejdernes Landsbank reg. 5371 account no. 0244504 within two weeks from {{ invoice.created|date:"b jS, Y" }} please. Thank you!

    From 1ff887ca82d0df38628f59f6335bd336c11dc1bd Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Tue, 12 Jul 2016 23:19:43 +0200 Subject: [PATCH 22/26] migration for previous commit --- shop/migrations/0028_auto_20160712_2119.py | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 shop/migrations/0028_auto_20160712_2119.py diff --git a/shop/migrations/0028_auto_20160712_2119.py b/shop/migrations/0028_auto_20160712_2119.py new file mode 100644 index 00000000..33cf57ff --- /dev/null +++ b/shop/migrations/0028_auto_20160712_2119.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-07-12 21:19 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0027_auto_20160712_2036'), + ] + + operations = [ + migrations.AddField( + model_name='customorder', + name='customer', + field=models.TextField(default='', help_text='The customer info for this order, use
    for newlines'), + preserve_default=False, + ), + migrations.AlterField( + model_name='customorder', + name='text', + field=models.TextField(help_text='The invoice text'), + ), + ] From 1b1c0a8065bc1fc1dacad6e654b4f449211c9a84 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Tue, 12 Jul 2016 23:32:05 +0200 Subject: [PATCH 23/26] use |linebreaks templatefilter --- shop/models.py | 2 +- shop/templates/pdf/custominvoice.html | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shop/models.py b/shop/models.py index 0ed28d3b..b3efe38e 100644 --- a/shop/models.py +++ b/shop/models.py @@ -27,7 +27,7 @@ class CustomOrder(CreatedUpdatedModel): ) customer = models.TextField( - help_text=_('The customer info for this order, use
    for newlines') + help_text=_('The customer info for this order') ) amount = models.IntegerField( diff --git a/shop/templates/pdf/custominvoice.html b/shop/templates/pdf/custominvoice.html index 389508e3..20b406d1 100644 --- a/shop/templates/pdf/custominvoice.html +++ b/shop/templates/pdf/custominvoice.html @@ -8,13 +8,13 @@

    {{ invoice.created|date:"b jS, Y" }}
    - Order CU#{{ invoice.customorder.pk }}
    + Custom Order #{{ invoice.customorder.pk }}
    Invoice #{{ invoice.pk }}

    -

    Customer: {{ invoice.customorder.customer }}

    +

    Customer: {{ invoice.customorder.customer|linebreaks }}


    INVOICE

    @@ -25,7 +25,7 @@ Price
    - {{ invoice.customorder.text }} + {{ invoice.customorder.text|linebreaks }} {{ invoice.customorder.amount|currency }} From edcdf23c19836a82376bdad81d88571ac4b8cae0 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Tue, 12 Jul 2016 23:34:46 +0200 Subject: [PATCH 24/26] add migration --- shop/migrations/0029_auto_20160712_2133.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 shop/migrations/0029_auto_20160712_2133.py diff --git a/shop/migrations/0029_auto_20160712_2133.py b/shop/migrations/0029_auto_20160712_2133.py new file mode 100644 index 00000000..ebec0768 --- /dev/null +++ b/shop/migrations/0029_auto_20160712_2133.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-07-12 21:33 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0028_auto_20160712_2119'), + ] + + operations = [ + migrations.AlterField( + model_name='customorder', + name='customer', + field=models.TextField(help_text='The customer info for this order'), + ), + ] From c4459695da7b00e5c46dedc894b137f3159af6d3 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Wed, 13 Jul 2016 00:21:03 +0200 Subject: [PATCH 25/26] pdf template styling --- shop/templates/pdf/custominvoice.html | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/shop/templates/pdf/custominvoice.html b/shop/templates/pdf/custominvoice.html index 20b406d1..6cb158dc 100644 --- a/shop/templates/pdf/custominvoice.html +++ b/shop/templates/pdf/custominvoice.html @@ -14,34 +14,38 @@
    -

    Customer: {{ invoice.customorder.customer|linebreaks }}

    + +

    CUSTOMER

    +

    {{ invoice.customorder.customer|linebreaks }}


    +

    INVOICE

    - -
    Text - + Price
    {{ invoice.customorder.text|linebreaks }} - + {{ invoice.customorder.amount|currency }}
    + Danish VAT (25%) - + {{ invoice.customorder.vat|currency }}
    + Total - + {{ invoice.customorder.amount|currency }}

    +

    Payment should be made by bank transfer to our account in Arbejdernes Landsbank reg. 5371 account no. 0244504 within two weeks from {{ invoice.created|date:"b jS, Y" }} please. Thank you!

    From 178be1deb0770424f5f62cb76978ec0d879a2310 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Wed, 13 Jul 2016 00:24:34 +0200 Subject: [PATCH 26/26] more template styling --- shop/templates/pdf/custominvoice.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/shop/templates/pdf/custominvoice.html b/shop/templates/pdf/custominvoice.html index 6cb158dc..101bc95d 100644 --- a/shop/templates/pdf/custominvoice.html +++ b/shop/templates/pdf/custominvoice.html @@ -20,28 +20,28 @@

    INVOICE

    - +
    - -
    Text - + Price
    {{ invoice.customorder.text|linebreaks }} - + {{ invoice.customorder.amount|currency }}
    + Danish VAT (25%) - + {{ invoice.customorder.vat|currency }}
    + Total - + {{ invoice.customorder.amount|currency }}