forked from DGNum/gestioCOF
Merge branch 'master' into aureplop/cof-tests_calendar
This commit is contained in:
commit
60d8e76fee
26 changed files with 2228 additions and 51 deletions
|
@ -14,7 +14,7 @@
|
||||||
</tr></thead>
|
</tr></thead>
|
||||||
<tbody class="bda_formset_content">
|
<tbody class="bda_formset_content">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr class="{% cycle row1,row2 %} dynamic-form {% if form.instance.pk %}has_original{% endif %}">
|
<tr class="{% cycle 'row1' 'row2' %} dynamic-form {% if form.instance.pk %}has_original{% endif %}">
|
||||||
{% for field in form.visible_fields %}
|
{% for field in form.visible_fields %}
|
||||||
{% if field.name != "DELETE" and field.name != "priority" %}
|
{% if field.name != "DELETE" and field.name != "priority" %}
|
||||||
<td class="bda-field-{{ field.name }}">
|
<td class="bda-field-{{ field.name }}">
|
||||||
|
|
|
@ -27,6 +27,14 @@ var django = {
|
||||||
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
|
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
|
||||||
$(this).attr('for', newFor);
|
$(this).attr('for', newFor);
|
||||||
});
|
});
|
||||||
|
// Cloning <select> element doesn't properly propagate the default
|
||||||
|
// selected <option>, so we set it manually.
|
||||||
|
newElement.find('select').each(function (index, select) {
|
||||||
|
var defaultValue = $(select).find('option[selected]').val();
|
||||||
|
if (typeof defaultValue !== 'undefined') {
|
||||||
|
$(select).val(defaultValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
total++;
|
total++;
|
||||||
$('#id_' + type + '-TOTAL_FORMS').val(total);
|
$('#id_' + type + '-TOTAL_FORMS').val(total);
|
||||||
$(selector).after(newElement);
|
$(selector).after(newElement);
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<h4 class="bda-prix">Total à payer : {{ total|floatformat }}€</h4>
|
<h4 class="bda-prix">Total à payer : {{ total|floatformat }}€</h4>
|
||||||
<br/>
|
<br/>
|
||||||
<p>Ne manque pas un spectacle avec le
|
<p>Ne manque pas un spectacle avec le
|
||||||
<a href="{% url "gestioncof.views.calendar" %}">calendrier
|
<a href="{% url "calendar" %}">calendrier
|
||||||
automatique !</a></p>
|
automatique !</a></p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h3>Vous n'avez aucune place :(</h3>
|
<h3>Vous n'avez aucune place :(</h3>
|
||||||
|
|
|
@ -7,6 +7,7 @@ the local development server should be here.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from . import secret
|
from . import secret
|
||||||
|
@ -53,9 +54,13 @@ BASE_DIR = os.path.dirname(
|
||||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TESTING = sys.argv[1] == 'test'
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
|
'shared',
|
||||||
|
|
||||||
'gestioncof',
|
'gestioncof',
|
||||||
|
|
||||||
# Must be before 'django.contrib.admin'.
|
# Must be before 'django.contrib.admin'.
|
||||||
|
|
|
@ -4,13 +4,18 @@ The settings that are not listed here are imported from .common
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .common import * # NOQA
|
from .common import * # NOQA
|
||||||
from .common import INSTALLED_APPS, MIDDLEWARE
|
from .common import INSTALLED_APPS, MIDDLEWARE, TESTING
|
||||||
|
|
||||||
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
|
if TESTING:
|
||||||
|
PASSWORD_HASHERS = [
|
||||||
|
'django.contrib.auth.hashers.MD5PasswordHasher',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
# Apache static/media config
|
# Apache static/media config
|
||||||
|
@ -36,6 +41,7 @@ def show_toolbar(request):
|
||||||
"""
|
"""
|
||||||
return DEBUG
|
return DEBUG
|
||||||
|
|
||||||
|
if not TESTING:
|
||||||
INSTALLED_APPS += ["debug_toolbar", "debug_panel"]
|
INSTALLED_APPS += ["debug_toolbar", "debug_panel"]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include "tristate_js.html" %}
|
{% include "tristate_js.html" %}
|
||||||
<h3>Filtres</h3>
|
<h3>Filtres</h3>
|
||||||
<form method="post" action="{% url 'gestioncof.views.event_status' event.id %}">
|
<form method="post" action="{% url 'event.details.status' event.id %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<input style="margin-top:10px;" type="submit" class="btn btn-primary" value="Filtrer" />
|
<input style="margin-top:10px;" type="submit" class="btn btn-primary" value="Filtrer" />
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
{% if survey.details %}
|
{% if survey.details %}
|
||||||
<p>{{ survey.details }}</p>
|
<p>{{ survey.details }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form class="form-horizontal" method="post" action="{% url 'gestioncof.views.survey' survey.id %}">
|
<form class="form-horizontal" method="post" action="{% url 'survey.details' survey.id %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form | bootstrap}}
|
{{ form | bootstrap}}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</tr></thead>
|
</tr></thead>
|
||||||
<tbody class="bda_formset_content">
|
<tbody class="bda_formset_content">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr class="{% cycle row1,row2 %} dynamic-form {% if form.instance.pk %}has_original{% endif %}">
|
<tr class="{% cycle 'row1' 'row2' %} dynamic-form {% if form.instance.pk %}has_original{% endif %}">
|
||||||
{% for field in form.visible_fields %}
|
{% for field in form.visible_fields %}
|
||||||
{% if field.name != "DELETE" and field.name != "priority" %}
|
{% if field.name != "DELETE" and field.name != "priority" %}
|
||||||
<td class="bda-field-{{ field.name }}">
|
<td class="bda-field-{{ field.name }}">
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{% block page_size %}col-sm-8{% endblock %}
|
{% block page_size %}col-sm-8{% endblock %}
|
||||||
|
|
||||||
{% block extra_head %}
|
{% block extra_head %}
|
||||||
<script src="{% static "autocomplete_light/autocomplete.js" %}" type="text/javascript"></script>
|
<script src="{% static "vendor/jquery.autocomplete-light/3.5.0/dist/jquery.autocomplete-light.min.js" %}" type="text/javascript"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block realcontent %}
|
{% block realcontent %}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h3>Filtres</h3>
|
<h3>Filtres</h3>
|
||||||
{% include "tristate_js.html" %}
|
{% include "tristate_js.html" %}
|
||||||
<form method="post" action="{% url 'gestioncof.views.survey_status' survey.id %}">
|
<form method="post" action="{% url 'survey.details.status' survey.id %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<input style="margin-top:10px;" type="submit" class="btn btn-primary" value="Filtrer" />
|
<input style="margin-top:10px;" type="submit" class="btn btn-primary" value="Filtrer" />
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.contrib.messages.storage.base import Message
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from bda.models import Salle, Tirage
|
from bda.models import Salle, Tirage
|
||||||
from gestioncof.models import CalendarSubscription
|
from gestioncof.models import CalendarSubscription, Event, Survey, SurveyAnswer
|
||||||
from gestioncof.tests.testcases import ViewTestCaseMixin
|
from gestioncof.tests.testcases import ViewTestCaseMixin
|
||||||
|
|
||||||
|
|
||||||
|
@ -149,3 +149,328 @@ class CalendarICSViewTests(ViewTestCaseMixin, TestCase):
|
||||||
'uid': 'show-{}-{}@example.com'.format(self.s2.pk, self.t.pk),
|
'uid': 'show-{}-{}@example.com'.format(self.s2.pk, self.t.pk),
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class EventViewTests(ViewTestCaseMixin, TestCase):
|
||||||
|
url_name = 'event.details'
|
||||||
|
http_methods = ['GET', 'POST']
|
||||||
|
|
||||||
|
auth_user = 'user'
|
||||||
|
auth_forbidden = [None]
|
||||||
|
|
||||||
|
post_expected_message = Message(messages.SUCCESS, (
|
||||||
|
"Votre inscription a bien été enregistrée ! Vous pouvez cependant la "
|
||||||
|
"modifier jusqu'à la fin des inscriptions."
|
||||||
|
))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_kwargs(self):
|
||||||
|
return {'event_id': self.e.pk}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_expected(self):
|
||||||
|
return '/event/{}'.format(self.e.pk)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
self.e = Event.objects.create()
|
||||||
|
|
||||||
|
self.ecf1 = self.e.commentfields.create(name='Comment Field 1')
|
||||||
|
self.ecf2 = self.e.commentfields.create(
|
||||||
|
name='Comment Field 2', fieldtype='char',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.o1 = self.e.options.create(name='Option 1')
|
||||||
|
self.o2 = self.e.options.create(name='Option 2', multi_choices=True)
|
||||||
|
|
||||||
|
self.oc1 = self.o1.choices.create(value='O1 - Choice 1')
|
||||||
|
self.oc2 = self.o1.choices.create(value='O1 - Choice 2')
|
||||||
|
self.oc3 = self.o2.choices.create(value='O2 - Choice 1')
|
||||||
|
self.oc4 = self.o2.choices.create(value='O2 - Choice 2')
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
r = self.client.get(self.url)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
def test_post_new(self):
|
||||||
|
r = self.client.post(self.url, {
|
||||||
|
'option_{}'.format(self.o1.pk): [str(self.oc1.pk)],
|
||||||
|
'option_{}'.format(self.o2.pk): [
|
||||||
|
str(self.oc3.pk), str(self.oc4.pk),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))
|
||||||
|
|
||||||
|
er = self.e.eventregistration_set.get(user=self.users['user'])
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
er.options.all(), map(repr, [self.oc1, self.oc3, self.oc4]),
|
||||||
|
ordered=False,
|
||||||
|
)
|
||||||
|
# TODO: Make the view care about comments.
|
||||||
|
# self.assertQuerysetEqual(
|
||||||
|
# er.comments.all(), map(repr, []),
|
||||||
|
# ordered=False,
|
||||||
|
# )
|
||||||
|
|
||||||
|
def test_post_edit(self):
|
||||||
|
er = self.e.eventregistration_set.create(user=self.users['user'])
|
||||||
|
er.options.add(self.oc1, self.oc3, self.oc4)
|
||||||
|
er.comments.create(
|
||||||
|
commentfield=self.ecf1, content='Comment 1',
|
||||||
|
)
|
||||||
|
|
||||||
|
r = self.client.post(self.url, {
|
||||||
|
'option_{}'.format(self.o1.pk): [],
|
||||||
|
'option_{}'.format(self.o2.pk): [str(self.oc3.pk)],
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))
|
||||||
|
|
||||||
|
er.refresh_from_db()
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
er.options.all(), map(repr, [self.oc3]),
|
||||||
|
ordered=False,
|
||||||
|
)
|
||||||
|
# TODO: Make the view care about comments.
|
||||||
|
# self.assertQuerysetEqual(
|
||||||
|
# er.comments.all(), map(repr, []),
|
||||||
|
# ordered=False,
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
class EventStatusViewTests(ViewTestCaseMixin, TestCase):
|
||||||
|
url_name = 'event.details.status'
|
||||||
|
|
||||||
|
http_methods = ['GET', 'POST']
|
||||||
|
|
||||||
|
auth_user = 'staff'
|
||||||
|
auth_forbidden = [None, 'user', 'member']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_kwargs(self):
|
||||||
|
return {'event_id': self.e.pk}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_expected(self):
|
||||||
|
return '/event/{}/status'.format(self.e.pk)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
self.e = Event.objects.create()
|
||||||
|
|
||||||
|
self.cf1 = self.e.commentfields.create(name='Comment Field 1')
|
||||||
|
self.cf2 = self.e.commentfields.create(
|
||||||
|
name='Comment Field 2', fieldtype='char',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.o1 = self.e.options.create(name='Option 1')
|
||||||
|
self.o2 = self.e.options.create(name='Option 2', multi_choices=True)
|
||||||
|
|
||||||
|
self.oc1 = self.o1.choices.create(value='O1 - Choice 1')
|
||||||
|
self.oc2 = self.o1.choices.create(value='O1 - Choice 2')
|
||||||
|
self.oc3 = self.o2.choices.create(value='O2 - Choice 1')
|
||||||
|
self.oc4 = self.o2.choices.create(value='O2 - Choice 2')
|
||||||
|
|
||||||
|
self.er1 = self.e.eventregistration_set.create(user=self.users['user'])
|
||||||
|
self.er1.options.add(self.oc1)
|
||||||
|
self.er2 = self.e.eventregistration_set.create(
|
||||||
|
user=self.users['member'],
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_oc_filter_name(self, oc):
|
||||||
|
return 'option_{}_choice_{}'.format(oc.event_option.pk, oc.pk)
|
||||||
|
|
||||||
|
def _test_filters(self, filters, expected):
|
||||||
|
r = self.client.post(self.url, {
|
||||||
|
self._get_oc_filter_name(oc): v for oc, v in filters
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
r.context['user_choices'], map(repr, expected),
|
||||||
|
ordered=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_filter_none(self):
|
||||||
|
self._test_filters([(self.oc1, 'none')], [self.er1, self.er2])
|
||||||
|
|
||||||
|
def test_filter_yes(self):
|
||||||
|
self._test_filters([(self.oc1, 'yes')], [self.er1])
|
||||||
|
|
||||||
|
def test_filter_no(self):
|
||||||
|
self._test_filters([(self.oc1, 'no')], [self.er2])
|
||||||
|
|
||||||
|
|
||||||
|
class SurveyViewTests(ViewTestCaseMixin, TestCase):
|
||||||
|
url_name = 'survey.details'
|
||||||
|
http_methods = ['GET', 'POST']
|
||||||
|
|
||||||
|
auth_user = 'user'
|
||||||
|
auth_forbidden = [None]
|
||||||
|
|
||||||
|
post_expected_message = Message(messages.SUCCESS, (
|
||||||
|
"Votre réponse a bien été enregistrée ! Vous pouvez cependant la "
|
||||||
|
"modifier jusqu'à la fin du sondage."
|
||||||
|
))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_kwargs(self):
|
||||||
|
return {'survey_id': self.s.pk}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_expected(self):
|
||||||
|
return '/survey/{}'.format(self.s.pk)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
self.s = Survey.objects.create(title='Title')
|
||||||
|
|
||||||
|
self.q1 = self.s.questions.create(question='Question 1 ?')
|
||||||
|
self.q2 = self.s.questions.create(
|
||||||
|
question='Question 2 ?',
|
||||||
|
multi_answers=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.qa1 = self.q1.answers.create(answer='Q1 - Answer 1')
|
||||||
|
self.qa2 = self.q1.answers.create(answer='Q1 - Answer 2')
|
||||||
|
self.qa3 = self.q2.answers.create(answer='Q2 - Answer 1')
|
||||||
|
self.qa4 = self.q2.answers.create(answer='Q2 - Answer 2')
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
r = self.client.get(self.url)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
def test_post_new(self):
|
||||||
|
r = self.client.post(self.url, {
|
||||||
|
'question_{}'.format(self.q1.pk): [str(self.qa1.pk)],
|
||||||
|
'question_{}'.format(self.q2.pk): [
|
||||||
|
str(self.qa3.pk), str(self.qa4.pk),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))
|
||||||
|
|
||||||
|
a = self.s.surveyanswer_set.get(user=self.users['user'])
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
a.answers.all(), map(repr, [self.qa1, self.qa3, self.qa4]),
|
||||||
|
ordered=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_post_edit(self):
|
||||||
|
a = self.s.surveyanswer_set.create(user=self.users['user'])
|
||||||
|
a.answers.add(self.qa1, self.qa1, self.qa4)
|
||||||
|
|
||||||
|
r = self.client.post(self.url, {
|
||||||
|
'question_{}'.format(self.q1.pk): [],
|
||||||
|
'question_{}'.format(self.q2.pk): [str(self.qa3.pk)],
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))
|
||||||
|
|
||||||
|
a.refresh_from_db()
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
a.answers.all(), map(repr, [self.qa3]),
|
||||||
|
ordered=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_post_delete(self):
|
||||||
|
a = self.s.surveyanswer_set.create(user=self.users['user'])
|
||||||
|
a.answers.add(self.qa1, self.qa4)
|
||||||
|
|
||||||
|
r = self.client.post(self.url, {'delete': '1'})
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
expected_message = Message(
|
||||||
|
messages.SUCCESS, "Votre réponse a bien été supprimée")
|
||||||
|
self.assertIn(expected_message, get_messages(r.wsgi_request))
|
||||||
|
|
||||||
|
with self.assertRaises(SurveyAnswer.DoesNotExist):
|
||||||
|
a.refresh_from_db()
|
||||||
|
|
||||||
|
def test_forbidden_closed(self):
|
||||||
|
self.s.survey_open = False
|
||||||
|
self.s.save()
|
||||||
|
|
||||||
|
r = self.client.get(self.url)
|
||||||
|
|
||||||
|
self.assertNotEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
def test_forbidden_old(self):
|
||||||
|
self.s.old = True
|
||||||
|
self.s.save()
|
||||||
|
|
||||||
|
r = self.client.get(self.url)
|
||||||
|
|
||||||
|
self.assertNotEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class SurveyStatusViewTests(ViewTestCaseMixin, TestCase):
|
||||||
|
url_name = 'survey.details.status'
|
||||||
|
|
||||||
|
http_methods = ['GET', 'POST']
|
||||||
|
|
||||||
|
auth_user = 'staff'
|
||||||
|
auth_forbidden = [None, 'user', 'member']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_kwargs(self):
|
||||||
|
return {'survey_id': self.s.pk}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_expected(self):
|
||||||
|
return '/survey/{}/status'.format(self.s.pk)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
self.s = Survey.objects.create(title='Title')
|
||||||
|
|
||||||
|
self.q1 = self.s.questions.create(question='Question 1 ?')
|
||||||
|
self.q2 = self.s.questions.create(
|
||||||
|
question='Question 2 ?',
|
||||||
|
multi_answers=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.qa1 = self.q1.answers.create(answer='Q1 - Answer 1')
|
||||||
|
self.qa2 = self.q1.answers.create(answer='Q1 - Answer 2')
|
||||||
|
self.qa3 = self.q2.answers.create(answer='Q2 - Answer 1')
|
||||||
|
self.qa4 = self.q2.answers.create(answer='Q2 - Answer 2')
|
||||||
|
|
||||||
|
self.a1 = self.s.surveyanswer_set.create(user=self.users['user'])
|
||||||
|
self.a1.answers.add(self.qa1)
|
||||||
|
self.a2 = self.s.surveyanswer_set.create(user=self.users['member'])
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
r = self.client.get(self.url)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
def _get_qa_filter_name(self, qa):
|
||||||
|
return 'question_{}_answer_{}'.format(qa.survey_question.pk, qa.pk)
|
||||||
|
|
||||||
|
def _test_filters(self, filters, expected):
|
||||||
|
r = self.client.post(self.url, {
|
||||||
|
self._get_qa_filter_name(qa): v for qa, v in filters
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
r.context['user_answers'], map(repr, expected),
|
||||||
|
ordered=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_filter_none(self):
|
||||||
|
self._test_filters([(self.qa1, 'none')], [self.a1, self.a2])
|
||||||
|
|
||||||
|
def test_filter_yes(self):
|
||||||
|
self._test_filters([(self.qa1, 'yes')], [self.a1])
|
||||||
|
|
||||||
|
def test_filter_no(self):
|
||||||
|
self._test_filters([(self.qa1, 'no')], [self.a2])
|
||||||
|
|
|
@ -20,6 +20,7 @@ class TriStateCheckbox(Widget):
|
||||||
def render(self, name, value, attrs=None, choices=()):
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
if value is None:
|
if value is None:
|
||||||
value = 'none'
|
value = 'none'
|
||||||
final_attrs = self.build_attrs(attrs, value=value)
|
attrs['value'] = value
|
||||||
|
final_attrs = self.build_attrs(self.attrs, attrs)
|
||||||
output = ["<span class=\"tristate\"%s></span>" % flatatt(final_attrs)]
|
output = ["<span class=\"tristate\"%s></span>" % flatatt(final_attrs)]
|
||||||
return mark_safe('\n'.join(output))
|
return mark_safe('\n'.join(output))
|
||||||
|
|
|
@ -296,17 +296,17 @@ class KPsulAccountForm(forms.ModelForm):
|
||||||
|
|
||||||
class KPsulCheckoutForm(forms.Form):
|
class KPsulCheckoutForm(forms.Form):
|
||||||
checkout = forms.ModelChoiceField(
|
checkout = forms.ModelChoiceField(
|
||||||
queryset=(
|
queryset=None,
|
||||||
Checkout.objects
|
|
||||||
.filter(
|
|
||||||
is_protected=False,
|
|
||||||
valid_from__lte=timezone.now(),
|
|
||||||
valid_to__gte=timezone.now(),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
widget=forms.Select(attrs={'id': 'id_checkout_select'}),
|
widget=forms.Select(attrs={'id': 'id_checkout_select'}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Create the queryset on form instanciation to use the current time.
|
||||||
|
self.fields['checkout'].queryset = (
|
||||||
|
Checkout.objects.is_valid().filter(is_protected=False))
|
||||||
|
|
||||||
|
|
||||||
class KPsulOperationForm(forms.ModelForm):
|
class KPsulOperationForm(forms.ModelForm):
|
||||||
article = forms.ModelChoiceField(
|
article = forms.ModelChoiceField(
|
||||||
|
|
20
kfet/migrations/0063_promo.py
Normal file
20
kfet/migrations/0063_promo.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.12 on 2018-04-05 21:47
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('kfet', '0062_delete_globalpermissions'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='promo',
|
||||||
|
field=models.IntegerField(blank=True, choices=[(1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018)], default=2017, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -341,6 +341,13 @@ class AccountNegative(models.Model):
|
||||||
return self.start + kfet_config.overdraft_duration
|
return self.start + kfet_config.overdraft_duration
|
||||||
|
|
||||||
|
|
||||||
|
class CheckoutQuerySet(models.QuerySet):
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
now = timezone.now()
|
||||||
|
return self.filter(valid_from__lte=now, valid_to__gte=now)
|
||||||
|
|
||||||
|
|
||||||
class Checkout(models.Model):
|
class Checkout(models.Model):
|
||||||
created_by = models.ForeignKey(
|
created_by = models.ForeignKey(
|
||||||
Account, on_delete = models.PROTECT,
|
Account, on_delete = models.PROTECT,
|
||||||
|
@ -353,6 +360,8 @@ class Checkout(models.Model):
|
||||||
default = 0)
|
default = 0)
|
||||||
is_protected = models.BooleanField(default = False)
|
is_protected = models.BooleanField(default = False)
|
||||||
|
|
||||||
|
objects = CheckoutQuerySet.as_manager()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('kfet.checkout.read', kwargs={'pk': self.pk})
|
return reverse('kfet.checkout.read', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
|
@ -362,6 +371,22 @@ class Checkout(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
created = self.pk is None
|
||||||
|
|
||||||
|
ret = super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
self.statements.create(
|
||||||
|
amount_taken=0,
|
||||||
|
balance_old=self.balance,
|
||||||
|
balance_new=self.balance,
|
||||||
|
by=self.created_by,
|
||||||
|
)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class CheckoutTransfer(models.Model):
|
class CheckoutTransfer(models.Model):
|
||||||
from_checkout = models.ForeignKey(
|
from_checkout = models.ForeignKey(
|
||||||
Checkout, on_delete = models.PROTECT,
|
Checkout, on_delete = models.PROTECT,
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{% block header-title %}Création d'un compte{% endblock %}
|
{% block header-title %}Création d'un compte{% endblock %}
|
||||||
|
|
||||||
{% block extra_head %}
|
{% block extra_head %}
|
||||||
<script src="{% static "autocomplete_light/autocomplete.js" %}" type="text/javascript"></script>
|
<script src="{% static "vendor/jquery.autocomplete-light/3.5.0/dist/jquery.autocomplete-light.min.js" %}" type="text/javascript"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
48
kfet/tests/test_forms.py
Normal file
48
kfet/tests/test_forms.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import datetime
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from kfet.forms import KPsulCheckoutForm
|
||||||
|
from kfet.models import Checkout
|
||||||
|
|
||||||
|
from .utils import create_user
|
||||||
|
|
||||||
|
|
||||||
|
class KPsulCheckoutFormTests(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.now = timezone.now()
|
||||||
|
|
||||||
|
user = create_user()
|
||||||
|
|
||||||
|
self.c1 = Checkout.objects.create(
|
||||||
|
name='C1', balance=10,
|
||||||
|
created_by=user.profile.account_kfet,
|
||||||
|
valid_from=self.now,
|
||||||
|
valid_to=self.now + datetime.timedelta(days=1),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.form = KPsulCheckoutForm()
|
||||||
|
|
||||||
|
def test_checkout(self):
|
||||||
|
checkout_f = self.form.fields['checkout']
|
||||||
|
self.assertListEqual(list(checkout_f.choices), [
|
||||||
|
('', '---------'),
|
||||||
|
(self.c1.pk, 'C1'),
|
||||||
|
])
|
||||||
|
|
||||||
|
@mock.patch('django.utils.timezone.now')
|
||||||
|
def test_checkout_valid(self, mock_now):
|
||||||
|
"""
|
||||||
|
Checkout are filtered using the current datetime.
|
||||||
|
Regression test for #184.
|
||||||
|
"""
|
||||||
|
self.now += datetime.timedelta(days=2)
|
||||||
|
mock_now.return_value = self.now
|
||||||
|
|
||||||
|
form = KPsulCheckoutForm()
|
||||||
|
|
||||||
|
checkout_f = form.fields['checkout']
|
||||||
|
self.assertListEqual(list(checkout_f.choices), [('', '---------')])
|
|
@ -1,7 +1,12 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
from kfet.models import Account
|
from kfet.models import Account, Checkout
|
||||||
|
|
||||||
|
from .utils import create_user
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
@ -23,3 +28,33 @@ class AccountTests(TestCase):
|
||||||
|
|
||||||
with self.assertRaises(Account.DoesNotExist):
|
with self.assertRaises(Account.DoesNotExist):
|
||||||
Account.objects.get_by_password('bernard')
|
Account.objects.get_by_password('bernard')
|
||||||
|
|
||||||
|
|
||||||
|
class CheckoutTests(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.now = timezone.now()
|
||||||
|
|
||||||
|
self.u = create_user()
|
||||||
|
self.u_acc = self.u.profile.account_kfet
|
||||||
|
|
||||||
|
self.c = Checkout(
|
||||||
|
created_by=self.u_acc,
|
||||||
|
valid_from=self.now,
|
||||||
|
valid_to=self.now + datetime.timedelta(days=1),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_initial_statement(self):
|
||||||
|
"""A statement is added with initial balance on creation."""
|
||||||
|
self.c.balance = 10
|
||||||
|
self.c.save()
|
||||||
|
|
||||||
|
st = self.c.statements.get()
|
||||||
|
self.assertEqual(st.balance_new, 10)
|
||||||
|
self.assertEqual(st.amount_taken, 0)
|
||||||
|
self.assertEqual(st.amount_error, 0)
|
||||||
|
|
||||||
|
# Saving again doesn't create a new statement.
|
||||||
|
self.c.save()
|
||||||
|
|
||||||
|
self.assertEqual(self.c.statements.count(), 1)
|
||||||
|
|
|
@ -746,11 +746,15 @@ class CheckoutReadViewTests(ViewTestCaseMixin, TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
|
with mock.patch('django.utils.timezone.now') as mock_now:
|
||||||
|
mock_now.return_value = self.now
|
||||||
|
|
||||||
self.checkout = Checkout.objects.create(
|
self.checkout = Checkout.objects.create(
|
||||||
name='Checkout',
|
name='Checkout', balance=Decimal('10'),
|
||||||
created_by=self.accounts['team'],
|
created_by=self.accounts['team'],
|
||||||
valid_from=self.now,
|
valid_from=self.now,
|
||||||
valid_to=self.now + timedelta(days=5),
|
valid_to=self.now + timedelta(days=1),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_ok(self):
|
def test_ok(self):
|
||||||
|
@ -794,7 +798,7 @@ class CheckoutUpdateViewTests(ViewTestCaseMixin, TestCase):
|
||||||
name='Checkout',
|
name='Checkout',
|
||||||
valid_from=self.now,
|
valid_from=self.now,
|
||||||
valid_to=self.now + timedelta(days=5),
|
valid_to=self.now + timedelta(days=5),
|
||||||
balance='3.14',
|
balance=Decimal('3.14'),
|
||||||
is_protected=False,
|
is_protected=False,
|
||||||
created_by=self.accounts['team'],
|
created_by=self.accounts['team'],
|
||||||
)
|
)
|
||||||
|
@ -864,6 +868,7 @@ class CheckoutStatementListViewTests(ViewTestCaseMixin, TestCase):
|
||||||
self.assertQuerysetEqual(
|
self.assertQuerysetEqual(
|
||||||
r.context['checkoutstatements'],
|
r.context['checkoutstatements'],
|
||||||
map(repr, expected_statements),
|
map(repr, expected_statements),
|
||||||
|
ordered=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -245,13 +245,7 @@ class ViewTestCaseMixin(TestCaseMixin):
|
||||||
self.register_user(label, user)
|
self.register_user(label, user)
|
||||||
|
|
||||||
if self.auth_user:
|
if self.auth_user:
|
||||||
# The wrapper is a sanity check.
|
self.client.force_login(self.users[self.auth_user])
|
||||||
self.assertTrue(
|
|
||||||
self.client.login(
|
|
||||||
username=self.auth_user,
|
|
||||||
password=self.auth_user,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
del self.users_base
|
del self.users_base
|
||||||
|
|
|
@ -528,15 +528,7 @@ class CheckoutCreate(SuccessMessageMixin, CreateView):
|
||||||
|
|
||||||
# Creating
|
# Creating
|
||||||
form.instance.created_by = self.request.user.profile.account_kfet
|
form.instance.created_by = self.request.user.profile.account_kfet
|
||||||
checkout = form.save()
|
form.save()
|
||||||
|
|
||||||
# Création d'un relevé avec balance initiale
|
|
||||||
CheckoutStatement.objects.create(
|
|
||||||
checkout = checkout,
|
|
||||||
by = self.request.user.profile.account_kfet,
|
|
||||||
balance_old = checkout.balance,
|
|
||||||
balance_new = checkout.balance,
|
|
||||||
amount_taken = 0)
|
|
||||||
|
|
||||||
return super(CheckoutCreate, self).form_valid(form)
|
return super(CheckoutCreate, self).form_valid(form)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Stop if an error is encountered
|
||||||
|
set -e
|
||||||
|
|
||||||
# Configuration de la base de données. Le mot de passe est constant car c'est
|
# Configuration de la base de données. Le mot de passe est constant car c'est
|
||||||
# pour une installation de dév locale qui ne sera accessible que depuis la
|
# pour une installation de dév locale qui ne sera accessible que depuis la
|
||||||
# machine virtuelle.
|
# machine virtuelle.
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Stop if an error is encountered.
|
||||||
|
set -e
|
||||||
|
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
python manage.py loaddata gestion sites articles
|
python manage.py loaddata gestion sites articles
|
||||||
python manage.py loaddevdata
|
python manage.py loaddevdata
|
||||||
|
|
|
@ -11,7 +11,7 @@ psycopg2
|
||||||
Pillow
|
Pillow
|
||||||
six
|
six
|
||||||
unicodecsv
|
unicodecsv
|
||||||
django-bootstrap-form==3.2.1
|
django-bootstrap-form==3.3
|
||||||
asgiref==1.1.1
|
asgiref==1.1.1
|
||||||
daphne==1.3.0
|
daphne==1.3.0
|
||||||
asgi-redis==1.3.0
|
asgi-redis==1.3.0
|
||||||
|
|
1698
shared/static/vendor/jquery.autocomplete-light/3.5.0/dist/jquery.autocomplete-light.js
vendored
Normal file
1698
shared/static/vendor/jquery.autocomplete-light/3.5.0/dist/jquery.autocomplete-light.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
9
shared/static/vendor/jquery.autocomplete-light/3.5.0/dist/jquery.autocomplete-light.min.js
vendored
Normal file
9
shared/static/vendor/jquery.autocomplete-light/3.5.0/dist/jquery.autocomplete-light.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue