Commits

Kai Diefenbach committed 3904451

First working version of vouchers

  • Participants
  • Parent commits 1f57c7c

Comments (0)

Files changed (25)

lfs/checkout/views.py

 from lfs.payment.settings import PAYPAL
 from lfs.payment.settings import DIRECT_DEBIT
 from lfs.payment.settings import CREDIT_CARD
+from lfs.voucher.models import Voucher
 
 def login(request, template_name="lfs/checkout/login.html"):
     """Displays a form to login or register/login the user within the check out
     selected_payment_method = lfs.payment.utils.get_selected_payment_method(request)
     payment_costs = lfs.payment.utils.get_payment_costs(request, selected_payment_method)
 
-    # cart costs
+    # Cart costs
     cart_costs = cart_utils.get_cart_costs(request, cart)
     cart_price = cart_costs["price"] + shipping_costs["price"] + payment_costs["price"]
     cart_tax = cart_costs["tax"] + shipping_costs["tax"] + payment_costs["tax"]
 
+    # Voucer
+    try:
+        voucher = Voucher.objects.get(number=request.POST.get("voucher"))
+    except Voucher.DoesNotExist:
+        display_voucher = False
+        voucher_value = 0
+        voucher_tax = 0
+    else:
+        cart_price = cart_price - voucher.get_price_gross()
+        display_voucher = True
+        voucher_value = voucher.get_price_gross(cart)
+        voucher_tax = voucher.get_tax(cart)
+        voucher.mark_as_used()
+
     return render_to_string(template_name, RequestContext(request, {
         "cart" : cart,
         "cart_price" : cart_price,
         "cart_tax" : cart_tax,
+        "display_voucher" : display_voucher,
+        "voucher_value" : voucher_value,
+        "voucher_tax" : voucher_tax,
         "shipping_price" : shipping_costs["price"],
         "payment_price" : payment_costs["price"],
         "selected_shipping_method" : selected_shipping_method,
                     selected_shipping_address.country_id = form.cleaned_data.get("shipping_country")
                     selected_shipping_address.phone = form.cleaned_data.get("shipping_phone")
                     selected_shipping_address.save()
-                    
+
             # Payment method
             customer.selected_payment_method_id = request.POST.get("payment_method")
 
             else:
                 if result.has_key("message"):
                     form._errors[result.get("message-key")] = result.get("message")
-        
+
         else: # form is not valid
             # Create or update invoice address
             if customer.selected_invoice_address is None:
                     selected_shipping_address.country_id = form.data.get("shipping_country")
                     selected_shipping_address.phone = form.data.get("shipping_phone")
                     selected_shipping_address.save()
-                    
+
             # Payment method
             customer.selected_payment_method_id = request.POST.get("payment_method")
 
     Factored out to be reusable for the starting request (which renders the
     whole checkout page and subsequent ajax requests which refresh the
     selectable payment methods.
-    
+
     Passing the form to be able to display payment forms within the several
     payment methods, e.g. credit card form.
     """
         "selected_shipping_method" : selected_shipping_method,
     }))
 
+def check_voucher(request):
+    """
+    """
+    result = simplejson.dumps({
+        "html" : (("#cart-inline", cart_inline(request)),)
+    })
+
+    return HttpResponse(result)
+
 def changed_checkout(request):
     """
     """
     customer = customer_utils.get_or_create_customer(request)
     _save_customer(request, customer)
     _save_country(request, customer)
-    
+
     result = simplejson.dumps({
         "shipping" : shipping_inline(request),
         "payment" : payment_inline(request, form),

lfs/core/tests.py

 from lfs.page.tests import *
 from lfs.search.tests import *
 from lfs.shipping.tests import *
+from lfs.voucher.tests import *
 
 # django imports
 from django.contrib.auth.models import User
     url(r'^checkout', "one_page_checkout", name="lfs_checkout"),
     url(r'^thank-you', "thank_you",name="lfs_thank_you"),
     url(r'^changed-checkout/$', "changed_checkout", name="lfs_changed_checkout"),
+    url(r'^check-voucher/$', "check_voucher", name="lfs_check_voucher"),    
 )
 
 # Customer

lfs/core/utils.py

 
 # django imports
 from django.http import HttpResponseRedirect
+from django.http import HttpResponse
 from django.utils import simplejson
 from django.utils.functional import Promise
 from django.utils.encoding import force_unicode
 
     return current_categories
 
+def render_to_ajax_response(html=[], message=None):
+    """Encodes given html and message to JSON and returns a HTTP response.
+    """
+    result = simplejson.dumps(
+        { "message" : message, "html" : html }, cls = LazyEncoder)
+
+    return HttpResponse(result)
+    
 def set_category_levels():
     """Creates category levels based on the position in hierarchy.
     """

lfs/manage/urls.py

     url(r'^send-rating-mails$', "rating_mails.send_rating_mails", name="lfs_send_rating_mails"),
 )
 
+# Voucher
+urlpatterns += patterns('lfs.manage.views.voucher',
+    url(r'^manage-vouchers$', "manage_vouchers", name="lfs_manage_vouchers"),
+    url(r'^add-voucher-group$', "add_voucher_group", name="lfs_manage_add_voucher_group"),
+    url(r'^manage-voucher-group/(?P<id>\d+)$', "voucher_group", name="lfs_manage_voucher_group"),
+    url(r'^save-voucher-group-data/(?P<id>\d+)$', "save_voucher_group_data", name="lfs_manage_save_voucher_group_data"),
+    url(r'^add-vouchers/(?P<group_id>\d+)$', "add_vouchers", name="lfs_manage_add_vouchers"),
+    url(r'^delete-vouchers/(?P<group_id>\d+)$', "delete_vouchers", name="lfs_manage_delete_vouchers"),
+)
+
 # Portlets
 urlpatterns += patterns('lfs.manage.views.lfs_portlets',
     url(r'^add-portlet/(?P<object_type_id>\d+)/(?P<object_id>\d+)$', "add_portlet", name="lfs_add_portlet"),

lfs/manage/views/voucher.py

+# django imports
+from django.contrib.auth.decorators import permission_required
+from django.core.urlresolvers import reverse
+from django.db import IntegrityError
+from django.http import HttpResponse
+from django.http import HttpResponseRedirect
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.template.loader import render_to_string
+from django.utils.translation import ugettext_lazy as _
+from django.utils import simplejson
+
+# lfs imports
+import lfs.voucher.utils
+from lfs.core.utils import LazyEncoder
+from lfs.core.utils import render_to_ajax_response
+from lfs.tax.models import Tax
+from lfs.voucher.forms import VoucherGroupForm
+from lfs.voucher.models import Voucher
+from lfs.voucher.models import VoucherGroup
+
+# Parts
+def voucher_group(request, id, template_name="manage/voucher/voucher_group.html"):
+    """Main view to display a voucher group.
+    """
+    voucher_group = VoucherGroup.objects.get(pk=id)
+
+    return render_to_response(template_name, RequestContext(request, {
+        "voucher_group"  : voucher_group,
+        "data_tab" : data_tab(request, voucher_group),
+        "vouchers_tab" : vouchers_tab(request, voucher_group),
+        "navigation" : navigation(request, voucher_group),
+    }))
+
+def navigation(request, voucher_group, template_name="manage/voucher/navigation.html"):
+    """Displays the navigation.
+    """
+    return render_to_string(template_name, RequestContext(request, {
+        "voucher_group" : voucher_group,
+        "voucher_groups" : VoucherGroup.objects.all(),
+    }))
+
+def data_tab(request, voucher_group, template_name="manage/voucher/data.html"):
+    """Displays the data tab of the passed voucher group.
+    """
+    if request.method == "POST":
+        form = VoucherGroupForm(instance=voucher_group, data=request.POST)
+        if form.is_valid():
+            form = VoucherGroupForm(instance=voucher_group)
+    else:
+        form = VoucherGroupForm(instance=voucher_group)
+
+    return render_to_string(template_name, RequestContext(request, {
+        "voucher_group" : voucher_group,
+        "form" : form,
+    }))
+
+def vouchers_tab(request, voucher_group, template_name="manage/voucher/vouchers.html"):
+    """Displays the vouchers tab
+    """
+    vouchers = voucher_group.vouchers.all()
+    taxes = Tax.objects.all()
+    
+    return render_to_string(template_name, RequestContext(request, {
+        "voucher_group" : voucher_group,
+        "vouchers" : vouchers,
+        "taxes" : taxes,
+    }))
+
+# Actions
+def manage_vouchers(request):
+    """Redirects to the first voucher group or to the add voucher form.
+    """
+    try:
+        voucher_group = VoucherGroup.objects.all()[0]
+    except IndexError:
+        url = reverse("lfs_manage_add_voucher_group")
+    else:
+        url = reverse("lfs_manage_voucher_group", kwargs={ "id" : voucher_group.id })
+
+    return HttpResponseRedirect(url)
+
+def add_vouchers(request, group_id):
+    """
+    """
+    voucher_group = VoucherGroup.objects.get(pk=group_id)
+
+    try:
+        amount = int(request.POST.get("amount", 0))
+    except TypeError:
+        amount = 0
+
+    for i in range(0, amount):
+        while 1:
+            try:
+                Voucher.objects.create(
+                    number = lfs.voucher.utils.create_voucher_number(),
+                    group = voucher_group,
+                    creator = request.user,
+                    kind_of = request.POST.get("kind_of", 0),
+                    value = request.POST.get("value", 0.0),
+                    expiration_date = request.POST.get("expiration_date"),
+                    tax_id = request.POST.get("tax_id")
+                )
+                break
+            except IntegrityError:
+                pass
+
+    return render_to_ajax_response(
+        (("#vouchers", vouchers_tab(request, voucher_group)), ),
+        _(u"Vouchers have been created."))
+
+def delete_vouchers(request, group_id):
+    """Deletes checked vouchers.
+    """
+    voucher_group = VoucherGroup.objects.get(pk=group_id)
+    vouchers = Voucher.objects.filter(pk__in=request.POST.getlist("voucher-ids"))
+
+    for voucher in vouchers:
+        voucher.delete()
+
+    return render_to_ajax_response(
+        (("#vouchers", vouchers_tab(request, voucher_group)), ),
+        _(u"Vouchers have been deleted."))
+
+def add_voucher_group(request, template_name="manage/voucher/add_voucher_group.html"):
+    """Adds a voucher group
+    """
+    if request.method == "POST":
+        form = VoucherGroupForm(data=request.POST)
+        if form.is_valid():
+            voucher_group = form.save(commit=False)
+            voucher_group.creator = request.user
+            voucher_group.save()
+            url = reverse("lfs_manage_voucher_group", kwargs={"id" : voucher_group.id })
+            return HttpResponseRedirect(url)
+    else:
+        form = VoucherGroupForm()
+
+    return render_to_response(template_name, RequestContext(request, {
+        "form" : form,
+        "voucher_groups" : VoucherGroup.objects.all(),
+    }))
+
+def save_voucher_group_data(request, id):
+    """Saves the data of the voucher group with passed id.
+    """
+    voucher_group = VoucherGroup.objects.get(pk=id)
+    form = VoucherGroupForm(instance=voucher_group, data=request.POST)
+    if form.is_valid():
+        voucher_group = form.save()
+
+    _update_positions()
+    voucher_group = VoucherGroup.objects.get(pk=voucher_group.id)
+
+    return render_to_ajax_response(
+        (("#data_tab", data_tab(request, voucher_group)),
+        ("#navigation", navigation(request, voucher_group)),),
+        _(u"Voucher data has been save."))
+
+def _update_positions():
+    for i, voucher_group in enumerate(VoucherGroup.objects.all()):
+        voucher_group.position = (i+1)*10
+        voucher_group.save()

lfs/marketing/tests.py

         """
         self.assertEqual(self.t1.position, 1)
         
-class UtilsTestCase(TestCase):
+class TopsellerUtilsTestCase(TestCase):
     """Tests the utils of the lfs.marketing
     """
     def setUp(self):

lfs/order/models.py

 
 class Order(models.Model):
     """An order is created when products have been sold.
+
+    Parameters:
+    ===========
+
+    - voucher_number, voucher_value, voucher_tax
+    
+        Storing this information here assures that we have it all time, even 
+        when the involved voucher will be deleted.
+
     """
     user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True)
     session = models.CharField(_(u"Session"), blank=True, max_length=100)
     bank_name = models.CharField(_(u"Bank name"), blank=True, max_length=100)
     depositor = models.CharField(_(u"Depositor"), blank=True, max_length=100)
 
+    voucher_number = models.CharField(_(u"Voucher number"), blank=True, max_length=100)
+    voucher_price = models.FloatField(_(u"Voucher value"), default=0.0)
+    voucher_tax = models.FloatField(_(u"Voucher tax"), default=0.0)
+
     message = models.TextField(_(u"Message"), blank=True)
 
     uuid = models.CharField(max_length=50, editable=False,unique=True, default=get_unique_id_str)

lfs/order/utils.py

-# django imports
-from django.core.urlresolvers import reverse
-
 # lfs imports
 from lfs.cart import utils as cart_utils
 from lfs.core.signals import order_submitted
 from lfs.order.models import OrderItem
 from lfs.payment import utils as payment_utils
 from lfs.shipping import utils as shipping_utils
+from lfs.voucher.models import Voucher
 
 def add_order(request):
-    """Adds an order based on current cart for the current customer. 
-    
-    It assumes that the customer is prepared with all needed information. This 
+    """Adds an order based on current cart for the current customer.
+
+    It assumes that the customer is prepared with all needed information. This
     is within the responsibility of the checkout form.
     """
     customer = customer_utils.get_customer(request)
         shipping_address = customer.selected_shipping_address
     else:
         shipping_address = customer.selected_invoice_address
-    
+
     cart = cart_utils.get_cart(request)
     if cart is None:
         return order
     cart_costs = cart_utils.get_cart_costs(request, cart, total=False)
-    
+
     shipping_method = shipping_utils.get_selected_shipping_method(request)
     shipping_costs = shipping_utils.get_shipping_costs(request, shipping_method)
 
     payment_method = payment_utils.get_selected_payment_method(request)
     payment_costs = payment_utils.get_payment_costs(request, payment_method)
 
-    # Set email dependend on login state. An anonymous customer doesn't  have a 
-    # django user account, so we set the name of the invoice address to the 
+    # Set email dependend on login state. An anonymous customer doesn't  have a
+    # django user account, so we set the name of the invoice address to the
     # customer name.
-    
+
     # Note: After this has been processed the order's customer email has an
     # email in any case. That means you can use it to send emails to the
     # customer.
     else:
         user = None
         customer_email = invoice_address.email
-        
-    # Calculate the totals    
+
+    # Calculate the totals
     price = cart_costs["price"] + shipping_costs["price"] + payment_costs["price"]
     tax = cart_costs["tax"] + shipping_costs["tax"] + payment_costs["tax"]
-    
+
+    # Add voucher if one exists
+    try:
+        voucher = Voucher.objects.get(number=request.POST.get("voucher"))
+    except Voucher.DoesNotExist:
+        voucher = None
+    else:
+        voucher_number = voucher.number
+        voucher_price = voucher.get_price_gross(cart)
+        voucher_tax = voucher.get_tax(cart)
+
+        price -= voucher_price
+        tax -= voucher_tax
+
     order = Order.objects.create(
         user = user,
         session = request.session.session_key,
         customer_firstname = invoice_address.firstname,
         customer_lastname = invoice_address.lastname,
         customer_email = customer_email,
-                
+
         shipping_method = shipping_method,
         shipping_price = shipping_costs["price"],
         shipping_tax = shipping_costs["tax"],
 
         message = request.POST.get("message", ""),
     )
-        
+
+    if voucher:
+        order.voucher_number = voucher_number
+        order.voucher_price = voucher_price
+        order.voucher_tax = voucher_tax
+        order.save()
+
     # Copy bank account if one exists
     if customer.selected_bank_account:
         bank_account = customer.selected_bank_account
         order.bank_identification_code = bank_account.bank_identification_code
         order.bank_name = bank_account.bank_name
         order.depositor = bank_account.depositor
-    
+
     order.save()
-    
+
     # Copy cart items
-    for cart_item in cart.cartitem_set.all():        
+    for cart_item in cart.cartitem_set.all():
         OrderItem.objects.create(
             order=order,
-            
+
             price_net = cart_item.get_price_net(),
             price_gross = cart_item.get_price_gross(),
             tax = cart_item.get_tax(),
             product = cart_item.product,
             product_sku = cart_item.product.sku,
             product_name = cart_item.product.get_name(),
-            product_amount=cart_item.amount,                        
+            product_amount=cart_item.amount,
             product_price_net = cart_item.product.get_price_net(),
             product_price_gross = cart_item.product.get_price_gross(),
             product_tax = cart_item.product.get_tax(),
         )
-        
+
         cart_item.product.decrease_stock_amount(cart_item.amount)
-        
+
     cart.delete()
     order_submitted.send(order)
-    
+
     # Note: Save order for later use in thank you page. The order will be
     # removed from the session if the thank you page has been called.
     request.session["order"] = order
-    
+
     return order
 
-                               

lfs/static/css/lfs.manage.css

     background-color: #ede9e8;
 }
 
+table.lfs-manage-table td {
+    vertical-align: middle;
+}
+
+.checkbox {
+    min-height: 20px;
+    height: 20px;
+}
+
 .padding-top {
     margin-top: 10px;
 }
 
 .padding-top-large {
-    margin-top: 20px;    
+    margin-top: 20px;
 }
 
 .middle {
 }
 
 td.middle-slot {
-  padding-top: 10px;    
+  padding-top: 10px;
 }
 
 th, td {
 }
 
 tr.marked {
-    background-color: yellow;    
+    background-color: yellow;
 }
 
 /* General formats */
     float: left;
 }
 
-.manage-products .navigation-pages, 
+.manage-products .navigation-pages,
 .manage-product .navigation-pages {
     float: left;
 }
 }
 
 #manage-categories-categories-control {
-    font-size: 9px;    
+    font-size: 9px;
     padding: 2px 0 10px 0;
 }
 

lfs/static/js/lfs.js

         update_checkout()
     });
 
+    var update_html = function(data) {
+        data = JSON.parse(data);
+        for (var html in data["html"])
+            $(data["html"][html][0]).html(data["html"][html][1]);
+
+        if (data["message"]) {
+            $.jGrowl(data["message"]);
+        }
+    }
+
+    $("#voucher").livequery("change", function() {
+        var url = $(this).attr("data");
+        var voucher = $(this).attr("value");
+        $.post(url, { "voucher" : voucher }, function(data) {
+            update_html(data);
+        });
+    });
 })

lfs/templates/manage/manage_base.html

                                 <ul>
                                     <li><a href="{% url lfs_manage_marketing %}">{% trans 'Topseller' %}</a></li>
                                     <li><a href="{% url lfs_manage_rating_mails %}">{% trans 'Rating Mails' %}</a></li>
+                                    <li><a href="{% url lfs_manage_vouchers %}">{% trans 'Vouchers' %}</a></li>
                                 </ul>
                             </li>
                             <li><a>{% trans 'Utils' %}</a>

lfs/templates/manage/order/order_inline.html

             </td>
         </tr>
     {% endfor %}
+    {% if current_order.voucher_number %}
+        <tr>
+            <td></td>
+            <td>
+                {% trans 'Voucher' %} ({{ current_order.voucher_number }})
+            </td>
+            <td class="number">
+                1
+            </td>
+            <td class="number">
+                - {{ current_order.voucher_price|currency }}
+            </td>
+            <td></td>
+            <td class="number">
+                - {{ current_order.voucher_price|currency }}
+            </td>
+        </tr>        
+    {% endif %}
     <tr>
         <td></td>
         <td>

lfs/templates/manage/voucher/add_voucher_group.html

+{% extends "manage/manage_base.html" %}
+{% load i18n %}
+
+{% block left_slot %}
+    {% for voucher_group in voucher_groups %}
+        <div>
+            <a href="{% url lfs_manage_voucher_group voucher_group.id %}"
+               class="selectable {% ifequal page.id current_id %}selected{% endifequal %}">
+                {{ voucher_group.name }}
+            </a>
+        </div>
+    {% endfor %}
+{% endblock %}
+
+{% block content %}
+    <div id="manage-tabs">
+        <ul>
+            <li class="ui-tabs-nav-item"><a href="#data">{% trans 'Add voucher group' %}</a></li>            
+        </ul>
+
+        <div id="data">
+            <h2>{% trans 'Add voucher group' %}</h2>
+
+            <form action="{% url lfs_manage_add_voucher_group %}"
+                  method="post"
+                  enctype="multipart/form-data">
+                {% include "manage/lfs_form.html" %}
+
+                <input type="submit"
+                       value="{% trans 'Add voucher group' %}" />
+                       
+                    {% if  request.META.HTTP_REFERER %}                       
+                        <input type="button"
+                               onclick="window.location='{{ request.META.HTTP_REFERER }}'"
+                               value="{% trans 'Cancel' %}" />
+                    {% endif %}
+            </form>
+        </div>
+    </div>    
+{% endblock %}

lfs/templates/manage/voucher/data.html

+{% load i18n %}
+<form action="{% url lfs_manage_save_voucher_group_data voucher_group.id %}"
+      method="post"
+      enctype="multipart/form-data">
+
+    {% include "manage/lfs_form.html" %}
+
+    <input type="submit"
+           class="ajax-save-button-2"
+           value="{% trans 'Save voucher group' %}">
+
+</form>

lfs/templates/manage/voucher/navigation.html

+{% for vg in voucher_groups %}
+    <div>
+        <a href="{% url lfs_manage_voucher_group vg.id %}"
+           class="selectable {% ifequal vg.id voucher_group.id %}selected{% endifequal %}">
+            {{ vg.name }}
+        </a>
+    </div>
+{% endfor %}

lfs/templates/manage/voucher/voucher_group.html

+{% extends "manage/manage_base.html" %}
+{% load i18n %}
+
+{% block left_slot %}
+    <div id="navigation"
+         style="padding-top:20px">
+        {{ navigation|safe }}
+    </div>
+{% endblock %}
+
+{% block content %}
+
+    <div class="site-actions">
+        <span class="label">{{ voucher_group.name }}</span>
+        
+        <a class="add" 
+           href="{% url lfs_manage_add_voucher_group %}">{% trans 'Add voucher group' %}</a>
+        
+        <a class="delete confirmation-link"
+           data="{% trans 'Delete?' %}"
+           href="">{% trans 'Delete group' %}</a>
+
+    </div>
+
+    <div id="manage-tabs">
+
+        <ul>
+            <li class="ui-tabs-nav-item"><a href="#data">{% trans 'Data' %}</a></li>
+            <li class="ui-tabs-nav-item"><a href="#vouchers">{% trans 'Vouchers' %}</a></li>
+        </ul>
+
+        <div id="data">
+            {{ data_tab|safe }}
+        </div>
+        <div id="vouchers">
+            {{ vouchers_tab|safe }}
+        </div>
+    </div>    
+{% endblock %}

