Création de K-Psul

K-Psul est l'interface pour l'équipe K-Fêt servant à effectuer des
opérations sur les comtpes

General :
- Ajout d'un default sur le montant d'un groupe d'opérations

K-Psul :
- Création de l'interface pour enregistrer une opération
This commit is contained in:
Aurélien Delobelle 2016-08-06 22:19:52 +02:00
parent e9bbb35e66
commit 6be65df654
7 changed files with 526 additions and 8 deletions

View file

@ -1,6 +1,7 @@
from django import forms
from django.contrib.auth.models import User
from kfet.models import Account, Checkout, Article
from django.forms import modelformset_factory
from kfet.models import Account, Checkout, Article, OperationGroup, Operation
from gestioncof.models import CofProfile
# -----
@ -107,3 +108,41 @@ class ArticleForm(forms.ModelForm):
class ArticleRestrictForm(ArticleForm):
class Meta(ArticleForm.Meta):
fields = ['name', 'is_sold', 'price', 'category']
# -----
# K-Psul forms
# -----
class KPsulOperationGroupForm(forms.ModelForm):
class Meta:
model = OperationGroup
fields = ['on_acc', 'checkout']
widgets = {
'on_acc' : forms.HiddenInput(),
'checkout': forms.HiddenInput(),
}
class KPsulAccountForm(forms.ModelForm):
class Meta:
model = Account
fields = ['trigramme']
widgets = {
'trigramme': forms.TextInput(attrs={'autocomplete': 'off'}),
}
class KPsulCheckoutForm(forms.Form):
checkout = forms.ModelChoiceField(
queryset=Checkout.objects.filter(is_protected=False),
widget=forms.Select(attrs={'id':'id_checkout_select'}))
class KPsulOperationForm(forms.ModelForm):
class Meta:
model = Operation
fields = ['type', 'amount', 'is_checkout', 'article', 'article_nb']
"""
OperationFormSet = modelformset_factory(
Operation,
form = KPsulOperationForm,
extra = 0,
min_num = 1, validate_min = True)
"""

View file

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('kfet', '0008_auto_20160804_1736'),
]
operations = [
migrations.RenameField(
model_name='operation',
old_name='on_checkout',
new_name='is_checkout',
),
migrations.AddField(
model_name='operation',
name='article_nb',
field=models.PositiveSmallIntegerField(default=None, null=True, blank=True),
),
]

View file

@ -355,7 +355,9 @@ class OperationGroup(models.Model):
Checkout, on_delete = models.PROTECT,
related_name = "operations")
at = models.DateTimeField(auto_now_add = True)
amount = models.IntegerField()
amount = models.DecimalField(
max_digits = 6, decimal_places = 2,
default = 0)
is_cof = models.BooleanField(default = False)
# Optional
comment = models.CharField(
@ -364,7 +366,7 @@ class OperationGroup(models.Model):
valid_by = models.ForeignKey(
Account, on_delete = models.PROTECT,
related_name = "+",
blank = True, null = True, default = True)
blank = True, null = True, default = None)
class Operation(models.Model):
PURCHASE = 'purchase'
@ -384,12 +386,14 @@ class Operation(models.Model):
choices = TYPE_ORDER_CHOICES,
max_length = choices_length(TYPE_ORDER_CHOICES))
amount = models.DecimalField(max_digits = 6, decimal_places = 2)
on_checkout = models.BooleanField(default = True)
is_checkout = models.BooleanField(default = True)
# Optional
article = models.ForeignKey(
Article, on_delete = models.PROTECT,
related_name = "operations",
blank = True, null = True, default = None)
article_nb = models.PositiveSmallIntegerField(
blank = True, null = True, default = None)
canceled_by = models.ForeignKey(
Account, on_delete = models.PROTECT,
related_name = "+",
@ -400,7 +404,9 @@ class Operation(models.Model):
Account, on_delete = models.PROTECT,
related_name = "addcosts",
blank = True, null = True, default = None)
addcost_amount = models.DecimalField(max_digits = 6, decimal_places = 2)
addcost_amount = models.DecimalField(
max_digits = 6, decimal_places = 2,
default = 0)
class GlobalPermissions(models.Model):
class Meta:

