Working book shop. shop
authorRadek Czajka <radekczajka@nowoczesnapolska.org.pl>
Thu, 31 Oct 2013 10:32:43 +0000 (11:32 +0100)
committerRadek Czajka <radekczajka@nowoczesnapolska.org.pl>
Thu, 31 Oct 2013 10:32:43 +0000 (11:32 +0100)
27 files changed:
prawokultury/locale/pl/LC_MESSAGES/django.mo
prawokultury/locale/pl/LC_MESSAGES/django.po
prawokultury/middleware.py [new file with mode: 0755]
prawokultury/settings.d/30-apps.py
prawokultury/settings.d/40-middleware.py
prawokultury/settings.d/50-contrib.py
prawokultury/settings.d/50-static.py
prawokultury/static/css/forms.css
prawokultury/static/css/forms.scss
prawokultury/templates/migdal/entry/publications/entry_detail.html
shop/admin.py
shop/forms.py
shop/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
shop/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
shop/migrations/0001_initial.py
shop/models.py
shop/static/shop/shop.js [new file with mode: 0755]
shop/templates/shop/email/base.txt
shop/templates/shop/email/payed.txt
shop/templates/shop/email/payed_managers.txt [new file with mode: 0755]
shop/templates/shop/no_thanks.html
shop/templates/shop/snippets/order_form.html
shop/templates/shop/thanks.html
shop/templatetags/shop_tags.py
shop/urls.py
shop/views.py
shop/widgets.py [new file with mode: 0755]

index 5859207..41f6877 100644 (file)
Binary files a/prawokultury/locale/pl/LC_MESSAGES/django.mo and b/prawokultury/locale/pl/LC_MESSAGES/django.mo differ
index 261bf54..ff1d3f9 100644 (file)
@@ -7,16 +7,16 @@ msgid ""
 msgstr ""
 "Project-Id-Version: prawokultury\n"
 "Report-Msgid-Bugs-To: \n"
 msgstr ""
 "Project-Id-Version: prawokultury\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-05-17 10:41+0200\n"
-"PO-Revision-Date: 2013-01-14 16:09+0100\n"
+"POT-Creation-Date: 2013-10-31 11:29+0100\n"
+"PO-Revision-Date: 2013-10-31 11:30+0100\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Language-Team: FNP <fundacja@nowoczesnapolska.org.pl>\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Language-Team: FNP <fundacja@nowoczesnapolska.org.pl>\n"
-"Language: \n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
-"|| n%100>=20) ? 1 : 2)\n"
+"|| n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 1.5.4\n"
 
 #: menu_items.py:15
 msgid "Publications"
 
 #: menu_items.py:15
 msgid "Publications"
@@ -38,10 +38,14 @@ msgstr "Pierwsza pomoc"
 msgid "Guide"
 msgstr "Przewodnik"
 
 msgid "Guide"
 msgstr "Przewodnik"
 
-#: urls.py:42
+#: urls.py:47
 msgid "events"
 msgstr "wydarzenia"
 
 msgid "events"
 msgstr "wydarzenia"
 
+#: urls.py:50
+msgid "shop"
+msgstr "sklep"
+
 #: settings.d/60-custom.py:5
 msgid "news"
 msgstr "newsy"
 #: settings.d/60-custom.py:5
 msgid "news"
 msgstr "newsy"
@@ -62,44 +66,44 @@ msgstr "Strona nie znaleziona"
 msgid "The page you were looking for doesn't exist."
 msgstr "Strona, której szukasz, nie istnieje."
 
 msgid "The page you were looking for doesn't exist."
 msgstr "Strona, której szukasz, nie istnieje."
 
-#: templates/base.html:8 templates/base.html.py:14 templates/base.html:15
-#: templates/base.html.py:27 templates/base.html:79
+#: templates/base.html:9 templates/base.html.py:15 templates/base.html:16
+#: templates/base.html.py:28 templates/base.html:80
 msgid "Right to Culture"
 msgstr "Prawo kultury"
 
 msgid "Right to Culture"
 msgstr "Prawo kultury"
 
-#: templates/base.html:32
+#: templates/base.html:33
 msgid "Organizer"
 msgstr "Organizator"
 
 msgid "Organizer"
 msgstr "Organizator"
 
-#: templates/base.html:35
+#: templates/base.html:36
 msgid "Modern Poland Foundation"
 msgstr "Fundacja Nowoczesna Polska"
 
 msgid "Modern Poland Foundation"
 msgstr "Fundacja Nowoczesna Polska"
 
-#: templates/base.html:37
+#: templates/base.html:38
 msgid "Sponsor"
 msgstr "Wspierane przez"
 
 msgid "Sponsor"
 msgstr "Wspierane przez"
 
-#: templates/base.html:40
+#: templates/base.html:41
 msgid "Trust for Civil Society in Central and Eastern Europe"
 msgstr ""
 
 msgid "Trust for Civil Society in Central and Eastern Europe"
 msgstr ""
 