lfs/templates/manage/voucher/vouchers.html

+{% load i18n %}
+<form action="{% url lfs_manage_delete_vouchers voucher_group.id %}"
+      method="post">
+      
+    <table class="lfs-manage-table checkbox middle">
+        <tr>
+            <th>
+                <input type="checkbox" 
+                       class="select-all-1">            
+            </th>
+            <th>
+                {% trans "Number" %}
+            </th>
+            <th>
+                {% trans "Type" %}
+            </th>
+            <th class="number">
+                {% trans "Value" %}
+            </th>
+            <th class="number">
+                {% trans "Tax" %}
+            </th>
+            <th class="middle right">
+                {% trans "Expiration date" %}
+            </th>
+            <th class="small right">
+                {% trans "Active" %}
+            </th>
+            <th class="small right">
+                {% trans "Used" %}
+            </th>
+            <th class="middle right">
+                {% trans "Used date" %}
+            </th>
+        </tr>
+        {% for voucher in vouchers %}
+            <tr class="{% cycle 'even' 'odd' %}">
+                <td class="tiny checkbox">
+                    {% if not voucher.used %}
+                        <input type="checkbox" 
+                               class="select-1"
+                               name="voucher-ids"
+                               value="{{ voucher.id }}">
+                    {% endif %}
+                </td>
+                <td>
+                    {{ voucher.number }}
+                </td>
+                <td>
+                    {{ voucher.get_kind_of_display }}
+                </td>
+                <td class="number">
+                    {{ voucher.value }}
+                </td>
+                <td class="number">
+                    {% if voucher.tax %}
+                        {{ voucher.tax.rate }} %
+                    {% else %}
+                        ---
+                    {% endif %}
+                    
+                </td>
+                <td class="right">
+                    {% if voucher.expiration_date %}
+                        {{ voucher.expiration_date }}
+                    {% else %}
+                        ---
+                    {% endif %}                    
+                </td>
+                <td class="right">
+                    {{ voucher.active }}
+                </td>
+                <td class="right">
+                    {{ voucher.used }}
+                </td>
+                <td class="right">
+                    {{ voucher.used_date|date:_("DATE_FORMAT") }}
+                </td>
+            </tr>
+        {% endfor %}
+    </table>
+    
+    {% if vouchers %}
+        <input class="ajax-save-button-2"
+               type="submit"
+               value='{% trans "Delete vouchers" %}' />  
+    {% endif %}
+</form>
+
+<h2 style="padding-top:20px">{% trans "Add vouchers" %}</h2>
+<form action="{% url lfs_manage_add_vouchers voucher_group.id %}"
+      method="post">
+
+    <label>{% trans "Amount" %}</label>:
+    <input name="amount"
+           size="8"
+           type="text" />
+    
+    <select name="kind_of">
+        <option value="0">{% trans "Absolute" %}</option>
+        <option value="1">{% trans "Percentage" %}</option>
+    </select>
+
+    <label>{% trans "Value" %}</label>:
+    <input name="value"
+           size="8"
+           type="text" />
+
+    <label>{% trans "Tax" %}</label>:
+    <select name="tax_id">
+        <option value="">---</option>
+        {% for tax in taxes %}
+            <option value="{{ tax.id }}">{{ tax.rate }} %</option>
+        {% endfor %}
+    </select>
+
+    <label>{% trans "Expiration date" %}</label>:
+    <input name="expiration_date"
+           type="text" />
+
+           
+    <input class="ajax-save-button-2"
+           type="submit" 
+           value='{% trans "Add vouchers" %}'
+
+</form>
+

lfs/voucher/__init__.py

Empty file added.

lfs/voucher/forms.py

+# django imports
+from django.contrib.admin import widgets
+from django.forms import ModelForm
+
+# lfs imports
+from lfs.voucher.models import VoucherGroup
+
+class VoucherGroupForm(ModelForm):
+    """Form to add a VoucherGroup.
+    """
+    class Meta:
+        model = VoucherGroup
+        fields = ("name", "position")

lfs/voucher/models.py

