Add the ability to add refunds

This commit is contained in:
Gabriel Augendre 2016-06-04 03:44:41 +02:00
parent 343bc1fd84
commit b8e7309ee2
No known key found for this signature in database
GPG key ID: D2B6A5B41FC438B1
11 changed files with 130 additions and 21 deletions

View file

@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from refunding.forms import RefundForm from refunding.forms import RefundForm, RefundFormAdmin
from refunding.models import Refund, Payment from refunding.models import Refund, Payment
@ -9,7 +9,7 @@ class RefundAdmin(admin.ModelAdmin):
list_display_links = ('title',) list_display_links = ('title',)
search_fields = ('title',) search_fields = ('title',)
date_hierarchy = 'date' date_hierarchy = 'date'
form = RefundForm form = RefundFormAdmin
readonly_fields = ('eur_value',) readonly_fields = ('eur_value',)

View file

@ -1,4 +1,6 @@
from bootstrap3_datetime.widgets import DateTimePicker
from django import forms from django import forms
from django.db.models import Q
from refunding.models import Refund, Payment from refunding.models import Refund, Payment
@ -7,18 +9,39 @@ class RefundForm(forms.ModelForm):
model = Refund model = Refund
fields = '__all__' fields = '__all__'
payments = forms.ModelMultipleChoiceField(queryset=Payment.objects.all()) payments = forms.ModelMultipleChoiceField(queryset=Payment.objects.none())
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(RefundForm, self).__init__(*args, **kwargs) super(RefundForm, self).__init__(*args, **kwargs)
if self.instance: if self.instance:
self.fields['payments'].initial = self.instance.payment_set.all() self.fields['payments'].initial = self.instance.payment_set.all()
self.fields['payments'].queryset = Payment.objects.filter(Q(refund=None) | Q(refund=self.instance))
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
# Save the refund # Save the refund
instance = super(RefundForm, self).save(commit=False) instance = super(RefundForm, self).save()
# Remove the refund from payments it was previously assigned to # Remove the refund from payments it was previously assigned to
self.fields['payments'].initial.update(refund=None) self.fields['payments'].initial.update(refund=None)
# Add the refund to the selected payments # Add the refund to the selected payments
self.cleaned_data['payments'].update(refund=instance) self.cleaned_data['payments'].update(refund=instance)
return instance return instance
class RefundFormAdmin(RefundForm):
class Meta:
model = Refund
fields = '__all__'
class RefundFormPublic(RefundForm):
class Meta:
model = Refund
exclude = ('user',)
date = forms.DateField(
widget=DateTimePicker(
options={
'format': 'YYYY-MM-DD'
}
)
)

View file

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.6 on 2016-06-04 01:40
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 = [
('refunding', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='refund',
name='user',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
),
]

View file

@ -12,11 +12,16 @@ class Refund(models.Model):
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
user = models.ForeignKey( user = models.ForeignKey(
AUTH_USER_MODEL, AUTH_USER_MODEL,
on_delete=models.PROTECT on_delete=models.PROTECT,
null=True
) )
def eur_value(self) -> float: def eur_value(self) -> float:
return self.payment_set.all().aggregate(Sum('value')).get('value__sum') / 100 value_sum = self.payment_set.all().aggregate(Sum('value')).get('value__sum')
if value_sum:
return value_sum / 100
else:
return 0
def __str__(self) -> str: def __str__(self) -> str:
return "{0} on {1} for {2}".format(self.title, self.date, self.eur_value()) return "{0} on {1} for {2}".format(self.title, self.date, self.eur_value())

View file

@ -0,0 +1,27 @@
{% extends 'base.html' %}
{% load crispy_forms_filters %}
{% load crispy_forms_field %}
{% block javascript %}
{{ block.super }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.min.js"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.37/js/bootstrap-datetimepicker.min.js"
crossorigin="anonymous"></script>
{% endblock %}
{% block style %}
{{ block.super }}
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.37/css/bootstrap-datetimepicker.min.css">
{% endblock %}
{% block content %}
<h1>{% block title %}New refund{% endblock %}</h1>
<form action="{% url 'new_refund' %}" method="post">
{% csrf_token %}
{{ form.media }}
{{ form|crispy }}
<input class="btn btn-primary" type="submit" value="Submit"/>
</form>
{% endblock %}

View file

@ -2,6 +2,15 @@
{% load l10n %} {% load l10n %}
{% block content %} {% block content %}
<h1>{% block title %}Latest refunds{% endblock %}</h1> <h1>
{% block title %}Latest refunds{% endblock %}
{% if user.is_authenticated %}
<div class="btn-group pull-right">
<a class="btn btn-success" href="{% url 'new_refund' %}">
<span class="glyphicon glyphicon-plus"></span>
</a>
</div>
{% endif %}
</h1>
{% include 'refunding/elements_list.html' with elements=refunds %} {% include 'refunding/elements_list.html' with elements=refunds %}
{% endblock %} {% endblock %}

View file

@ -1,7 +1,8 @@
from django.conf.urls import url from django.conf.urls import url
from refunding.views import not_refunded_payments, latest_refunds from refunding import views
urlpatterns = [ urlpatterns = [
url(r'^payments/$', not_refunded_payments, name='not_refunded_payments'), url(r'^payments/$', views.not_refunded_payments, name='not_refunded_payments'),
url(r'^refunds/$', latest_refunds, name='latest_refunds'), url(r'^refunds/$', views.latest_refunds, name='latest_refunds'),
url(r'^refunds/new/$', views.new_refund, name='new_refund'),
] ]

View file

@ -1,6 +1,7 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.db.models import Sum from django.db.models import Sum
from django.shortcuts import render from django.shortcuts import render, redirect
from refunding.forms import RefundForm, RefundFormPublic
from refunding.models import Payment, Refund from refunding.models import Payment, Refund
@ -24,3 +25,21 @@ def latest_refunds(request):
'default_nothing': 'No refund to show.' 'default_nothing': 'No refund to show.'
} }
return render(request, "refunding/refunds.html", context) return render(request, "refunding/refunds.html", context)
@login_required
def new_refund(request):
if request.method == 'POST':
form = RefundFormPublic(request.POST)
if form.is_valid():
refund = form.save()
refund.user = request.user
refund.save()
return redirect('latest_refunds')
else:
form = RefundFormPublic()
context = {
'form': form
}
return render(request, "refunding/new_refund.html", context)

View file

@ -50,6 +50,7 @@ X_FRAME_OPTIONS = 'DENY'
INSTALLED_APPS = [ INSTALLED_APPS = [
'refunding', 'refunding',
'authentication', 'authentication',
'bootstrap3_datetime',
'crispy_forms', 'crispy_forms',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',

View file

@ -1,5 +1,6 @@
dj-database-url==0.4.1 dj-database-url==0.4.1
Django==1.9.6 Django==1.9.6
django-bootstrap3-datetimepicker-2==2.4.2
django-crispy-forms==1.6.0 django-crispy-forms==1.6.0
django-dotenv==1.4.1 django-dotenv==1.4.1
gunicorn==19.6.0 gunicorn==19.6.0

View file

@ -7,9 +7,19 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
{% block javascript %}
<script src="https://code.jquery.com/jquery-2.2.3.min.js"
integrity="sha256-a23g1Nt4dtEYOj7bR+vTu7+T8VP13humZFBJNIYoEJo="
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
crossorigin="anonymous"></script>
{% endblock javascript %}
{% block style %} {% block style %}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700"> <link rel="stylesheet" type="text/css"
href="//fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700">
<link rel="stylesheet" href="{% static 'default_style.css' %}"> <link rel="stylesheet" href="{% static 'default_style.css' %}">
{% endblock style %} {% endblock style %}
</head> </head>
@ -20,14 +30,5 @@
{% block content %} {% block content %}
{% endblock %} {% endblock %}
</div> </div>
{% block javascript %}
<script src="https://code.jquery.com/jquery-2.2.3.min.js"
integrity="sha256-a23g1Nt4dtEYOj7bR+vTu7+T8VP13humZFBJNIYoEJo="
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
crossorigin="anonymous"></script>
{% endblock javascript %}
</body> </body>
</html> </html>