-#: templates/base.html:49
+#: templates/base.html:50
 msgid "Search"
 msgstr "Szukaj"
 
 msgid "Search"
 msgstr "Szukaj"
 
-#: templates/base.html:65
+#: templates/base.html:66
 msgid "Upcoming events"
 msgstr "Nadchodzące wydarzenia"
 
 msgid "Upcoming events"
 msgstr "Nadchodzące wydarzenia"
 
-#: templates/base.html:69
+#: templates/base.html:70
 msgid "Ask a lawyer"
 msgstr "Zapytaj prawnika"
 
 msgid "Ask a lawyer"
 msgstr "Zapytaj prawnika"
 
-#: templates/base.html:83
+#: templates/base.html:84
 msgid "Latest comments"
 msgstr "Ostatnie komentarze"
 
 msgid "Latest comments"
 msgstr "Ostatnie komentarze"
 
-#: templates/base.html:112
+#: templates/base.html:113
 msgid ""
 "If not explicitly stated otherwise, all texts are licensed under the <a "
 "href='http://creativecommons.org/licenses/by-sa/3.0/'>Creative Commons "
 msgid ""
 "If not explicitly stated otherwise, all texts are licensed under the <a "
 "href='http://creativecommons.org/licenses/by-sa/3.0/'>Creative Commons "
@@ -109,6 +113,14 @@ msgstr ""
 "href='http://creativecommons.org/licenses/by-sa/3.0/deed.pl'>Creative "
 "Commons Uznanie autorstwa – Na tych samych warunkach</a>."
 
 "href='http://creativecommons.org/licenses/by-sa/3.0/deed.pl'>Creative "
 "Commons Uznanie autorstwa – Na tych samych warunkach</a>."
 
+#: templates/migdal/entry/publications/entry_detail.html:10
+msgid "This entry hasn't been published yet."
+msgstr "Ten wpis nie został jeszcze opublikowany."
+
+#: templates/migdal/entry/publications/entry_detail.html:28
+msgid "Price"
+msgstr "Cena"
+
 #~ msgid "First aid in copyright"
 #~ msgstr "Pierwsza pomoc w prawie autorskim"
 
 #~ msgid "First aid in copyright"
 #~ msgstr "Pierwsza pomoc w prawie autorskim"
 
diff --git a/prawokultury/middleware.py b/prawokultury/middleware.py
new file mode 100755 (executable)
index 0000000..0911a69
--- /dev/null
@@ -0,0 +1,12 @@
+from honeypot.middleware import HoneypotViewMiddleware
+
+def honeypot_exempt(view):
+    view.honeypot_exempt = True
+    return view
+
+class ExemptableHoneypotViewMiddleware(HoneypotViewMiddleware):
+    def process_view(self, request, callback, callback_args, callback_kwargs):
+        if hasattr(callback, 'honeypot_exempt'):
+            return None
+        return super(ExemptableHoneypotViewMiddleware, self).process_view(
+            request, callback, callback_args, callback_kwargs)
index 578e9dd..1e87f11 100644 (file)
@@ -22,6 +22,9 @@ INSTALLED_APPS = (
     'taggit_autosuggest',
     'getpaid',
     'getpaid.backends.payu',
     'taggit_autosuggest',
     'getpaid',
     'getpaid.backends.payu',
+    'djcelery',
+    'djkombu',
+
 
     'django.contrib.auth',
     'django.contrib.contenttypes',
 
     'django.contrib.auth',
     'django.contrib.contenttypes',
index c8a63b6..f0229cd 100644 (file)
@@ -5,7 +5,8 @@ MIDDLEWARE_CLASSES = (
 
     'django.middleware.common.CommonMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
 
     'django.middleware.common.CommonMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
-    'honeypot.middleware.HoneypotMiddleware',
+    'prawokultury.middleware.ExemptableHoneypotViewMiddleware',
+    'honeypot.middleware.HoneypotResponseMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
 )
 
     'django.contrib.auth.middleware.AuthenticationMiddleware',
 )
 
index 4423184..4625e79 100644 (file)
@@ -19,3 +19,21 @@ CAS_VERSION = '1'
 HONEYPOT_FIELD_NAME='miut'
 
 TAGGIT_AUTOSUGGEST_MODEL = ('questions', 'Tag')
 HONEYPOT_FIELD_NAME='miut'
 
 TAGGIT_AUTOSUGGEST_MODEL = ('questions', 'Tag')