+# python imports
+import datetime
+
+# django imports
+from django.contrib.auth.models import User
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+# lfs imports
+from lfs.tax.models import Tax
+from lfs.voucher.settings import KIND_OF_CHOICES
+from lfs.voucher.settings import ABSOLUTE
+from lfs.voucher.settings import PERCENTAGE
+
+class VoucherGroup(models.Model):
+    """Groups vouchers together.
+    """
+    name = models.CharField(max_length=100)
+    creator = models.ForeignKey(User)
+    creation_date = models.DateTimeField(auto_now_add=True)
+    position = models.PositiveSmallIntegerField(default=10)
+
+    class Meta:
+        ordering = ("position", )
+
+class Voucher(models.Model):
+    """A voucher.
+
+    Parameters:
+
+        - number
+            The unique number of the voucher. This number has to be provided
+            by the shop customer within the checkout in order to get the
+            credit.
+
+        - group
+            The group the voucher belongs to.
+
+        - creator
+            The creator of the voucher
+
+        - creation_date
+            The date the voucher has been created
+
+        - expiration_date
+            The date the voucher is going to expire. After that date the
+            voucher can't be used.
+
+        - kind_of
+            The kind of the voucher. Absolute or percentage.
+
+        - value
+            The value of the the voucher, which is interpreted either as an
+            absolute value in the current currency or a percentage quotation.
+
+        - tax
+            The tax of the voucher
+
+        - active
+            Only active vouchers can be redeemed.
+
+        - used
+            Indicates whether a voucher has already be used. Every voucher can
+            only used one time.
+
+        - used_date
+            The date the voucher has been redeemed.
+    """
+    number = models.CharField(max_length=100, unique=True)
+    group = models.ForeignKey(VoucherGroup, related_name="vouchers")
+    creator = models.ForeignKey(User)
+    creation_date = models.DateTimeField(auto_now_add=True)
+    expiration_date = models.DateField(blank=True, null=True)
+    kind_of = models.PositiveSmallIntegerField(choices=KIND_OF_CHOICES)
+    value = models.FloatField(default=0.0)
+    tax = models.ForeignKey(Tax, verbose_name=_(u"Tax"), blank=True, null=True)
+    active = models.BooleanField(default=True)
+    used = models.BooleanField(default=False)
+    used_date = models.DateTimeField(blank=True, null=True)
+
+    class Meta:
+        ordering = ("creation_date", "number")
+
+    def __unicode__(self):
+        return self.number
+
+    def get_price_net(self, cart=None):
+        """Returns the net price of the voucher.
+        """
+        if self.kind_of == ABSOLUTE:
+            return self.value - self.get_tax()
+        else:
+            raise NotImplemented
+
+    def get_price_gross(self, cart=None):
+        """Returns the gross price of the voucher.
+        """
+        if self.kind_of == ABSOLUTE:
+            return self.value
+        else:
+            raise NotImplemented
+
+    def get_tax(self, cart=None):
+        """Returns the absolute tax of the voucher
+        """
+        if self.tax:
+            if self.kind_of == ABSOLUTE:
+                return (self.tax.rate / (100 + self.tax.rate)) * self.value
+            else:
+                raise NotImplemented
+        else:
+            return 0.0
+
+    def mark_as_used(self):
+        """Mark voucher as used.
+        """
+        self.used = True
+        self.used_date = datetime.datetime.now()
+        self.save()
+
+    def is_absolute(self):
+        """Returns True if voucher is absolute.
+        """
+        return self.kind_of == ABSOLUTE
+
+    def is_percentage(self):
+        """Returns True if voucher is percentage.
+        """
+        return self.kind_of == PERCENTAGE

lfs/voucher/settings.py

+from django.utils.translation import gettext_lazy as _
+
+ABSOLUTE = 0
+PERCENTAGE = 1
+
+KIND_OF_CHOICES = (
+    (ABSOLUTE, _(u"Absolute")),
+    # (PERCENTAGE, _(u"Percentage")),
+)

lfs/voucher/tests.py