View file

@ -0,0 +1,151 @@
/*!
* JavaScript Cookie v2.1.2
* https://github.com/js-cookie/js-cookie
*
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
* Released under the MIT license
*/
;(function (factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
var OldCookies = window.Cookies;
var api = window.Cookies = factory();
api.noConflict = function () {
window.Cookies = OldCookies;
return api;
};
}
}(function () {
function extend () {
var i = 0;
var result = {};
for (; i < arguments.length; i++) {
var attributes = arguments[ i ];
for (var key in attributes) {
result[key] = attributes[key];
}
}
return result;
}
function init (converter) {
function api (key, value, attributes) {
var result;
if (typeof document === 'undefined') {
return;
}
// Write
if (arguments.length > 1) {
attributes = extend({
path: '/'
}, api.defaults, attributes);
if (typeof attributes.expires === 'number') {
var expires = new Date();
expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
attributes.expires = expires;
}
try {
result = JSON.stringify(value);
if (/^[\{\[]/.test(result)) {
value = result;
}
} catch (e) {}
if (!converter.write) {
value = encodeURIComponent(String(value))
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
} else {
value = converter.write(value, key);
}
key = encodeURIComponent(String(key));
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
key = key.replace(/[\(\)]/g, escape);
return (document.cookie = [
key, '=', value,
attributes.expires && '; expires=' + attributes.expires.toUTCString(), // use expires attribute, max-age is not supported by IE
attributes.path && '; path=' + attributes.path,
attributes.domain && '; domain=' + attributes.domain,
attributes.secure ? '; secure' : ''
].join(''));
}
// Read
if (!key) {
result = {};
}
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling "get()"
var cookies = document.cookie ? document.cookie.split('; ') : [];
var rdecode = /(%[0-9A-Z]{2})+/g;
var i = 0;
for (; i < cookies.length; i++) {
var parts = cookies[i].split('=');
var cookie = parts.slice(1).join('=');
if (cookie.charAt(0) === '"') {
cookie = cookie.slice(1, -1);
}
try {
var name = parts[0].replace(rdecode, decodeURIComponent);
cookie = converter.read ?
converter.read(cookie, name) : converter(cookie, name) ||
cookie.replace(rdecode, decodeURIComponent);
if (this.json) {
try {
cookie = JSON.parse(cookie);
} catch (e) {}
}
if (key === name) {
result = cookie;
break;
}
if (!key) {
result[name] = cookie;
}
} catch (e) {}
}
return result;
}
api.set = api;
api.get = function (key) {
return api(key);
};
api.getJSON = function () {
return api.apply({
json: true
}, [].slice.call(arguments));
};
api.defaults = {};
api.remove = function (key, attributes) {
api(key, '', extend(attributes, {
expires: -1
}));
};
api.withConverter = init;
return api;
}
return init(function () {});
}));

View file

@ -0,0 +1,214 @@
{% extends 'kfet/base.html' %}
{% load staticfiles %}
{% block extra_head %}
<script type="text/javascript" src="{% static 'kfet/js.cookie.js' %}"></script>
{% endblock %}
{% block title %}K-Psul{% endblock %}
{% block content %}
{% csrf_token %}
<form id="operationgroup_form">{{ operationgroup_form }}</form>
{{ trigramme_form.as_p }}
<div id="account_data">
<p id="account-balance"></p>
<p id="account-name"></p>
<p id="account-email"></p>
<p id="account-is_cof"></p>
<p id="account-promo"></p>
<p id="account-is_frozen"></p>
<p id="account-departement"></p>
<p id="account-nickname"></p>
</div>
{{ checkout_form.as_p }}
<div id="checkout_data">
<p id="checkout-name"></p>
<p id="checkout-balance"></p>
<p id="checkout-valid_from"></p>
<p id="checkout-valid_to"></p>
</div>
<form id="operation_formset">
{{ operation_formset.as_p }}
</form>
<button type="button" id="perform_operations">Valider</button>
<script type="text/javascript">
$(document).ready(function() {
// -----
// General
// -----
// Retrieving csrf token
var csrftoken = Cookies.get('csrftoken');
// Appending csrf token to ajax post requests
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
// -----
// Account data management
// -----
// Initializing
var triInput = $('#id_trigramme')
var triPattern = /^[^a-z]{3}$/
var account_data = {}
// Display data
function displayAccountData() {
for (var elem in account_data) {
$('#account-'+elem).text(account_data[elem]);
}
}
// Clear data
function resetAccountData() {
account_data = {
'pk' : 0,
'name' : '',
'email': '',
'is_cof' : '',
'promo' : '',
'balance': '',
'is_frozen' : '',
'departement': '',
'nickname' : '',
}
$('#id_on_acc').val(0);
displayAccountData();
}
// Store data
function storeAccountData(data) {
for (var elem in data) {
account_data[elem] = data[elem];
}
$('#id_on_acc').val(account_data['pk']);
displayAccountData();
}
// Retrieve via ajax
function retrieveAccountData(tri) {
$.ajax({
dataType: "json",
url : "{% url 'kfet.kpsul.account_data' %}",
method : "POST",
data : { trigramme: tri },
})
.done(function(data) { storeAccountData(data) })
.fail(function() { resetAccountData() });
}
// Event listener
triInput.on('input', function() {
var tri = triInput.val()
// Checking if tri is valid to avoid sending requests
if (tri.match(triPattern)) {
retrieveAccountData(tri);
} else {
resetAccountData();
}
});
// -----
// Checkout data management
// -----
// Initializing
var checkoutInput = $('#id_checkout_select');
var checkout_data = {}
// Display data
function displayCheckoutData() {
for (var elem in checkout_data) {
$('#checkout-'+elem).text(checkout_data[elem]);
}
}
// Clear data
function resetCheckoutData() {
checkout_data = {
'pk' : 0,
'name': '',
'balance' : '',
'valid_from': '',
'valid_to' : '',
}
$('#id_checkout').val(0);
displayCheckoutData();
}
// Store data
function storeCheckoutData(data) {
for (var elem in data) {
checkout_data[elem] = data[elem];
}
$('#id_checkout').val(checkout_data['pk']);
displayCheckoutData();
}
// Retrieve data
function retrieveCheckoutData(id) {
$.ajax({
dataType: "json",
url : "{% url 'kfet.kpsul.checkout_data' %}",
method : "POST",
data : { 'pk': id },
})
.done(function(data) { storeCheckoutData(data) })
.fail(function() { resetCheckoutData() });
}
// Event listener
checkoutInput.on('change', function() {
retrieveCheckoutData(checkoutInput.val());
});
// -----
// Perform operations
// -----
var performButton = $('#perform_operations');
var operationGroup = $('#operationgroup_form');
var operations = $('#operation_formset');
function performOperations() {
data = operationGroup.serialize() + '&' + operations.serialize();
$.ajax({
dataType: "json",
url : "{% url 'kfet.kpsul.perform_operations' %}",
method : "POST",
data : data,
})
.done(function(data) {
console.log(data);
})
.always(function($xhr) {
var data = $xhr.responseJSON;
console.log(data);
});
}
// Event listeners
performButton.on('click', function() {
performOperations();
});
});
</script>
{% endblock %}

View file

@ -78,4 +78,16 @@ urlpatterns = [
url('^article/(?P<pk>\d+)/edit$',
permission_required('kfet.is_team')(views.ArticleUpdate.as_view()),
name = 'kfet.article.update'),
# -----
# K-Psul urls
# -----
url('^k-psul/$', views.kpsul, name = 'kfet.kpsul'),
url('^k-psul/account_data$', views.kpsul_account_data,
name = 'kfet.kpsul.account_data'),
url('^k-psul/checkout_data$', views.kpsul_checkout_data,
name = 'kfet.kpsul.checkout_data'),
url('^k-psul/perform_operations$', views.kpsul_perform_operations,
name = 'kfet.kpsul.perform_operations'),
]

View file

@ -7,11 +7,12 @@ from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.models import User
from django.http import HttpResponse, Http404
import json
from django.http import HttpResponse, JsonResponse, Http404
from django.forms import modelformset_factory
from gestioncof.models import CofProfile, Clipper
from kfet.models import Account, Checkout, Article
from kfet.forms import *
from collections import defaultdict
@login_required
def home(request):
@ -40,7 +41,7 @@ def account_is_validandfree_ajax(request):
raise Http404
trigramme = request.GET.get("trigramme")
data = Account.is_validandfree(trigramme)
return HttpResponse(json.dumps(data), content_type = 'application/json')
return JsonResponse(data)
# Account - Create
@ -349,3 +350,74 @@ class ArticleUpdate(UpdateView):
raise PermissionDenied
# Updating
return super(ArticleUpdate, self).form_valid(form)
# -----
# K-Psul
# -----
@permission_required('kfet.is_team')
def kpsul(request):
data = {}
data['operationgroup_form'] = KPsulOperationGroupForm()
data['trigramme_form'] = KPsulAccountForm()
data['checkout_form'] = KPsulCheckoutForm()
OperationFormSet = modelformset_factory(
Operation,
form = KPsulOperationForm,
extra = 0,
min_num = 1, validate_min = True)
operation_formset = OperationFormSet(queryset=Operation.objects.none())
data['operation_formset'] = operation_formset
return render(request, 'kfet/kpsul.html', data)
@permission_required('kfet.is_team')
def kpsul_account_data(request):
trigramme = request.POST.get('trigramme', '')
account = get_object_or_404(Account, trigramme=trigramme)
data = { 'pk': account.pk, 'name': account.name, 'email': account.email,
'is_cof': account.is_cof, 'promo': account.promo,
'balance': account.balance, 'is_frozen': account.is_frozen,
'departement': account.departement, 'nickname': account.nickname }
return JsonResponse(data)
@permission_required('kfet.is_team')
def kpsul_checkout_data(request):
pk = request.POST.get('pk', 0)
checkout = get_object_or_404(Checkout, pk=pk)
data = { 'pk': checkout.pk, 'name': checkout.name, 'balance': checkout.balance,
'valid_from': checkout.valid_from, 'valid_to': checkout.valid_to }
return JsonResponse(data)
@permission_required('kfet.is_team')
def kpsul_perform_operations(request):
# Initializing
data = defaultdict(list)
# Checking operationgroup data
try:
account_pk = request.POST.get('on_acc', 0)
account = Account.objects.get(pk=account_pk)
except (Account.DoesNotExist, ValueError):
data['errors'].append('Trigramme invalide')
try:
checkout_pk = request.POST.get('checkout', 0)
checkout = Checkout.objects.get(pk=checkout_pk)
except (Checkout.DoesNotExist, ValueError):
data['errors'].append('Caisse invalide')
if 'errors' in data:
return JsonResponse(data, status=400)
operationgroup_form = KPsulOperationGroupForm(request.POST)
operationgroup = operationgroup_form.save(commit = False)
operation_formset = OperationFormSet(request.POST)
operations = operation_formset.save(commit = False)
for operation in operations:
operationgroup.amount += operation.amount
operationgroup.save()
data['operationgroup'] = operationgroup.pk
for operation in operations:
operation.group = operationgroup
operation.save()
data['operations'].append(operation.pk)
return JsonResponse(data)