+
+GETPAID_BACKENDS = (
+    'getpaid.backends.payu',
+)
+
+
+import djcelery
+djcelery.setup_loader()
+
+BROKER_BACKEND = "djkombu.transport.DatabaseTransport"
+BROKER_HOST = "localhost"
+BROKER_PORT = 5672
+BROKER_USER = "guest"
+BROKER_PASSWORD = "guest"
+BROKER_VHOST = "/"
+
+CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
+CELERY_SEND_TASK_ERROR_EMAILS = True
index 8f514da..f43274b 100644 (file)
@@ -42,6 +42,7 @@ PIPELINE_JS = {
     'base': {
         'source_filenames': (
             'js/promobox.js',
     'base': {
         'source_filenames': (
             'js/promobox.js',
+            'shop/shop.js',
 
         ),
         'output_filename': 'compressed/base.js',
 
         ),
         'output_filename': 'compressed/base.js',
index 4acb30f..70930a0 100644 (file)
@@ -1,10 +1,13 @@
+.entry-wrapped .submit-form {
+  font-size: .9em; }
+
 .form-info {
   font-size: 1.2em; }
 
 .submit-form {
   margin-top: 3em; }
   .submit-form table {
 .form-info {
   font-size: 1.2em; }
 
 .submit-form {
   margin-top: 3em; }
   .submit-form table {
-    border-spacing: 0 .5em; }
+    border-spacing: 0 0.5em; }
   .submit-form tr {
     background-color: #fdfdfd; }
   .submit-form label {
   .submit-form tr {
     background-color: #fdfdfd; }
   .submit-form label {
@@ -14,7 +17,7 @@
     color: #e41b13;
     font-size: 1.2em;
     padding: 0 0 0 1.3em;
     color: #e41b13;
     font-size: 1.2em;
     padding: 0 0 0 1.3em;
-    margin: 0 0 .5em 0;
+    margin: 0 0 0.5em 0;
     list-style: url("/static/img/read-more.png"); }
   .submit-form input, .submit-form textarea, .submit-form select {
     font-size: 1.2em;
     list-style: url("/static/img/read-more.png"); }
   .submit-form input, .submit-form textarea, .submit-form select {
     font-size: 1.2em;
@@ -23,7 +26,7 @@
     color: #363A3B;
     width: 100%;
     margin-bottom: .5em;
     color: #363A3B;
     width: 100%;
     margin-bottom: .5em;
-    border: 1px solid #EDECE7; }
+    border: 1px solid #edece7; }
   .submit-form th, .submit-form td {
     text-align: left;
     font-weight: normal;
   .submit-form th, .submit-form td {
     text-align: left;
     font-weight: normal;
index 9539415..1f3488a 100644 (file)
@@ -1,3 +1,7 @@
+.entry-wrapped .submit-form {
+    font-size: .9em;
+}
+
 .form-info {
     font-size: 1.2em;
 }
 .form-info {
     font-size: 1.2em;
 }
index 0cae1fa..b231954 100755 (executable)
@@ -1,6 +1,7 @@
 {% extends "base.html" %}
 {% load comments i18n %}
 {% load fnp_common migdal_tags fnp_share shop_tags %}
 {% extends "base.html" %}
 {% load comments i18n %}
 {% load fnp_common migdal_tags fnp_share shop_tags %}
+{% load url from future %}
 
 
 {% block "body" %}
 
 
 {% block "body" %}
 
 {% entry_begin entry 1 %}
 <div class="body">
 
 {% entry_begin entry 1 %}
 <div class="body">
-{{ entry.body }}
+
+{% if entry.offer %}
+<div style="float:left;clear:left;">
+<a href="{% url 'migdal_entry_info' 'regulamin-sklepu' %}">Regulamin sklepu</a>
+</div>
+{% endif %}
 
 
 
 
+{% if entry.offer %}
 
 
+<p>{% trans "Price" %}: {{ entry.offer.price|floatformat:"-2" }} PLN</p>
 
 
-{% if entry.offer_set.all.exists %}
 
 
-{% order_form_for entry.offer_set.all.0 form %}
+{% order_form_for entry.offer form %}
 
 {% endif %}
 
 
 
 {% endif %}
 
 
+{{ entry.body }}
+
+
 
 
 {% for inline_html in entry.inline_html %}
 
 
 {% for inline_html in entry.inline_html %}
index 6c84a94..58fd842 100644 (file)
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
-# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# This file is part of PrawoKultury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.utils.translation import ugettext_lazy as _
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.utils.translation import ugettext_lazy as _
@@ -10,7 +10,6 @@ from .models import Offer, Order
 class OfferAdmin(admin.ModelAdmin):
     model = Offer
     list_display = ['entry', 'price']
 class OfferAdmin(admin.ModelAdmin):
     model = Offer
     list_display = ['entry', 'price']
-    #~ search_fields = ['entry__title_pl']
 
 
 class PayedFilter(admin.SimpleListFilter):
 
 
 class PayedFilter(admin.SimpleListFilter):
index ce8a48f..fedd152 100644 (file)
@@ -1,32 +1,50 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
-# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# This file is part of PrawoKultury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django import forms
 from django.utils import formats
 from django.utils.translation import ugettext_lazy as _, ugettext, get_language
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django import forms
 from django.utils import formats
 from django.utils.translation import ugettext_lazy as _, ugettext, get_language
-from .models import Order
 from . import app_settings
 from . import app_settings
+from .models import Order
+from .widgets import NumberInput
 
 
 class OrderForm(forms.Form):
     required_css_class = 'required'
     backend = 'getpaid.backends.payu'
 
 
 class OrderForm(forms.Form):
     required_css_class = 'required'
     backend = 'getpaid.backends.payu'
-
+    items = forms.IntegerField(label=_("Items"), min_value=1, initial=1,
+        widget=NumberInput(attrs={'min': '1', 'step': '1', 'class': 'cost-items'}))
     name = forms.CharField(label=_("Name"))
     email = forms.EmailField(label=_("Contact e-mail"))
     address = forms.CharField(label=_("Address"), widget=forms.Textarea)
     name = forms.CharField(label=_("Name"))
     email = forms.EmailField(label=_("Contact e-mail"))
     address = forms.CharField(label=_("Address"), widget=forms.Textarea)
-    consent = forms.CharField(label=_("Consent"), widget=forms.Textarea,
-        help_text=_('I hereby consent'))
+
+    accept = forms.BooleanField(label=_("Accept terms"),
+        help_text='''Akceptuję <a href='/info/regulamin-sklepu/'>regulamin sklepu</a>.''')
+
+    consent = forms.BooleanField(label=_("Consent to the processing of data"),
+        help_text='''Wyrażam zgodę na przetwarzanie moich danych osobowych w celu realizacji
+zamówienia. Administratorem danych osobowych jest Fundacja Nowoczesna
+Polska, ul. Marszałkowska 84/92, lok. 125, 00-514 Warszawa.
+Zapoznałem/zapoznałam się
+z&nbsp;<a href="http://nowoczesnapolska.org.pl/prywatnosc/">polityką prywatności Fundacji</a>.
+Jestem świadom/świadoma, iż moja zgoda może być odwołana w każdym czasie, co skutkować będzie
+usunięciem mojego adresu e-mail z bazy danych.''')
 
     def __init__(self, offer, *args, **kwargs):
 
     def __init__(self, offer, *args, **kwargs):
-        print 'o:', offer
         self.offer = offer
         super(OrderForm, self).__init__(*args, **kwargs)
         self.offer = offer
         super(OrderForm, self).__init__(*args, **kwargs)
+        self.fields['items'].widget.attrs.update({
+            'data-cost-price': self.offer.price,
+            'data-cost-per-item': self.offer.cost_per_item,
+            'data-cost-const': self.offer.cost_const,
+            'data-decimal-separator': formats.get_format("DECIMAL_SEPARATOR"),
+            })
 
     def save(self):
         order = Order.objects.create(
             offer=self.offer,
 
     def save(self):
         order = Order.objects.create(
             offer=self.offer,
+            items=self.cleaned_data['items'],
             name=self.cleaned_data['name'],
             email=self.cleaned_data['email'],
             address=self.cleaned_data['address'],
             name=self.cleaned_data['name'],
             email=self.cleaned_data['email'],
             address=self.cleaned_data['address'],
diff --git a/shop/locale/pl/LC_MESSAGES/django.mo b/shop/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..24fe2e9
Binary files /dev/null and b/shop/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/shop/locale/pl/LC_MESSAGES/django.po b/shop/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..b523584
--- /dev/null
@@ -0,0 +1,143 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-10-31 11:13+0100\n"
+"PO-Revision-Date: 2013-10-31 11:25+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: admin.py:16
+msgid "payment complete"
+msgstr "płatność wykonana"
+
+#: admin.py:20
+msgid "Yes"
+msgstr "Tak"
+
+#: admin.py:21
+msgid "No"
+msgstr "Nie"
+
+#: forms.py:16
+msgid "Items"
+msgstr "Liczba egzemplarzy"
+
+#: forms.py:18
+msgid "Name"
+msgstr "Imię i nazwisko"
+
+#: forms.py:19
+msgid "Contact e-mail"
+msgstr "E-mail kontaktowy"
+
+#: forms.py:20
+msgid "Address"
+msgstr "Adres"
+
+#: forms.py:22
+msgid "Accept terms"
+msgstr "Akceptacja regulaminu"
+
+#: forms.py:25
+msgid "Consent to the processing of data"
+msgstr "Zgoda na przetwarzanie danych"
+
+#: models.py:20
+msgid "price"
+msgstr "cena"
+
+#: models.py:25 models.py:45
+msgid "offer"
+msgstr "oferta"
+
+#: models.py:26
+msgid "offers"
+msgstr "oferty"
+
+#: models.py:46
+msgid "items"
+msgstr "liczba egzemplarzy"
+
+#: models.py:47
+msgid "name"
+msgstr "nazwisko"
+
+#: models.py:48
+msgid "email"
+msgstr "e-mail"
+
+#: models.py:49
+msgid "address"
+msgstr "adres"
+
+#: models.py:50
+msgid "payed at"
+msgstr "zapłacono"
+
+#: models.py:54
+msgid "order"
+msgstr "zamówienie"
+
+#: models.py:55
+msgid "orders"
+msgstr "zamówienie"
+
+#: models.py:113
+msgid "Your payment has been completed."
+msgstr "Twoja płatność została wykonana."
+
+#: models.py:117
+msgid "New order has been placed."
+msgstr "Zostało złożone nowe zamówienie."
+
+#: templates/shop/no_thanks.html:5 templates/shop/no_thanks.html.py:9
+msgid "Payment failed"
+msgstr "Płatność zakończona niepowodzeniem"
+
+#: templates/shop/no_thanks.html:13
+msgid "You're support has not been processed successfully."
+msgstr "Twoja płatność nie została zakończona powodzeniem."
+
+#: templates/shop/no_thanks.html:15 templates/shop/thanks.html:15
+msgid "Go back to:"
+msgstr "Wróć do:"
+
+#: templates/shop/thanks.html:5 templates/shop/thanks.html.py:9
+msgid "Payment successful"
+msgstr "Płatność wykonana."
+
+#: templates/shop/thanks.html:13 templates/shop/email/payed.txt:6
+msgid "Your payment has been successfully completed."
+msgstr "Twoja płatność została wykonana."
+
+#: templates/shop/email/base.txt:1
+msgid "Hi"
+msgstr "Cześć"
+
+#: templates/shop/email/base.txt:4
+msgid ""
+"Cheers,\n"
+"Right to Culture team"
+msgstr ""
+"Pozdrowienia,\n"
+"Zespół Prawa Kultury"
+
+#: templates/shop/email/payed.txt:8
+msgid "Your order is now being processed: "
+msgstr "Twoje zamówienie jest przetwarzane:"
+
+#: templates/shop/snippets/order_form.html:18
+msgid "Donate!"
+msgstr "Wesprzyj!"
index 0bc1dc2..471a911 100644 (file)
@@ -11,8 +11,10 @@ class Migration(SchemaMigration):
         # Adding model 'Offer'
         db.create_table('shop_offer', (
             ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
         # Adding model 'Offer'
         db.create_table('shop_offer', (
             ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('entry', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['migdal.Entry'])),
+            ('entry', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['migdal.Entry'], unique=True)),
             ('price', self.gf('django.db.models.fields.DecimalField')(max_digits=6, decimal_places=2)),
             ('price', self.gf('django.db.models.fields.DecimalField')(max_digits=6, decimal_places=2)),
+            ('cost_const', self.gf('django.db.models.fields.DecimalField')(max_digits=6, decimal_places=2)),
+            ('cost_per_item', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=6, decimal_places=2)),
         ))
         db.send_create_signal('shop', ['Offer'])
 
         ))
         db.send_create_signal('shop', ['Offer'])
 
@@ -20,6 +22,7 @@ class Migration(SchemaMigration):
         db.create_table('shop_order', (
             ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
             ('offer', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['shop.Offer'])),
         db.create_table('shop_order', (
             ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
             ('offer', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['shop.Offer'])),
+            ('items', self.gf('django.db.models.fields.IntegerField')(default=1)),
             ('name', self.gf('django.db.models.fields.CharField')(max_length=127, blank=True)),
             ('email', self.gf('django.db.models.fields.EmailField')(max_length=75, db_index=True)),
             ('address', self.gf('django.db.models.fields.TextField')(db_index=True)),
             ('name', self.gf('django.db.models.fields.CharField')(max_length=127, blank=True)),
             ('email', self.gf('django.db.models.fields.EmailField')(max_length=75, db_index=True)),
             ('address', self.gf('django.db.models.fields.TextField')(db_index=True)),
@@ -83,8 +86,10 @@ class Migration(SchemaMigration):
             'type': ('django.db.models.fields.CharField', [], {'max_length': '16', 'db_index': 'True'})
         },
         'shop.offer': {
             'type': ('django.db.models.fields.CharField', [], {'max_length': '16', 'db_index': 'True'})
         },
         'shop.offer': {
-            'Meta': {'ordering': "['entry__title']", 'object_name': 'Offer'},
-            'entry': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['migdal.Entry']"}),
+            'Meta': {'ordering': "['entry']", 'object_name': 'Offer'},
+            'cost_const': ('django.db.models.fields.DecimalField', [], {'max_digits': '6', 'decimal_places': '2'}),
+            'cost_per_item': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '2'}),
+            'entry': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['migdal.Entry']", 'unique': 'True'}),
             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
             'price': ('django.db.models.fields.DecimalField', [], {'max_digits': '6', 'decimal_places': '2'})
         },
             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
             'price': ('django.db.models.fields.DecimalField', [], {'max_digits': '6', 'decimal_places': '2'})
         },
@@ -93,6 +98,7 @@ class Migration(SchemaMigration):
             'address': ('django.db.models.fields.TextField', [], {'db_index': 'True'}),
             'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'db_index': 'True'}),
             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
             'address': ('django.db.models.fields.TextField', [], {'db_index': 'True'}),
             'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'db_index': 'True'}),
             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'items': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
             'language_code': ('django.db.models.fields.CharField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}),
             'name': ('django.db.models.fields.CharField', [], {'max_length': '127', 'blank': 'True'}),
             'offer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shop.Offer']"}),
             'language_code': ('django.db.models.fields.CharField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}),
             'name': ('django.db.models.fields.CharField', [], {'max_length': '127', 'blank': 'True'}),
             'offer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shop.Offer']"}),
index 72d599c..349cf43 100644 (file)
@@ -3,7 +3,7 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from datetime import datetime
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from datetime import datetime
-from django.core.mail import send_mail
+from django.core.mail import send_mail, mail_managers
 from django.conf import settings
 from django.contrib.sites.models import Site
 from django.db import models
 from django.conf import settings
 from django.contrib.sites.models import Site
 from django.db import models
@@ -16,8 +16,10 @@ from . import app_settings
 
 class Offer(models.Model):
     """ A fundraiser for a particular book. """
 
 class Offer(models.Model):
     """ A fundraiser for a particular book. """
-    entry = models.ForeignKey(Entry)  # filter publications!
+    entry = models.OneToOneField(Entry)  # filter publications!
     price = models.DecimalField(_('price'), decimal_places=2, max_digits=6)
     price = models.DecimalField(_('price'), decimal_places=2, max_digits=6)
+    cost_const = models.DecimalField(decimal_places=2, max_digits=6)
+    cost_per_item = models.DecimalField(decimal_places=2, max_digits=6, default=0)
 
     class Meta:
         verbose_name = _('offer')
 
     class Meta:
         verbose_name = _('offer')
@@ -30,9 +32,11 @@ class Offer(models.Model):
     def get_absolute_url(self):
         return self.entry.get_absolute_url()
 
     def get_absolute_url(self):
         return self.entry.get_absolute_url()
 
-    def sum(self):
-        """ The money gathered. """
-        return self.order_payed().aggregate(s=models.Sum('amount'))['s'] or 0
+    def total_per_item(self):
+        return self.price + self.cost_per_item
+
+    def price_per_items(self, items):
+        return self.cost_const + items * self.total_per_item()
 
 
 class Order(models.Model):
 
 
 class Order(models.Model):
@@ -42,6 +46,7 @@ class Order(models.Model):
 
     """
     offer = models.ForeignKey(Offer, verbose_name=_('offer'))
 
     """
     offer = models.ForeignKey(Offer, verbose_name=_('offer'))
+    items = models.IntegerField(verbose_name=_('items'), default=1)
     name = models.CharField(_('name'), max_length=127, blank=True)
     email = models.EmailField(_('email'), db_index=True)
     address = models.TextField(_('address'), db_index=True)
     name = models.CharField(_('name'), max_length=127, blank=True)
     email = models.EmailField(_('email'), db_index=True)
     address = models.TextField(_('address'), db_index=True)
@@ -54,11 +59,14 @@ class Order(models.Model):
         ordering = ['-payed_at']
 
     def __unicode__(self):
         ordering = ['-payed_at']
 
     def __unicode__(self):
-        return unicode(self.offer)
+        return "%s (%d egz.)" % (unicode(self.offer), self.items)
 
     def get_absolute_url(self):
         return self.offer.get_absolute_url()
 
 
     def get_absolute_url(self):
         return self.offer.get_absolute_url()
 
+    def amount(self):
+        return self.offer.price_per_items(self.items)
+
     def notify(self, subject, template_name, extra_context=None):
         context = {
             'order': self,
     def notify(self, subject, template_name, extra_context=None):
         context = {
             'order': self,
@@ -74,13 +82,23 @@ class Order(models.Model):
                 fail_silently=False
             )
 
                 fail_silently=False
             )
 
+    def notify_managers(self, subject, template_name, extra_context=None):
+        context = {
+            'order': self,
+            'site': Site.objects.get_current(),
+        }
+        if extra_context:
+            context.update(extra_context)
+        with override(app_settings.DEFAULT_LANGUAGE):
+            mail_managers(subject, render_to_string(template_name, context))
+
 # Register the Order model with django-getpaid for payments.
 getpaid.register_to_payment(Order, unique=False, related_name='payment')
 
 
 def new_payment_query_listener(sender, order=None, payment=None, **kwargs):
     """ Set payment details for getpaid. """
 # Register the Order model with django-getpaid for payments.
 getpaid.register_to_payment(Order, unique=False, related_name='payment')
 
 
 def new_payment_query_listener(sender, order=None, payment=None, **kwargs):
     """ Set payment details for getpaid. """
-    payment.amount = order.offer.price
+    payment.amount = order.amount()
     payment.currency = 'PLN'
 getpaid.signals.new_payment_query.connect(new_payment_query_listener)
 
     payment.currency = 'PLN'
 getpaid.signals.new_payment_query.connect(new_payment_query_listener)
 
@@ -99,4 +117,8 @@ def payment_status_changed_listener(sender, instance, old_status, new_status, **
             _('Your payment has been completed.'),
             'shop/email/payed.txt'
         )
             _('Your payment has been completed.'),
             'shop/email/payed.txt'
         )
+        instance.order.notify_managers(
+            _('New order has been placed.'),
+            'shop/email/payed_managers.txt'
+        )
 getpaid.signals.payment_status_changed.connect(payment_status_changed_listener)
 getpaid.signals.payment_status_changed.connect(payment_status_changed_listener)
diff --git a/shop/static/shop/shop.js b/shop/static/shop/shop.js
new file mode 100755 (executable)
index 0000000..96b9f9d
--- /dev/null
@@ -0,0 +1,31 @@
+$(function() {
+
+    $('.cost-items').each(function() {
+
+        var $items = $(this);
+        var price = parseFloat($items.attr('data-cost-price')) * 100;
+        var cost_const = parseFloat($items.attr('data-cost-const')) * 100;
+        var cost_per_item = parseFloat($items.attr('data-cost-per-item')) * 100;
+
+        var decimal_separator = $items.attr('data-decimal-separator');
+
+        var money = function(amount) {
+            return amount.toFixed(2).replace(".", decimal_separator);
+        }
+
+        var update_costs = function() {
+            var items = $items.val();
+            if (items < 1)
+                items = 1;
+            var total_costs = cost_per_item * items + cost_const;
+            var final = price * items + total_costs;
+            $("#cost-costs").text(money(total_costs / 100) + " zł");
+            $("#cost-final").text(money(final / 100) + " zł");
+        }
+
+        $items.change(update_costs);
+        update_costs();
+
+    });
+
+});
index 5cf1b9a..a0fd074 100755 (executable)
@@ -1,5 +1,5 @@
-{% autoescape off %}{% load i18n %}{% trans 'Hi' %}{{ order.name }}{% endif %},
+{% autoescape off %}{% load i18n %}{% trans 'Hi' %} {{ order.name }},
 {% block body %}
 {% endblock %}
 {% blocktrans %}Cheers,
 {% block body %}
 {% endblock %}
 {% blocktrans %}Cheers,
-Right to Culture team{% endblocktrans %}
+Right to Culture team{% endblocktrans %}{% endautoescape %}
index 4697ea9..387b6ba 100644 (file)
@@ -4,4 +4,11 @@
 
 {% block body %}
 {% blocktrans %}Your payment has been successfully completed.{% endblocktrans %}
 
 {% block body %}
 {% blocktrans %}Your payment has been successfully completed.{% endblocktrans %}
+
+{% blocktrans %}Your order is now being processed: {% endblocktrans %}
+
+{{ order }}
+{{ order.name }}
+{{ order.address }}
+
 {% endblock %}
 {% endblock %}
diff --git a/shop/templates/shop/email/payed_managers.txt b/shop/templates/shop/email/payed_managers.txt
new file mode 100755 (executable)
index 0000000..748d4d9
--- /dev/null
@@ -0,0 +1,18 @@
+{% extends "shop/email/base.txt" %}
+{% load i18n %}
+{% load url from future %}
+
+
+{% block body %}
+Następujące zamówienie zostało złożone i opłacone:
+
+{{ order }}
+
+{{ order.email }}
+{{ order.name }}
+
+{{ order.address }}
+
+http://{{ site.domain }}{% url 'admin:shop_order_change' order.pk %}
+
+{% endblock %}
index 007c28d..ad2d65e 100644 (file)
@@ -8,8 +8,11 @@
 
 <h1>{% trans "Payment failed" %}</h1>
 
 
 <h1>{% trans "Payment failed" %}</h1>
 
-<p>{% trans "You're support has not been processed successfully." %}</p>
+<div class="normal">
 
 
+<p>{% trans "You're support has not been processed successfully." %}</p>
 
 
+<p>{% trans "Go back to:" %} <a href="{{ object.order.get_absolute_url }}">{{ object.order.offer }}</a>.</p>
+</div>
 
 {% endblock %}
 
 {% endblock %}
index c513025..a1f00e9 100755 (executable)
@@ -1,13 +1,22 @@
-{% load i18n %}
+{% load i18n staticfiles %}
 {% load url from future %}
 
 {% load url from future %}
 
-<form action="{% url 'shop_buy' form.offer.entry.slug %}" method="post">
+<form class="submit-form" action="{% url 'shop_buy' form.offer.entry.slug %}" method="post">
     <table>
     <table>
-    {{ form.as_table }}
-    <tr><td></td><td>
-        <button type="submit" style="border: none; background: none; cursor: pointer">
-        <img alt="{% trans 'Donate!' %}" src="{% static 'img/payu.png' % }" />
-        </button>
+        {{ form.as_table }}
+
+        <tr><th>{% trans "Payment and shipping costs" %}:</th><td style="font-size:1.2em;" id="cost-costs">
+                {{ form.offer.cost_const|floatformat:"-2" }} zł
+                + {{ form.offer.cost_per_item|floatformat:"-2" }} zł {% trans "for each copy" %}
+            </td></tr>
+        <tr><th>{% trans "Final cost" %}:</th><td id="cost-final" style="font-size:1.5em;">
+                {{ form.offer.total_per_item|floatformat:"-2" }} zł {% trans "for each copy" %}
+                + {{ form.offer.cost_const|floatformat:"-2" }} zł
+            </td></tr>
+        <tr><td></td><td>
+            <button type="submit" style="border: none; background: none; cursor: pointer">
+            <img alt="{% trans 'Donate!' %}" src="{% static 'img/payu.png' %}" />
+            </button>
         </td></tr>
     </table>
 </form>
         </td></tr>
     </table>
 </form>
index fe21d98..c08447c 100644 (file)
@@ -8,6 +8,12 @@
 
 <h1>{% trans "Payment successful" %}</h1>
 
 
 <h1>{% trans "Payment successful" %}</h1>
 
+<div class="normal">
+
 <p>{% trans "Your payment has been successfully completed." %}</p>
 
 <p>{% trans "Your payment has been successfully completed." %}</p>
 
+<p>{% trans "Go back to:" %} <a href="{{ object.order.get_absolute_url }}">{{ object.order.offer }}</a>.</p>
+
+</div>
+
 {% endblock %}
 {% endblock %}
index 10c53ae..9ecf196 100755 (executable)
@@ -10,6 +10,6 @@ register = template.Library()
 
 @register.inclusion_tag('shop/snippets/order_form.html', takes_context=True)
 def order_form_for(context, offer, form=None):
 
 @register.inclusion_tag('shop/snippets/order_form.html', takes_context=True)
 def order_form_for(context, offer, form=None):
-    if form is None:
+    if not form:
         form = OrderForm(offer)
     return {'form': form}
         form = OrderForm(offer)
     return {'form': form}
index ff1c847..debea33 100644 (file)
@@ -4,13 +4,18 @@
 #
 from django.conf.urls import patterns, url, include
 from django.utils.translation import ugettext_lazy as _
 #
 from django.conf.urls import patterns, url, include
 from django.utils.translation import ugettext_lazy as _
-
+from django.views.decorators.csrf import csrf_exempt
+from getpaid.backends.payu.views import OnlineView
+from prawokultury.middleware import honeypot_exempt
 from .views import ThanksView, NoThanksView, OfferDetailView
 
 
 urlpatterns = patterns('',
     url(r'^kup/(?P<slug>[^/]+)/$', OfferDetailView.as_view(), name='shop_buy'),
 from .views import ThanksView, NoThanksView, OfferDetailView
 
 
 urlpatterns = patterns('',
     url(r'^kup/(?P<slug>[^/]+)/$', OfferDetailView.as_view(), name='shop_buy'),
-    url(r'^dziekujemy/$', ThanksView.as_view(), name='shop_thanks'),
-    url(r'^niepowodzenie/$', NoThanksView.as_view(), name='shop_nothanks'),
+    url(r'^dziekujemy/(?P<pk>\d+)/$', ThanksView.as_view(), name='shop_thanks'),
+    url(r'^niepowodzenie/(?P<pk>\d+)/$', NoThanksView.as_view(), name='shop_nothanks'),
+    url(r'^getpaid/getpaid.backends.payu/online/$', 
+            honeypot_exempt(csrf_exempt(OnlineView.as_view())),
+            name='getpaid-payu-online'),
     url(r'^getpaid/', include('getpaid.urls')),
 )
     url(r'^getpaid/', include('getpaid.urls')),
 )
index 16634e7..ef73d66 100644 (file)
@@ -30,16 +30,16 @@ class OfferDetailView(FormView):
             self.object = get_object_or_404(Offer, **args)
         return super(OfferDetailView, self).dispatch(request, slug)
 
             self.object = get_object_or_404(Offer, **args)
         return super(OfferDetailView, self).dispatch(request, slug)
 
+    def get(self, *args, **kwargs):
+        return redirect(self.object.get_absolute_url())
+
     def get_context_data(self, *args, **kwargs):
         ctx = super(OfferDetailView, self).get_context_data(*args, **kwargs)
         ctx['entry'] = self.object.entry
         return ctx
 
     def get_form(self, form_class):
     def get_context_data(self, *args, **kwargs):
         ctx = super(OfferDetailView, self).get_context_data(*args, **kwargs)
         ctx['entry'] = self.object.entry
         return ctx
 
     def get_form(self, form_class):
-        if self.request.method == 'POST':
-            return form_class(self.object, self.request.POST)
-        else:
-            return form_class(self.object)
+        return form_class(self.object, self.request.POST)
 
     def form_valid(self, form):
         order = form.save()
 
     def form_valid(self, form):
         order = form.save()
@@ -50,9 +50,11 @@ class OfferDetailView(FormView):
         return redirect(gateway_url_tuple[0])
 
 
         return redirect(gateway_url_tuple[0])
 
 
-class ThanksView(TemplateView):
+class ThanksView(DetailView):
+    model = Payment
     template_name = "shop/thanks.html"
 
 
     template_name = "shop/thanks.html"
 
 
-class NoThanksView(TemplateView):
+class NoThanksView(DetailView):
+    model = Payment
     template_name = "shop/no_thanks.html"
     template_name = "shop/no_thanks.html"
diff --git a/shop/widgets.py b/shop/widgets.py
new file mode 100755 (executable)
index 0000000..5690be3
--- /dev/null
@@ -0,0 +1,4 @@
+from django.forms.widgets import TextInput
+
+class NumberInput(TextInput):
+    input_type = 'number'