+# django imports
+from django.contrib.auth.models import User
+from django.contrib.sessions.backends.file import SessionStore
+from django.core.urlresolvers import reverse
+from django.test import TestCase
+
+# lfs imports
+from lfs.tests.utils import RequestFactory
+from lfs.tax.models import Tax
+from lfs.voucher.models import Voucher
+from lfs.voucher.models import VoucherGroup
+from lfs.voucher.settings import ABSOLUTE
+from lfs.voucher.settings import PERCENTAGE
+
+class VoucherUtilsTestCase(TestCase):
+    """
+    """
+    def setUp(self):
+        """
+        """
+        pass
+
+    def test_create_vouchers(self):
+        """
+        """
+        pass
+
+class VoucherTestCase(TestCase):
+    """
+    """
+    def setUp(self):
+        """
+        """
+        self.request = RequestFactory().get("/")
+        self.request.session = SessionStore()
+        self.request.user = User(id=1)
+
+        self.vg = VoucherGroup.objects.create(
+            name="xmas",
+            creator = self.request.user,
+        )
+
+        self.v1 = Voucher.objects.create(
+            number = "AAAA",
+            group = self.vg,
+            creator = self.request.user,
+            expiration_date = "2009-12-31",
+            kind_of = ABSOLUTE,
+            value = 10.0,
+        )
+
+    def test_defaults(self):
+        """
+        """
+        self.assertEqual(self.v1.number, "AAAA")
+        self.assertEqual(self.v1.group, self.vg)
+        self.assertEqual(self.v1.creator, self.request.user)
+        self.assertEqual(self.v1.expiration_date, "2009-12-31")
+        self.assertEqual(self.v1.kind_of, ABSOLUTE)
+        self.assertEqual(self.v1.active, True)
+        self.assertEqual(self.v1.used, False)
+        self.assertEqual(self.v1.used_date, None)
+        self.assertEqual(self.v1.value, 10.0)
+        self.assertEqual(self.v1.tax, None)
+
+    def test_prices(self):
+        """
+        """
+        # Absolute without tax
+        price_net = self.v1.get_price_net()
+        self.assertEqual(price_net, 10)
+
+        price_gross = self.v1.get_price_gross()
+        self.assertEqual(price_gross, 10)
+
+        tax = self.v1.get_tax()
+        self.assertEqual(tax, 0.0)
+
+        # Absolute with tax
+        self.v1.tax = Tax.objects.create(rate=19.0)
+        self.v1.save()
+
+        price_net = self.v1.get_price_net()
+        self.assertEqual("%.2f" % price_net, "%.2f" % 8.4)
+
+        price_gross = self.v1.get_price_gross()
+        self.assertEqual(price_gross, 10)
+
+        tax = self.v1.get_tax()
+        self.assertEqual("%.2f" % tax, "%.2f" % 1.6)
+
+    def test_kind_of(self):
+        """
+        """
+        self.assertEqual(self.v1.kind_of, ABSOLUTE)
+        self.assertEqual(self.v1.is_absolute(), True)
+        self.assertEqual(self.v1.is_percentage(), False)
+
+        self.v1.kind_of = PERCENTAGE
+        self.v1.save()
+
+        self.assertEqual(self.v1.kind_of, PERCENTAGE)
+        self.assertEqual(self.v1.is_absolute(), False)
+        self.assertEqual(self.v1.is_percentage(), True)
+
+    def test_mark_as_used(self):
+        """
+        """
+        self.assertEqual(self.v1.used, False)
+        self.assertEqual(self.v1.used_date, None)
+
+        self.v1.mark_as_used()
+
+        self.assertEqual(self.v1.used, True)
+        self.failIf(self.v1.used_date is None)

lfs/voucher/utils.py

+# python imports
+import random
+import datetime
+
+# lfs imports
+from lfs.voucher.models import Voucher
+
+def create_voucher_number():
+    """
+    """
+    letters = "ABCDEFGHIJKLMNOPQRSTUVXYZ"
+
+    number = ""
+    for i in range(0, 20):
+        number += random.choice(letters)
+
+    return number

lfs/voucher/views.py

+# django imports
+from django.contrib.auth.decorators import permission_required
+from django.core.urlresolvers import reverse
+from django.forms import ModelForm
+from django.http import HttpResponseRedirect
+from django.shortcuts import render_to_response
+from django.shortcuts import get_object_or_404
+from django.template import RequestContext
+from django.utils.translation import ugettext_lazy as _
+
+from lfs.voucher.models import VoucherGroup
+
+def manage_vouchers(request):
+    """
+    """
+    try:
+        voucher_group = VoucherGroup.objects.all()[0]
+    except IndexError:
+        url = reverse("lfs_manage_add_voucher_group")
+    else:
+        url = reverse("lfs_manage_voucher_group", kwargs={ "id" : voucher_group.id })
+    
+    return HttpResponseRedirect(url)
+    
+def add_voucher_group(request, template_name="manage/voucher/add_voucher_group.html"):
+    """Adds a voucher group
+    """
+    if request.method == "POST":
+        form = VoucherGroupForm(data=request.POST)
+        if form.is_valid():
+            voucher = form.save()
+    else:
+        form = VoucherGroupForm()
+    
+    return render_to_response(template_name, RequestContext(request, {
+        "form" : form,
+        "voucher_groups" : VoucherGroup.objects.all(),
+    }))
+
+def manage_voucher_group(request, id, template_name="manage/voucher/voucher_group.html"):
+    """
+    """
+    voucher_group = VoucherGroup.objects.get(pk=id)    
+
+    return render_to_response(template_name, RequestContext(request, {
+        "form" : VoucherGroupForm(instance=voucher_group),
+        "voucher_groups" : VoucherGroup.objects.all(),
+    }))