mirror of
https://github.com/ae-utbm/sith.git
synced 2024-12-22 15:51:19 +00:00
Add ajax-select app and improve some templates
This commit is contained in:
parent
bfb2dc5f82
commit
7e90e657a7
39
core/lookups.py
Normal file
39
core/lookups.py
Normal file
@ -0,0 +1,39 @@
|
||||
from ajax_select import register, LookupChannel
|
||||
|
||||
from core.views.site import search_user
|
||||
from core.models import User
|
||||
from counter.models import Product, Counter
|
||||
|
||||
@register('users')
|
||||
class UsersLookup(LookupChannel):
|
||||
model = User
|
||||
|
||||
def get_query(self, q, request):
|
||||
return search_user(q)
|
||||
|
||||
def format_match(self, obj):
|
||||
return obj.get_mini_item()
|
||||
|
||||
def format_item_display(self, item):
|
||||
return item.get_display_name()
|
||||
|
||||
@register('counters')
|
||||
class CountersLookup(LookupChannel):
|
||||
model = Counter
|
||||
|
||||
def get_query(self, q, request):
|
||||
return self.model.objects.filter(name__icontains=q)[:50]
|
||||
|
||||
def format_item_display(self, item):
|
||||
return item.name
|
||||
|
||||
@register('products')
|
||||
class ProductsLookup(LookupChannel):
|
||||
model = Product
|
||||
|
||||
def get_query(self, q, request):
|
||||
print(request.__dict__)
|
||||
return (self.model.objects.filter(name__icontains=q) | self.model.objects.filter(code__icontains=q))[:50]
|
||||
|
||||
def format_item_display(self, item):
|
||||
return item.name
|
@ -351,6 +351,20 @@ class User(AbstractBaseUser):
|
||||
def can_be_edited_by(self, user):
|
||||
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_root
|
||||
|
||||
def get_mini_item(self):
|
||||
return """
|
||||
<div class="mini_profile_link" >
|
||||
<span>
|
||||
<img src="%s" alt="%s" />
|
||||
</span>
|
||||
<em>%s</em>
|
||||
</a>
|
||||
""" % (
|
||||
self.profile_pict.get_download_url() if self.profile_pict else "/static/core/img/na.gif",
|
||||
_("Profile"),
|
||||
self.get_display_name(),
|
||||
)
|
||||
|
||||
|
||||
class AnonymousUser(AuthAnonymousUser):
|
||||
def __init__(self, request):
|
||||
|
@ -1,192 +0,0 @@
|
||||
/* ----------------------------------------------------------------------------------------------------
|
||||
Super Form Reset
|
||||
----------------------------------------------------------------------------------------------------*/
|
||||
|
||||
form {
|
||||
margin: 0px auto;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
input,
|
||||
label,
|
||||
select,
|
||||
button,
|
||||
textarea
|
||||
{
|
||||
margin: 1px;
|
||||
border: none;
|
||||
padding: 1px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
white-space: normal;
|
||||
background: none;
|
||||
line-height: 1;
|
||||
|
||||
/* Browsers have different default form fonts */
|
||||
font-size: 13px;
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
label {
|
||||
min-width: 50%;
|
||||
}
|
||||
|
||||
/* Remove the stupid outer glow in Webkit */
|
||||
input:focus
|
||||
{
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* Box Sizing Reset
|
||||
-----------------------------------------------*/
|
||||
|
||||
/* All of our custom controls should be what we expect them to be */
|
||||
input,
|
||||
textarea
|
||||
{
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/* These elements are usually rendered a certain way by the browser */
|
||||
button,
|
||||
input[type=reset],
|
||||
input[type=button],
|
||||
input[type=submit],
|
||||
input[type=checkbox],
|
||||
input[type=radio],
|
||||
select
|
||||
{
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Text Inputs
|
||||
-----------------------------------------------*/
|
||||
|
||||
input[type=date],
|
||||
input[type=datetime],
|
||||
input[type=datetime-local],
|
||||
input[type=email],
|
||||
input[type=month],
|
||||
input[type=number],
|
||||
input[type=password],
|
||||
input[type=range],
|
||||
input[type=search],
|
||||
input[type=tel],
|
||||
input[type=text],
|
||||
input[type=time],
|
||||
input[type=url],
|
||||
input[type=week],
|
||||
textarea
|
||||
{
|
||||
background-color: white;
|
||||
border: 1px solid black;
|
||||
padding: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Button Controls
|
||||
-----------------------------------------------*/
|
||||
|
||||
input[type=checkbox],
|
||||
input[type=radio]
|
||||
{
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
/* File Uploads
|
||||
-----------------------------------------------*/
|
||||
|
||||
input[type=file]
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* Search Input
|
||||
-----------------------------------------------*/
|
||||
|
||||
/* Make webkit render the search input like a normal text field */
|
||||
input[type=search]
|
||||
{
|
||||
-webkit-appearance: textfield;
|
||||
-webkit-box-sizing: content-box;
|
||||
}
|
||||
|
||||
/* Turn off the recent search for webkit. It adds about 15px padding on the left */
|
||||
::-webkit-search-decoration
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Buttons
|
||||
-----------------------------------------------*/
|
||||
|
||||
button,
|
||||
input[type="reset"],
|
||||
input[type="button"],
|
||||
input[type="submit"]
|
||||
{
|
||||
/* Fix IE7 display bug */
|
||||
overflow: visible;
|
||||
width: auto;
|
||||
cursor: pointer;
|
||||
padding: 3px;
|
||||
border: 1px solid black;
|
||||
border-radius: 2px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
input[type="reset"]:hover,
|
||||
input[type="button"]:hover,
|
||||
input[type="submit"]:hover
|
||||
{
|
||||
box-shadow: 0 0 2px #000;
|
||||
}
|
||||
|
||||
button:active,
|
||||
input[type="reset"]:active,
|
||||
input[type="button"]:active,
|
||||
input[type="submit"]:active
|
||||
{
|
||||
box-shadow: inset 0 0 2px #000;
|
||||
}
|
||||
|
||||
/* IE8 and FF freak out if this rule is within another selector */
|
||||
::-webkit-file-upload-button
|
||||
{
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
/* Textarea
|
||||
-----------------------------------------------*/
|
||||
|
||||
textarea
|
||||
{
|
||||
/* Move the label to the top */
|
||||
vertical-align: top;
|
||||
|
||||
/* Turn off scroll bars in IE unless needed */
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Selects
|
||||
-----------------------------------------------*/
|
||||
|
||||
select
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
select[multiple]
|
||||
{
|
||||
/* Move the label to the top */
|
||||
vertical-align: top;
|
||||
}
|
||||
|
@ -160,22 +160,31 @@ tbody>tr:hover {
|
||||
float: right;
|
||||
padding: 10px;
|
||||
}
|
||||
#user_info_container {
|
||||
}
|
||||
#user_info {
|
||||
float: right;
|
||||
padding: 5px;
|
||||
width: 40%;
|
||||
margin: 0px auto;
|
||||
background: lightgrey;
|
||||
}
|
||||
|
||||
/*-----------------------------USER PROFILE----------------------------*/
|
||||
#user_profile {
|
||||
#user_profile_container {
|
||||
width: 80%;
|
||||
margin: 0px auto;
|
||||
}
|
||||
#user_profile {
|
||||
width: 100%;
|
||||
margin: 0px auto;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
#user_profile h4 { border-bottom: 1px solid grey; max-width: 60%; }
|
||||
#user_profile #pictures {
|
||||
width: 30%;
|
||||
max-width: 250px;
|
||||
max-height: 300px;
|
||||
float: right;
|
||||
font-style: italic;
|
||||
}
|
||||
@ -238,7 +247,14 @@ footer{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/*--------------------------------MODALE-------------------------------*/
|
||||
/*---------------------------------FORMS-------------------------------*/
|
||||
form {
|
||||
margin: 0px auto;
|
||||
width: 60%;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
.choose_file_widget {
|
||||
display: none;
|
||||
}
|
||||
@ -247,24 +263,10 @@ footer{
|
||||
position: absolute;
|
||||
width: 97%;
|
||||
}
|
||||
.form-wrapper {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #DDD;
|
||||
opacity: 0.8;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
#user_edit * {
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
.form-wrapper>form {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
left: 30%;
|
||||
top: 40%;
|
||||
background-color: white;
|
||||
#user_edit img {
|
||||
width: 100px;
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
{% block head %}
|
||||
<title>{% block title %}{% trans %}Welcome!{% endtrans %}{% endblock %}</title>
|
||||
<link rel="stylesheet" href="{{ static('core/base.css') }}">
|
||||
<link rel="stylesheet" href="{{ static('core/form.css') }}">
|
||||
<link rel="stylesheet" href="{{ static('core/multiple-select.css') }}">
|
||||
<link rel="stylesheet" href="{{ static('core/js/ui/jquery-ui.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ static('ajax_select/css/ajax_select.css') }}">
|
||||
<link rel="stylesheet" href="{{ static('core/style.css') }}">
|
||||
{% endblock %}
|
||||
</head>
|
||||
@ -70,6 +70,7 @@
|
||||
<script src="{{ static('core/js/jquery-3.1.0.min.js') }}"></script>
|
||||
<script src="{{ static('core/js/ui/jquery-ui.min.js') }}"></script>
|
||||
<script src="{{ static('core/js/multiple-select.js') }}"></script>
|
||||
<script src="{{ static('ajax_select/js/ajax_select.js') }}"></script>
|
||||
<script src="{{ static('core/js/script.js') }}"></script>
|
||||
<script>
|
||||
$('.select_single').multipleSelect({
|
||||
|
@ -14,3 +14,24 @@
|
||||
<em>{{ user.get_display_name() }}</em>
|
||||
</a>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro user_mini_profile(user) %}
|
||||
<div id="user_profile">
|
||||
<div id="pictures">
|
||||
{% if user.profile_pict %}
|
||||
<img src="{{ user.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" />
|
||||
{% endif %}
|
||||
</div>
|
||||
<p>{{ user.get_full_name() }}</p>
|
||||
{% if user.nick_name %}
|
||||
<p id="nickname">« {{ user.nick_name }} »</p>
|
||||
{% endif %}
|
||||
{% if user.date_of_birth %}
|
||||
<p>{% trans %}Born: {% endtrans %}{{ user.date_of_birth|date("d/m/Y") }}</p>
|
||||
{% endif %}
|
||||
{% if user.promo %}
|
||||
<p><img src="{{ static('core/img/promo_%02d.png' % user.promo) }}" alt="Promo {{ user.promo }}" class="promo_pict" />
|
||||
{% trans %}Promo: {% endtrans %}{{ user.promo }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
@ -6,31 +6,33 @@
|
||||
|
||||
{% block infos %}
|
||||
|
||||
<div id="user_profile">
|
||||
<div id="pictures">
|
||||
{% if profile.profile_pict %}
|
||||
<img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" />
|
||||
<div id="user_profile_container">
|
||||
<div id="user_profile">
|
||||
<div id="pictures">
|
||||
{% if profile.profile_pict %}
|
||||
<img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" />
|
||||
{% endif %}
|
||||
<p><em>{{ profile.quote }}</em></p>
|
||||
</div>
|
||||
<h4>{{ profile.get_full_name() }}</h4>
|
||||
{% if profile.nick_name %}
|
||||
<p id="nickname">« {{ profile.nick_name }} »</p>
|
||||
{% endif %}
|
||||
{% if profile.date_of_birth %}
|
||||
<p>{% trans %}Born: {% endtrans %}{{ profile.date_of_birth|date("d/m/Y") }}</p>
|
||||
{% endif %}
|
||||
{% if profile.department != "NA" %}
|
||||
<p>{{ profile.department }}{{ profile.semester }}
|
||||
{% endif %}
|
||||
{% if profile.dpt_option %}
|
||||
<br>{% trans %}Option: {% endtrans %}{{ profile.dpt_option }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% if profile.promo %}
|
||||
<p><img src="{{ static('core/img/promo_%02d.png' % profile.promo) }}" alt="Promo {{ profile.promo }}" class="promo_pict" />
|
||||
{% trans %}Promo: {% endtrans %}{{ profile.promo }}</p>
|
||||
{% endif %}
|
||||
<p><em>{{ profile.quote }}</em></p>
|
||||
</div>
|
||||
<h4>{{ profile.get_full_name() }}</h4>
|
||||
{% if profile.nick_name %}
|
||||
<p id="nickname">« {{ profile.nick_name }} »</p>
|
||||
{% endif %}
|
||||
{% if profile.date_of_birth %}
|
||||
<p>{% trans %}Born: {% endtrans %}{{ profile.date_of_birth|date("d/m/Y") }}</p>
|
||||
{% endif %}
|
||||
{% if profile.department != "NA" %}
|
||||
<p>{{ profile.department }}{{ profile.semester }}
|
||||
{% endif %}
|
||||
{% if profile.dpt_option %}
|
||||
<br>{% trans %}Option: {% endtrans %}{{ profile.dpt_option }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% if profile.promo %}
|
||||
<p><img src="{{ static('core/img/promo_%02d.png' % profile.promo) }}" alt="Promo {{ profile.promo }}" class="promo_pict" />
|
||||
{% trans %}Promo: {% endtrans %}{{ profile.promo }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if user.membership.filter(end_date=None).exists() or user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) %}
|
||||
|
@ -6,19 +6,19 @@
|
||||
|
||||
{% block infos %}
|
||||
<h2>{% trans %}Edit user profile{% endtrans %}</h2>
|
||||
<form action="" method="post" enctype="multipart/form-data">
|
||||
<form action="" method="post" enctype="multipart/form-data" id="user_edit">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<p>{{ field.errors }}<label for="{{ field.name }}">{{ field.label }}
|
||||
{%- if field.name == "profile_pict" and form.instance.profile_pict -%}
|
||||
<br>{% trans %}Current profile: {% endtrans %}
|
||||
<img src="{{ form.instance.profile_pict.get_download_url() }}" title="{% trans %}Profile{% endtrans %}" width="50px" /><br>
|
||||
<img src="{{ form.instance.profile_pict.get_download_url() }}" title="{% trans %}Profile{% endtrans %}" /><br>
|
||||
{%- elif field.name == "avatar_pict" and form.instance.avatar_pict -%}
|
||||
<br>{% trans %}Current avatar: {% endtrans %}
|
||||
<img src="{{ form.instance.avatar_pict.get_download_url() }}" title="{% trans %}Avatar{% endtrans %}" width="50px" /><br>
|
||||
<img src="{{ form.instance.avatar_pict.get_download_url() }}" title="{% trans %}Avatar{% endtrans %}" /><br>
|
||||
{%- elif field.name == "scrub_pict" and form.instance.scrub_pict -%}
|
||||
<br>{% trans %}Current scrub: {% endtrans %}
|
||||
<img src="{{ form.instance.scrub_pict.get_download_url() }}" title="{% trans %}Scrub{% endtrans %}" width="50px" /><br>
|
||||
<img src="{{ form.instance.scrub_pict.get_download_url() }}" title="{% trans %}Scrub{% endtrans %}" /><br>
|
||||
{%- endif %}</label> {{ field }}</p>
|
||||
{% endfor %}
|
||||
<p><input type="submit" value="{% trans %}Update{% endtrans %}" /></p>
|
||||
|
@ -1,19 +1,3 @@
|
||||
<div id="user_profile">
|
||||
<div id="pictures">
|
||||
{% if profile.profile_pict %}
|
||||
<img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" />
|
||||
{% endif %}
|
||||
</div>
|
||||
<p>{{ profile.get_full_name() }}</p>
|
||||
{% if profile.nick_name %}
|
||||
<p id="nickname">« {{ profile.nick_name }} »</p>
|
||||
{% endif %}
|
||||
{% if profile.date_of_birth %}
|
||||
<p>{% trans %}Born: {% endtrans %}{{ profile.date_of_birth|date("d/m/Y") }}</p>
|
||||
{% endif %}
|
||||
{% if profile.promo %}
|
||||
<p><img src="{{ static('core/img/promo_%02d.png' % profile.promo) }}" alt="Promo {{ profile.promo }}" class="promo_pict" />
|
||||
{% trans %}Promo: {% endtrans %}{{ profile.promo }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% from "core/macros.jinja" import user_mini_profile %}
|
||||
|
||||
{{ user_mini_profile(profile) }}
|
||||
|
@ -6,6 +6,7 @@ urlpatterns = [
|
||||
url(r'^$', index, name='index'),
|
||||
url(r'^search/$', search_view, name='search'),
|
||||
url(r'^search_json/$', search_json, name='search_json'),
|
||||
url(r'^search_user/$', search_user_json, name='search_user'),
|
||||
|
||||
# Login and co
|
||||
url(r'^login/$', login, name='login'),
|
||||
|
@ -52,6 +52,20 @@ class SelectFile(TextInput):
|
||||
output += '<span name="' + name + '" class="choose_file_button">' + _("Choose file") + '</span>'
|
||||
return output
|
||||
|
||||
class SelectUser(TextInput):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
attrs['class'] = "select_user"
|
||||
else:
|
||||
attrs = {'class': "select_user"}
|
||||
output = '%(content)s<div name="%(name)s" class="choose_user_widget" title="%(title)s"></div>' % {
|
||||
'content': super(SelectUser, self).render(name, value, attrs),
|
||||
'title': _("Choose user"),
|
||||
'name': name,
|
||||
}
|
||||
output += '<span name="' + name + '" class="choose_user_button">' + _("Choose user") + '</span>'
|
||||
return output
|
||||
|
||||
# Forms
|
||||
|
||||
class RegisteringForm(UserCreationForm):
|
||||
|
@ -15,36 +15,57 @@ from club.models import Club
|
||||
def index(request, context=None):
|
||||
return render(request, "core/index.jinja")
|
||||
|
||||
def search(query, as_json=False):
|
||||
result = {'users': None, 'clubs': None}
|
||||
def search_user(query, as_json=False):
|
||||
users = []
|
||||
if query:
|
||||
exact_nick = User.objects.filter(nick_name__iexact=query).all()
|
||||
nicks = User.objects.filter(nick_name__icontains=query).exclude(id__in=exact_nick).all()
|
||||
users = User.objects.filter(Q(first_name__icontains=query) |
|
||||
Q(last_name__icontains=query)).exclude(id__in=exact_nick).exclude(id__in=nicks).all()
|
||||
clubs = Club.objects.filter(name__icontains=query).all()
|
||||
nicks = nicks[:5]
|
||||
users = users[:5]
|
||||
clubs = clubs[:5]
|
||||
if as_json: # Re-loads json to avoid double encoding by JsonResponse, but still benefit from serializers
|
||||
exact_nick = json.loads(serializers.serialize('json', exact_nick, fields=('nick_name', 'last_name', 'first_name', 'profile_pict')))
|
||||
nicks = json.loads(serializers.serialize('json', nicks, fields=('nick_name', 'last_name', 'first_name', 'profile_pict')))
|
||||
users = json.loads(serializers.serialize('json', users, fields=('nick_name', 'last_name', 'first_name', 'profile_pict')))
|
||||
clubs = json.loads(serializers.serialize('json', clubs, fields=('name')))
|
||||
else:
|
||||
exact_nick = list(exact_nick)
|
||||
nicks = list(nicks)
|
||||
users = list(users)
|
||||
users = exact_nick + nicks + users
|
||||
return users
|
||||
|
||||
def search_club(query, as_json=False):
|
||||
clubs = []
|
||||
if query:
|
||||
clubs = Club.objects.filter(name__icontains=query).all()
|
||||
clubs = clubs[:5]
|
||||
if as_json: # Re-loads json to avoid double encoding by JsonResponse, but still benefit from serializers
|
||||
clubs = json.loads(serializers.serialize('json', clubs, fields=('name')))
|
||||
else:
|
||||
clubs = list(clubs)
|
||||
result['users'] = exact_nick + nicks + users
|
||||
result['clubs'] = clubs
|
||||
return result
|
||||
return clubs
|
||||
|
||||
@login_required
|
||||
def search_view(request):
|
||||
return render(request, "core/search.jinja", context={'result': search(request.GET.get('query', ''))})
|
||||
result = {
|
||||
'users': search_user(request.GET.get('query', '')),
|
||||
'clubs': search_club(request.GET.get('query', '')),
|
||||
}
|
||||
return render(request, "core/search.jinja", context={'result': result})
|
||||
|
||||
@login_required
|
||||
def search_user_json(request):
|
||||
result = {
|
||||
'users': search_user(request.GET.get('query', ''), True),
|
||||
}
|
||||
return JsonResponse(result)
|
||||
|
||||
@login_required
|
||||
def search_json(request):
|
||||
return JsonResponse(search(request.GET.get('query', ''), True))
|
||||
result = {
|
||||
'users': search_user(request.GET.get('query', ''), True),
|
||||
'clubs': search_club(request.GET.get('query', ''), True),
|
||||
}
|
||||
return JsonResponse(result)
|
||||
|
||||
|
24
counter/migrations/0014_auto_20160819_1650.py
Normal file
24
counter/migrations/0014_auto_20160819_1650.py
Normal file
@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('counter', '0013_auto_20160818_1736'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='refilling',
|
||||
name='date',
|
||||
field=models.DateTimeField(verbose_name='date'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='selling',
|
||||
name='date',
|
||||
field=models.DateTimeField(verbose_name='date'),
|
||||
),
|
||||
]
|
@ -1,4 +1,6 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
{% from "core/macros.jinja" import user_mini_profile %}
|
||||
|
||||
|
||||
{% macro add_product(id, content) %}
|
||||
<form method="post" action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}" class="inline" style="display:inline">
|
||||
@ -21,13 +23,12 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{% trans %}Counter{% endtrans %}</h3>
|
||||
<h4>{{ counter }}</h4>
|
||||
<p><strong>{% trans %}Club: {% endtrans %}</strong> {{ counter.club }}</p>
|
||||
|
||||
<div>
|
||||
<div id="user_info">
|
||||
<h5>{% trans %}Customer{% endtrans %}</h5>
|
||||
<p>{{ customer.user.get_display_name() }}, {{ customer.amount }} €</p>
|
||||
{{ user_mini_profile(customer.user) }}
|
||||
<p>{% trans %}Amount: {% endtrans %}{{ customer.amount }} €</p>
|
||||
</div>
|
||||
{% if counter.type == 'BAR' %}
|
||||
<div>
|
||||
@ -48,7 +49,7 @@
|
||||
<form method="post" action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="code">
|
||||
<input type="input" name="code" value="" autofocus />
|
||||
<input type="input" name="code" value="" autofocus id="code_field"/>
|
||||
<input type="submit" value="{% trans %}Go{% endtrans %}" />
|
||||
</form>
|
||||
<p>{% trans %}Basket: {% endtrans %}</p>
|
||||
@ -79,5 +80,45 @@
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
$( function() {
|
||||
var products = [
|
||||
{% for p in counter.products.all() %}
|
||||
{
|
||||
value: "{{ p.code }}",
|
||||
label: "{{ p.name }}",
|
||||
tags: "{{ p.code }} {{ p.name }}",
|
||||
},
|
||||
{% endfor %}
|
||||
];
|
||||
var quantity = "";
|
||||
var search = "";
|
||||
var pattern = /^(\d+x)?(.*)/i;
|
||||
$( "#code_field" ).autocomplete({
|
||||
select: function (event, ui) {
|
||||
event.preventDefault();
|
||||
$("#code_field").val(quantity + ui.item.value);
|
||||
},
|
||||
focus: function (event, ui) {
|
||||
event.preventDefault();
|
||||
$("#code_field").val(quantity + ui.item.value);
|
||||
},
|
||||
source: function( request, response ) {
|
||||
var res = pattern.exec(request.term);
|
||||
quantity = res[1] || "";
|
||||
search = res[2];
|
||||
var matcher = new RegExp( $.ui.autocomplete.escapeRegex( search ), "i" );
|
||||
response($.grep( products, function( value ) {
|
||||
value = value.tags;
|
||||
return matcher.test( value );
|
||||
}));
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
19
counter/templates/counter/counter_edit.jinja
Normal file
19
counter/templates/counter/counter_edit.jinja
Normal file
@ -0,0 +1,19 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
|
||||
{% block title %}
|
||||
{% trans obj=object %}Edit {{ obj }}{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% trans obj=object %}Edit {{ obj }}{% endtrans %}</h2>
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<p>{{ form.sellers.errors }}<label for="{{ form.sellers.name }}">{{ form.sellers.label }}</label> {{ form.sellers }}</p>
|
||||
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
|
||||
<p>{{ form.products.errors }}<label for="{{ form.products.name }}">{{ form.products.label }}</label> {{ form.products }}</p>
|
||||
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
@ -10,13 +10,19 @@
|
||||
<h3>{% trans %}Counter admin list{% endtrans %}</h3>
|
||||
<ul>
|
||||
{% for c in counter_list %}
|
||||
<li>
|
||||
{% if c.type == "EBOUTIC" %}
|
||||
<li><a href="{{ url('eboutic:main') }}">{{ c }}</a> -
|
||||
<a href="{{ url('counter:admin', counter_id=c.id) }}">{% trans %}Edit{% endtrans %}</a></li>
|
||||
<a href="{{ url('eboutic:main') }}">{{ c }}</a> -
|
||||
{% else %}
|
||||
<li><a href="{{ url('counter:details', counter_id=c.id) }}">{{ c }}</a> -
|
||||
<a href="{{ url('counter:admin', counter_id=c.id) }}">{% trans %}Edit{% endtrans %}</a></li>
|
||||
<a href="{{ url('counter:details', counter_id=c.id) }}">{{ c }}</a> -
|
||||
{% endif %}
|
||||
{% if user.can_edit(c) %}
|
||||
<a href="{{ url('counter:admin', counter_id=c.id) }}">{% trans %}Edit{% endtrans %}</a> -
|
||||
{% endif %}
|
||||
{% if user.is_owner(c) %}
|
||||
<a href="{{ url('counter:prop_admin', counter_id=c.id) }}">{% trans %}Props{% endtrans %}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
|
@ -8,6 +8,7 @@ urlpatterns = [
|
||||
url(r'^(?P<counter_id>[0-9]+)/login$', CounterLogin.as_view(), name='login'),
|
||||
url(r'^(?P<counter_id>[0-9]+)/logout$', CounterLogout.as_view(), name='logout'),
|
||||
url(r'^admin/(?P<counter_id>[0-9]+)$', CounterEditView.as_view(), name='admin'),
|
||||
url(r'^admin/(?P<counter_id>[0-9]+)/prop$', CounterEditPropView.as_view(), name='prop_admin'),
|
||||
url(r'^admin$', CounterListView.as_view(), name='admin_list'),
|
||||
url(r'^admin/new$', CounterCreateView.as_view(), name='new'),
|
||||
url(r'^admin/delete/(?P<counter_id>[0-9]+)$', CounterDeleteView.as_view(), name='delete'),
|
||||
|
@ -14,8 +14,11 @@ from django.db import DataError, transaction
|
||||
|
||||
import re
|
||||
from datetime import date, timedelta
|
||||
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
||||
from ajax_select import make_ajax_form, make_ajax_field
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
|
||||
from core.views.forms import SelectUser
|
||||
from subscription.models import Subscriber
|
||||
from subscription.views import get_subscriber
|
||||
from counter.models import Counter, Customer, Product, Selling, Refilling, ProductType
|
||||
@ -29,8 +32,7 @@ class GetUserForm(forms.Form):
|
||||
some nickname, first name, or last name (TODO)
|
||||
"""
|
||||
code = forms.CharField(label="Code", max_length=10, required=False)
|
||||
id = forms.IntegerField(label="ID", required=False)
|
||||
# TODO: add a nice JS widget to search for users
|
||||
id = AutoCompleteSelectField('users', required=False, label=_("Select user"), help_text=None)
|
||||
|
||||
def as_p(self):
|
||||
self.fields['code'].widget.attrs['autofocus'] = True
|
||||
@ -327,15 +329,31 @@ class CounterListView(CanViewMixin, ListView):
|
||||
model = Counter
|
||||
template_name = 'counter/counter_list.jinja'
|
||||
|
||||
class CounterEditView(CanEditPropMixin, UpdateView):
|
||||
class CounterEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Counter
|
||||
fields = ['sellers', 'products']
|
||||
sellers = make_ajax_field(Counter, 'sellers', 'users', show_help_text=False)
|
||||
products = make_ajax_field(Counter, 'products', 'products')
|
||||
|
||||
class CounterEditView(CanEditMixin, UpdateView):
|
||||
"""
|
||||
Edit a counter's main informations (for the counter's manager)
|
||||
"""
|
||||
model = Counter
|
||||
form_class = CounterEditForm
|
||||
pk_url_kwarg = "counter_id"
|
||||
template_name = 'counter/counter_edit.jinja'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('counter:admin', kwargs={'counter_id': self.object.id})
|
||||
|
||||
class CounterEditPropView(CanEditPropMixin, UpdateView):
|
||||
"""
|
||||
Edit a counter's main informations (for the counter's admin)
|
||||
"""
|
||||
model = Counter
|
||||
form_class = modelform_factory(Counter, fields=['name', 'club', 'type', 'sellers', 'products'],
|
||||
widgets={
|
||||
'products':CheckboxSelectMultiple,
|
||||
'sellers':CheckboxSelectMultiple})
|
||||
form_class = modelform_factory(Counter, fields=['name', 'club', 'type'])
|
||||
pk_url_kwarg = "counter_id"
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
@ -399,13 +417,20 @@ class ProductCreateView(CanCreateMixin, CreateView):
|
||||
'selling_price', 'special_selling_price', 'icon', 'club']
|
||||
template_name = 'core/create.jinja'
|
||||
|
||||
class ProductEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Product
|
||||
fields = ['name', 'description', 'product_type', 'code', 'purchase_price',
|
||||
'selling_price', 'special_selling_price', 'icon', 'club']
|
||||
counters = make_ajax_field(Product, 'counters', 'counters', show_help_text=False, label='Counters', help_text="Guy",
|
||||
required=False) # TODO FIXME
|
||||
|
||||
class ProductEditView(CanEditPropMixin, UpdateView):
|
||||
"""
|
||||
An edit view for the admins
|
||||
"""
|
||||
model = Product
|
||||
fields = ['name', 'description', 'product_type', 'code', 'purchase_price',
|
||||
'selling_price', 'special_selling_price', 'icon', 'club']
|
||||
form_class = ProductEditForm
|
||||
pk_url_kwarg = "product_id"
|
||||
template_name = 'core/edit.jinja'
|
||||
# TODO: add management of the 'counters' ForeignKey
|
||||
|
Binary file not shown.
@ -6,7 +6,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-08-19 01:43+0200\n"
|
||||
"POT-Creation-Date: 2016-08-19 23:14+0200\n"
|
||||
"PO-Revision-Date: 2016-07-18\n"
|
||||
"Last-Translator: Skia <skia@libskia.so>\n"
|
||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||
@ -66,7 +66,7 @@ msgstr "montant effectif"
|
||||
msgid "number"
|
||||
msgstr "numéro"
|
||||
|
||||
#: accounting/models.py:154 core/models.py:404 core/models.py:680
|
||||
#: accounting/models.py:154 core/models.py:418 core/models.py:694
|
||||
#: counter/models.py:209 counter/models.py:255 eboutic/models.py:14
|
||||
#: eboutic/models.py:47
|
||||
msgid "date"
|
||||
@ -122,7 +122,7 @@ msgstr "Compte"
|
||||
msgid "Company"
|
||||
msgstr "Entreprise"
|
||||
|
||||
#: accounting/models.py:163 sith/settings.py:280 sith/settings_sample.py:266
|
||||
#: accounting/models.py:163 sith/settings.py:284 sith/settings_sample.py:267
|
||||
msgid "Other"
|
||||
msgstr "Autre"
|
||||
|
||||
@ -209,8 +209,7 @@ msgstr "Nouveau compte club"
|
||||
#: club/templates/club/club_detail.jinja:7 core/templates/core/file.jinja:38
|
||||
#: core/templates/core/page.jinja:31 core/templates/core/user_base.jinja:10
|
||||
#: core/templates/core/user_tools.jinja:33
|
||||
#: counter/templates/counter/counter_list.jinja:15
|
||||
#: counter/templates/counter/counter_list.jinja:18
|
||||
#: counter/templates/counter/counter_list.jinja:20
|
||||
#: launderette/templates/launderette/launderette_list.jinja:14
|
||||
msgid "Edit"
|
||||
msgstr "Éditer"
|
||||
@ -303,6 +302,7 @@ msgstr "Voir"
|
||||
|
||||
#: accounting/templates/accounting/journal_details.jinja:10
|
||||
#: core/templates/core/user_account.jinja:10
|
||||
#: counter/templates/counter/counter_click.jinja:31
|
||||
msgid "Amount: "
|
||||
msgstr "Montant: "
|
||||
|
||||
@ -447,6 +447,8 @@ msgstr "Éditer le club"
|
||||
#: core/templates/core/create.jinja:12 core/templates/core/edit.jinja:12
|
||||
#: core/templates/core/file_edit.jinja:8 core/templates/core/page_prop.jinja:8
|
||||
#: core/templates/core/pagerev_edit.jinja:24
|
||||
#: counter/templates/counter/counter_edit.jinja:12
|
||||
#: counter/templates/counter/counter_edit.jinja:14
|
||||
#: subscription/templates/subscription/subscription.jinja:22
|
||||
msgid "Save"
|
||||
msgstr "Sauver"
|
||||
@ -769,109 +771,116 @@ msgstr "adresse des parents"
|
||||
msgid "A user with that username already exists"
|
||||
msgstr "Un utilisateur de ce nom d'utilisateur existe déjà"
|
||||
|
||||
#: core/models.py:381
|
||||
#: core/models.py:364 core/templates/core/macros.jinja:9
|
||||
#: core/templates/core/macros.jinja:11 core/templates/core/macros.jinja:22
|
||||
#: core/templates/core/user_detail.jinja:13
|
||||
#: core/templates/core/user_edit.jinja:15
|
||||
msgid "Profile"
|
||||
msgstr "Profil"
|
||||
|
||||
#: core/models.py:395
|
||||
msgid "Visitor"
|
||||
msgstr "Visiteur"
|
||||
|
||||
#: core/models.py:386
|
||||
#: core/models.py:400
|
||||
msgid "define if we show a users stats"
|
||||
msgstr "Definit si l'on montre les statistiques de l'utilisateur"
|
||||
|
||||
#: core/models.py:388
|
||||
#: core/models.py:402
|
||||
msgid "Show your account statistics to others"
|
||||
msgstr "Montrez vos statistiques de compte aux autres"
|
||||
|
||||
#: core/models.py:395
|
||||
#: core/models.py:409
|
||||
msgid "file name"
|
||||
msgstr "nom du fichier"
|
||||
|
||||
#: core/models.py:396 core/models.py:529
|
||||
#: core/models.py:410 core/models.py:543
|
||||
msgid "parent"
|
||||
msgstr "parent"
|
||||
|
||||
#: core/models.py:397 core/models.py:407
|
||||
#: core/models.py:411 core/models.py:421
|
||||
msgid "file"
|
||||
msgstr "fichier"
|
||||
|
||||
#: core/models.py:398
|
||||
#: core/models.py:412
|
||||
msgid "owner"
|
||||
msgstr "propriétaire"
|
||||
|
||||
#: core/models.py:399 core/models.py:535
|
||||
#: core/models.py:413 core/models.py:549
|
||||
msgid "edit group"
|
||||
msgstr "groupe d'édition"
|
||||
|
||||
#: core/models.py:400 core/models.py:536
|
||||
#: core/models.py:414 core/models.py:550
|
||||
msgid "view group"
|
||||
msgstr "groupe de vue"
|
||||
|
||||
#: core/models.py:401
|
||||
#: core/models.py:415
|
||||
msgid "is folder"
|
||||
msgstr "est un dossier"
|
||||
|
||||
#: core/models.py:402
|
||||
#: core/models.py:416
|
||||
msgid "mime type"
|
||||
msgstr "type mime"
|
||||
|
||||
#: core/models.py:403
|
||||
#: core/models.py:417
|
||||
msgid "size"
|
||||
msgstr "taille"
|
||||
|
||||
#: core/models.py:433
|
||||
#: core/models.py:447
|
||||
msgid "Character '/' not authorized in name"
|
||||
msgstr "Le caractère '/' n'est pas autorisé dans les noms de fichier"
|
||||
|
||||
#: core/models.py:436 core/models.py:441
|
||||
#: core/models.py:450 core/models.py:455
|
||||
msgid "Loop in folder tree"
|
||||
msgstr "Boucle dans l'arborescence des dossiers"
|
||||
|
||||
#: core/models.py:445
|
||||
#: core/models.py:459
|
||||
msgid "You can not make a file be a children of a non folder file"
|
||||
msgstr ""
|
||||
"Vous ne pouvez pas mettre un fichier enfant de quelque chose qui n'est pas "
|
||||
"un dossier"
|
||||
|
||||
#: core/models.py:449
|
||||
#: core/models.py:463
|
||||
msgid "Duplicate file"
|
||||
msgstr "Un fichier de ce nom existe déjà"
|
||||
|
||||
#: core/models.py:459
|
||||
#: core/models.py:473
|
||||
msgid "You must provide a file"
|
||||
msgstr "Vous devez fournir un fichier"
|
||||
|
||||
#: core/models.py:484
|
||||
#: core/models.py:498
|
||||
msgid "Folder: "
|
||||
msgstr "Dossier : "
|
||||
|
||||
#: core/models.py:486
|
||||
#: core/models.py:500
|
||||
msgid "File: "
|
||||
msgstr "Fichier : "
|
||||
|
||||
#: core/models.py:528 core/models.py:532
|
||||
#: core/models.py:542 core/models.py:546
|
||||
msgid "page name"
|
||||
msgstr "nom de la page"
|
||||
|
||||
#: core/models.py:533
|
||||
#: core/models.py:547
|
||||
msgid "owner group"
|
||||
msgstr "groupe propriétaire"
|
||||
|
||||
#: core/models.py:564
|
||||
#: core/models.py:578
|
||||
msgid "Duplicate page"
|
||||
msgstr "Une page de ce nom existe déjà"
|
||||
|
||||
#: core/models.py:570
|
||||
#: core/models.py:584
|
||||
msgid "Loop in page tree"
|
||||
msgstr "Boucle dans l'arborescence des pages"
|
||||
|
||||
#: core/models.py:677
|
||||
#: core/models.py:691
|
||||
msgid "revision"
|
||||
msgstr "révision"
|
||||
|
||||
#: core/models.py:678
|
||||
#: core/models.py:692
|
||||
msgid "page title"
|
||||
msgstr "titre de la page"
|
||||
|
||||
#: core/models.py:679
|
||||
#: core/models.py:693
|
||||
msgid "page content"
|
||||
msgstr "contenu de la page"
|
||||
|
||||
@ -883,7 +892,7 @@ msgstr "403. Non autorisé"
|
||||
msgid "404, Not Found"
|
||||
msgstr "404. Non trouvé"
|
||||
|
||||
#: core/templates/core/base.jinja:5
|
||||
#: core/templates/core/base.jinja:5 core/templates/core/index.jinja:4
|
||||
msgid "Welcome!"
|
||||
msgstr "Bienvenue!"
|
||||
|
||||
@ -908,27 +917,31 @@ msgstr "Outils"
|
||||
msgid "Logout"
|
||||
msgstr "Déconnexion"
|
||||
|
||||
#: core/templates/core/base.jinja:36 core/templates/core/search.jinja:10
|
||||
#: core/templates/core/base.jinja:28 core/templates/core/base.jinja.py:29
|
||||
msgid "Search"
|
||||
msgstr "Recherche"
|
||||
|
||||
#: core/templates/core/base.jinja:40 core/templates/core/search.jinja:10
|
||||
msgid "Users"
|
||||
msgstr "Utilisateurs"
|
||||
|
||||
#: core/templates/core/base.jinja:37
|
||||
#: core/templates/core/base.jinja:41
|
||||
msgid "Wiki"
|
||||
msgstr "Wiki"
|
||||
|
||||
#: core/templates/core/base.jinja:38
|
||||
#: core/templates/core/base.jinja:42
|
||||
msgid "Pages"
|
||||
msgstr "Pages"
|
||||
|
||||
#: core/templates/core/base.jinja:39 core/templates/core/search.jinja:18
|
||||
#: core/templates/core/base.jinja:43 core/templates/core/search.jinja:18
|
||||
msgid "Clubs"
|
||||
msgstr "Clubs"
|
||||
|
||||
#: core/templates/core/base.jinja:40
|
||||
#: core/templates/core/base.jinja:44
|
||||
msgid "Services"
|
||||
msgstr "Services"
|
||||
|
||||
#: core/templates/core/base.jinja:56
|
||||
#: core/templates/core/base.jinja:60
|
||||
msgid "Site made by good people"
|
||||
msgstr "Site réalisé par des gens bons"
|
||||
|
||||
@ -957,12 +970,14 @@ msgstr "Confirmation"
|
||||
|
||||
#: core/templates/core/delete_confirm.jinja:14
|
||||
#: core/templates/core/file_delete_confirm.jinja:14
|
||||
#: counter/templates/counter/counter_click.jinja:71
|
||||
#: counter/templates/counter/counter_click.jinja:72
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: core/templates/core/edit.jinja:4 core/templates/core/edit.jinja.py:8
|
||||
#: core/templates/core/file_edit.jinja:4
|
||||
#: counter/templates/counter/counter_edit.jinja:4
|
||||
#: counter/templates/counter/counter_edit.jinja:8
|
||||
#, python-format
|
||||
msgid "Edit %(obj)s"
|
||||
msgstr "Éditer %(obj)s"
|
||||
@ -1038,7 +1053,7 @@ msgstr "Liste des groupes"
|
||||
msgid "New group"
|
||||
msgstr "Nouveau groupe"
|
||||
|
||||
#: core/templates/core/index.jinja:6
|
||||
#: core/templates/core/index.jinja:7
|
||||
msgid "Hello, world. You're at the core index using Jinja2."
|
||||
msgstr "Hello, world! Vous êtes sur la page d'accueil utilisant Jinja2."
|
||||
|
||||
@ -1069,12 +1084,15 @@ msgstr "login"
|
||||
msgid "Lost password?"
|
||||
msgstr "Mot de passe perdu ?"
|
||||
|
||||
#: core/templates/core/macros.jinja:9 core/templates/core/macros.jinja:11
|
||||
#: core/templates/core/user_detail.jinja:12
|
||||
#: core/templates/core/user_edit.jinja:15
|
||||
#: core/templates/core/user_mini.jinja:4
|
||||
msgid "Profile"
|
||||
msgstr "Profil"
|
||||
#: core/templates/core/macros.jinja:30
|
||||
#: core/templates/core/user_detail.jinja:22
|
||||
msgid "Born: "
|
||||
msgstr "Né le : "
|
||||
|
||||
#: core/templates/core/macros.jinja:34
|
||||
#: core/templates/core/user_detail.jinja:33
|
||||
msgid "Promo: "
|
||||
msgstr "Promo : "
|
||||
|
||||
#: core/templates/core/page.jinja:7 core/templates/core/page_list.jinja:4
|
||||
#: core/templates/core/page_list.jinja:9
|
||||
@ -1247,7 +1265,6 @@ msgstr "Rechargements"
|
||||
|
||||
#: core/templates/core/user_account.jinja:17
|
||||
#: core/templates/core/user_account.jinja:45
|
||||
#: counter/templates/counter/counter_click.jinja:24
|
||||
msgid "Counter"
|
||||
msgstr "Comptoir"
|
||||
|
||||
@ -1299,30 +1316,20 @@ msgstr "Groupes"
|
||||
msgid "%(user_name)s's profile"
|
||||
msgstr "Profil de %(user_name)s"
|
||||
|
||||
#: core/templates/core/user_detail.jinja:21
|
||||
#: core/templates/core/user_mini.jinja:12
|
||||
msgid "Born: "
|
||||
msgstr "Né le : "
|
||||
|
||||
#: core/templates/core/user_detail.jinja:27
|
||||
#: core/templates/core/user_detail.jinja:28
|
||||
msgid "Option: "
|
||||
msgstr "Filière : "
|
||||
|
||||
#: core/templates/core/user_detail.jinja:32
|
||||
#: core/templates/core/user_mini.jinja:16
|
||||
msgid "Promo: "
|
||||
msgstr "Promo : "
|
||||
|
||||
#: core/templates/core/user_detail.jinja:40
|
||||
#: core/templates/core/user_detail.jinja:42
|
||||
#, python-format
|
||||
msgid "User is subscriber until %(subscription_end)s"
|
||||
msgstr "L'utilisateur est cotisant jusqu'au %(subscription_end)s"
|
||||
|
||||
#: core/templates/core/user_detail.jinja:42
|
||||
#: core/templates/core/user_detail.jinja:44
|
||||
msgid "User is not subscribed. "
|
||||
msgstr "L'utilisateur n'est pas cotisant."
|
||||
|
||||
#: core/templates/core/user_detail.jinja:43
|
||||
#: core/templates/core/user_detail.jinja:45
|
||||
#: subscription/templates/subscription/subscription.jinja:4
|
||||
#: subscription/templates/subscription/subscription.jinja:8
|
||||
msgid "New subscription"
|
||||
@ -1382,6 +1389,7 @@ msgid "User list"
|
||||
msgstr "Liste d'utilisateurs"
|
||||
|
||||
#: core/templates/core/user_stats.jinja:4
|
||||
#, python-format
|
||||
msgid "%(user_name)s's stats"
|
||||
msgstr "Stats de %(user_name)s"
|
||||
|
||||
@ -1439,7 +1447,7 @@ msgstr "Ajouter un nouveau dossier"
|
||||
msgid "Error creating folder %(folder_name)s: %(msg)s"
|
||||
msgstr "Erreur de création du dossier %(folder_name)s : %(msg)s"
|
||||
|
||||
#: core/views/files.py:62 core/views/forms.py:154 core/views/forms.py:158
|
||||
#: core/views/files.py:62 core/views/forms.py:168 core/views/forms.py:172
|
||||
#, python-format
|
||||
msgid "Error uploading file %(file_name)s: %(msg)s"
|
||||
msgstr "Erreur d'envoie du fichier %(file_name)s : %(msg)s"
|
||||
@ -1448,7 +1456,11 @@ msgstr "Erreur d'envoie du fichier %(file_name)s : %(msg)s"
|
||||
msgid "Choose file"
|
||||
msgstr "Choisir un fichier"
|
||||
|
||||
#: core/views/forms.py:113
|
||||
#: core/views/forms.py:63 core/views/forms.py:66
|
||||
msgid "Choose user"
|
||||
msgstr "Choisir un utilisateur"
|
||||
|
||||
#: core/views/forms.py:127
|
||||
msgid ""
|
||||
"Profile: you need to be visible on the picture, in order to be recognized (e."
|
||||
"g. by the barmen)"
|
||||
@ -1456,15 +1468,15 @@ msgstr ""
|
||||
"Photo de profil: vous devez être visible sur la photo afin d'être reconnu "
|
||||
"(par exemple par les barmen)"
|
||||
|
||||
#: core/views/forms.py:114
|
||||
#: core/views/forms.py:128
|
||||
msgid "Avatar: used on the forum"
|
||||
msgstr "Avatar : utilisé sur le forum"
|
||||
|
||||
#: core/views/forms.py:115
|
||||
#: core/views/forms.py:129
|
||||
msgid "Scrub: let other know how your scrub looks like!"
|
||||
msgstr "Blouse : montrez aux autres à quoi ressemble votre blouse !"
|
||||
|
||||
#: core/views/forms.py:159
|
||||
#: core/views/forms.py:173
|
||||
msgid "Bad image format, only jpeg, png, and gif are accepted"
|
||||
msgstr "Mauvais format d'image, seuls les jpeg, png, et gif sont acceptés"
|
||||
|
||||
@ -1480,7 +1492,7 @@ msgstr "client"
|
||||
msgid "customers"
|
||||
msgstr "clients"
|
||||
|
||||
#: counter/models.py:44 counter/templates/counter/counter_click.jinja:46
|
||||
#: counter/models.py:44 counter/templates/counter/counter_click.jinja:47
|
||||
msgid "Not enough money"
|
||||
msgstr "Solde insuffisant"
|
||||
|
||||
@ -1520,7 +1532,7 @@ msgstr "Bureau"
|
||||
#: eboutic/templates/eboutic/eboutic_main.jinja:24
|
||||
#: eboutic/templates/eboutic/eboutic_makecommand.jinja:8
|
||||
#: eboutic/templates/eboutic/eboutic_payment_result.jinja:4
|
||||
#: sith/settings.py:279 sith/settings_sample.py:265
|
||||
#: sith/settings.py:283 sith/settings_sample.py:266
|
||||
msgid "Eboutic"
|
||||
msgstr "Eboutic"
|
||||
|
||||
@ -1556,9 +1568,9 @@ msgstr "quantité"
|
||||
msgid "Sith account"
|
||||
msgstr "Compte utilisateur"
|
||||
|
||||
#: counter/models.py:257 sith/settings.py:272 sith/settings.py:277
|
||||
#: sith/settings.py:298 sith/settings_sample.py:258
|
||||
#: sith/settings_sample.py:263 sith/settings_sample.py:284
|
||||
#: counter/models.py:257 sith/settings.py:276 sith/settings.py:281
|
||||
#: sith/settings.py:302 sith/settings_sample.py:259
|
||||
#: sith/settings_sample.py:264 sith/settings_sample.py:285
|
||||
msgid "Credit card"
|
||||
msgstr "Carte banquaire"
|
||||
|
||||
@ -1570,47 +1582,43 @@ msgstr "vente"
|
||||
msgid "permanency"
|
||||
msgstr "permanence"
|
||||
|
||||
#: counter/templates/counter/counter_click.jinja:26
|
||||
msgid "Club: "
|
||||
msgstr "Club : "
|
||||
|
||||
#: counter/templates/counter/counter_click.jinja:29
|
||||
msgid "Customer"
|
||||
msgstr "Client"
|
||||
|
||||
#: counter/templates/counter/counter_click.jinja:34
|
||||
#: counter/templates/counter/counter_click.jinja:35
|
||||
msgid "Refilling"
|
||||
msgstr "Rechargement"
|
||||
|
||||
#: counter/templates/counter/counter_click.jinja:39
|
||||
#: counter/templates/counter/counter_click.jinja:52
|
||||
#: counter/templates/counter/counter_click.jinja:40
|
||||
#: counter/templates/counter/counter_click.jinja:53
|
||||
#: launderette/templates/launderette/launderette_admin.jinja:35
|
||||
#: launderette/templates/launderette/launderette_click.jinja:14
|
||||
msgid "Go"
|
||||
msgstr "Valider"
|
||||
|
||||
#: counter/templates/counter/counter_click.jinja:44
|
||||
#: counter/templates/counter/counter_click.jinja:45
|
||||
#: launderette/templates/launderette/launderette_admin.jinja:8
|
||||
msgid "Selling"
|
||||
msgstr "Vente"
|
||||
|
||||
#: counter/templates/counter/counter_click.jinja:54
|
||||
#: counter/templates/counter/counter_click.jinja:55
|
||||
#: eboutic/templates/eboutic/eboutic_main.jinja:27
|
||||
#: eboutic/templates/eboutic/eboutic_makecommand.jinja:11
|
||||
msgid "Basket: "
|
||||
msgstr "Panier : "
|
||||
|
||||
#: counter/templates/counter/counter_click.jinja:62
|
||||
#: counter/templates/counter/counter_click.jinja:63
|
||||
#: counter/templates/counter/counter_main.jinja:24
|
||||
#: eboutic/templates/eboutic/eboutic_main.jinja:34
|
||||
msgid "Total: "
|
||||
msgstr "Total : "
|
||||
|
||||
#: counter/templates/counter/counter_click.jinja:66
|
||||
#: counter/templates/counter/counter_click.jinja:67
|
||||
msgid "Finish"
|
||||
msgstr "Terminer"
|
||||
|
||||
#: counter/templates/counter/counter_click.jinja:73
|
||||
#: counter/templates/counter/counter_click.jinja:74
|
||||
msgid "Products: "
|
||||
msgstr "Produits : "
|
||||
|
||||
@ -1624,6 +1632,10 @@ msgid "New counter"
|
||||
msgstr "Nouveau comptoir"
|
||||
|
||||
#: counter/templates/counter/counter_list.jinja:23
|
||||
msgid "Props"
|
||||
msgstr "Propriétés"
|
||||
|
||||
#: counter/templates/counter/counter_list.jinja:29
|
||||
msgid "There is no counters in this website."
|
||||
msgstr "Il n'y a pas de comptoirs dans ce site web."
|
||||
|
||||
@ -1691,19 +1703,23 @@ msgstr "Nouveau type de produit"
|
||||
msgid "There is no product types in this website."
|
||||
msgstr "Il n'y a pas de types de produit dans ce site web."
|
||||
|
||||
#: counter/views.py:49
|
||||
#: counter/views.py:35
|
||||
msgid "Select user"
|
||||
msgstr "Choisir un utilisateur"
|
||||
|
||||
#: counter/views.py:51
|
||||
msgid "User not found"
|
||||
msgstr "Utilisateur non trouvé"
|
||||
|
||||
#: counter/views.py:212
|
||||
#: counter/views.py:214
|
||||
msgid "END"
|
||||
msgstr "FIN"
|
||||
|
||||
#: counter/views.py:214
|
||||
#: counter/views.py:216
|
||||
msgid "CAN"
|
||||
msgstr "ANN"
|
||||
|
||||
#: counter/views.py:244
|
||||
#: counter/views.py:246
|
||||
msgid "You have not enough money to buy all the basket"
|
||||
msgstr "Vous n'avez pas assez d'argent pour acheter le panier"
|
||||
|
||||
@ -1855,12 +1871,12 @@ msgid "Washing and drying"
|
||||
msgstr "Lavage et séchage"
|
||||
|
||||
#: launderette/templates/launderette/launderette_book.jinja:26
|
||||
#: sith/settings.py:408 sith/settings_sample.py:394
|
||||
#: sith/settings.py:412 sith/settings_sample.py:395
|
||||
msgid "Washing"
|
||||
msgstr "Lavage"
|
||||
|
||||
#: launderette/templates/launderette/launderette_book.jinja:30
|
||||
#: sith/settings.py:408 sith/settings_sample.py:394
|
||||
#: sith/settings.py:412 sith/settings_sample.py:395
|
||||
msgid "Drying"
|
||||
msgstr "Séchage"
|
||||
|
||||
@ -1915,108 +1931,108 @@ msgstr "L'utilisateur n'a pas réservé de créneau"
|
||||
msgid "Token not found"
|
||||
msgstr "Jeton non trouvé"
|
||||
|
||||
#: sith/settings.py:269 sith/settings.py:276 sith/settings.py:296
|
||||
#: sith/settings_sample.py:255 sith/settings_sample.py:262
|
||||
#: sith/settings_sample.py:282
|
||||
#: sith/settings.py:273 sith/settings.py:280 sith/settings.py:300
|
||||
#: sith/settings_sample.py:256 sith/settings_sample.py:263
|
||||
#: sith/settings_sample.py:283
|
||||
msgid "Check"
|
||||
msgstr "Chèque"
|
||||
|
||||
#: sith/settings.py:270 sith/settings.py:278 sith/settings.py:297
|
||||
#: sith/settings_sample.py:256 sith/settings_sample.py:264
|
||||
#: sith/settings_sample.py:283
|
||||
#: sith/settings.py:274 sith/settings.py:282 sith/settings.py:301
|
||||
#: sith/settings_sample.py:257 sith/settings_sample.py:265
|
||||
#: sith/settings_sample.py:284
|
||||
msgid "Cash"
|
||||
msgstr "Espèces"
|
||||
|
||||
#: sith/settings.py:271 sith/settings_sample.py:257
|
||||
#: sith/settings.py:275 sith/settings_sample.py:258
|
||||
msgid "Transfert"
|
||||
msgstr "Virement"
|
||||
|
||||
#: sith/settings.py:284 sith/settings_sample.py:270
|
||||
#: sith/settings.py:288 sith/settings_sample.py:271
|
||||
msgid "Belfort"
|
||||
msgstr "Belfort"
|
||||
|
||||
#: sith/settings.py:285 sith/settings_sample.py:271
|
||||
#: sith/settings.py:289 sith/settings_sample.py:272
|
||||
msgid "Sevenans"
|
||||
msgstr "Sevenans"
|
||||
|
||||
#: sith/settings.py:286 sith/settings_sample.py:272
|
||||
#: sith/settings.py:290 sith/settings_sample.py:273
|
||||
msgid "Montbéliard"
|
||||
msgstr "Montbéliard"
|
||||
|
||||
#: sith/settings.py:321 sith/settings_sample.py:307
|
||||
#: sith/settings.py:325 sith/settings_sample.py:308
|
||||
msgid "One semester"
|
||||
msgstr "Un semestre"
|
||||
|
||||
#: sith/settings.py:326 sith/settings_sample.py:312
|
||||
#: sith/settings.py:330 sith/settings_sample.py:313
|
||||
msgid "Two semesters"
|
||||
msgstr "Deux semestres"
|
||||
|
||||
#: sith/settings.py:331 sith/settings_sample.py:317
|
||||
#: sith/settings.py:335 sith/settings_sample.py:318
|
||||
msgid "Common core cursus"
|
||||
msgstr "Cursus tronc commun"
|
||||
|
||||
#: sith/settings.py:336 sith/settings.py:341 sith/settings_sample.py:322
|
||||
#: sith/settings_sample.py:327
|
||||
#: sith/settings.py:340 sith/settings.py:345 sith/settings_sample.py:323
|
||||
#: sith/settings_sample.py:328
|
||||
msgid "Branch cursus"
|
||||
msgstr "Cursus branche"
|
||||
|
||||
#: sith/settings.py:346 sith/settings_sample.py:332
|
||||
#: sith/settings.py:350 sith/settings_sample.py:333
|
||||
msgid "Honorary member"
|
||||
msgstr "Membre honoraire"
|
||||
|
||||
#: sith/settings.py:351 sith/settings_sample.py:337
|
||||
#: sith/settings.py:355 sith/settings_sample.py:338
|
||||
msgid "Assidu member"
|
||||
msgstr "Membre d'Assidu"
|
||||
|
||||
#: sith/settings.py:356 sith/settings_sample.py:342
|
||||
#: sith/settings.py:360 sith/settings_sample.py:343
|
||||
msgid "Amicale/DOCEO member"
|
||||
msgstr "Membre de l'Amicale/DOCEO"
|
||||
|
||||
#: sith/settings.py:361 sith/settings_sample.py:347
|
||||
#: sith/settings.py:365 sith/settings_sample.py:348
|
||||
msgid "UT network member"
|
||||
msgstr "Cotisant du réseau UT"
|
||||
|
||||
#: sith/settings.py:366 sith/settings_sample.py:352
|
||||
#: sith/settings.py:370 sith/settings_sample.py:353
|
||||
msgid "CROUS member"
|
||||
msgstr "Membres du CROUS"
|
||||
|
||||
#: sith/settings.py:371 sith/settings_sample.py:357
|
||||
#: sith/settings.py:375 sith/settings_sample.py:358
|
||||
msgid "Sbarro/ESTA member"
|
||||
msgstr "Membre de Sbarro ou de l'ESTA"
|
||||
|
||||
#: sith/settings.py:379 sith/settings_sample.py:365
|
||||
#: sith/settings.py:383 sith/settings_sample.py:366
|
||||
msgid "President"
|
||||
msgstr "Président"
|
||||
|
||||
#: sith/settings.py:380 sith/settings_sample.py:366
|
||||
#: sith/settings.py:384 sith/settings_sample.py:367
|
||||
msgid "Vice-President"
|
||||
msgstr "Vice-Président"
|
||||
|
||||
#: sith/settings.py:381 sith/settings_sample.py:367
|
||||
#: sith/settings.py:385 sith/settings_sample.py:368
|
||||
msgid "Treasurer"
|
||||
msgstr "Trésorier"
|
||||
|
||||
#: sith/settings.py:382 sith/settings_sample.py:368
|
||||
#: sith/settings.py:386 sith/settings_sample.py:369
|
||||
msgid "Communication supervisor"
|
||||
msgstr "Responsable com"
|
||||
|
||||
#: sith/settings.py:383 sith/settings_sample.py:369
|
||||
#: sith/settings.py:387 sith/settings_sample.py:370
|
||||
msgid "Secretary"
|
||||
msgstr "Secrétaire"
|
||||
|
||||
#: sith/settings.py:384 sith/settings_sample.py:370
|
||||
#: sith/settings.py:388 sith/settings_sample.py:371
|
||||
msgid "IT supervisor"
|
||||
msgstr "Responsable info"
|
||||
|
||||
#: sith/settings.py:385 sith/settings_sample.py:371
|
||||
#: sith/settings.py:389 sith/settings_sample.py:372
|
||||
msgid "Board member"
|
||||
msgstr "Membre du bureau"
|
||||
|
||||
#: sith/settings.py:386 sith/settings_sample.py:372
|
||||
#: sith/settings.py:390 sith/settings_sample.py:373
|
||||
msgid "Active member"
|
||||
msgstr "Membre actif"
|
||||
|
||||
#: sith/settings.py:387 sith/settings_sample.py:373
|
||||
#: sith/settings.py:391 sith/settings_sample.py:374
|
||||
msgid "Curious"
|
||||
msgstr "Curieux"
|
||||
|
||||
@ -2052,11 +2068,14 @@ msgstr "Vous ne pouvez pas cotiser plusieurs fois pour la même période"
|
||||
msgid "You are trying to create a subscription without member"
|
||||
msgstr "Vous essayez de créer une cotisation sans membre"
|
||||
|
||||
#: subscription/views.py:42
|
||||
#: subscription/views.py:52
|
||||
msgid "A user with that email address already exists"
|
||||
msgstr "Un utilisateur avec cette adresse email existe déjà"
|
||||
|
||||
#: subscription/views.py:57
|
||||
#: subscription/views.py:67
|
||||
msgid "You must either choose an existing user or create a new one properly"
|
||||
msgstr ""
|
||||
"Vous devez soit choisir un utilisateur existant, ou en créer un proprement."
|
||||
|
||||
#~ msgid "Club: "
|
||||
#~ msgstr "Club : "
|
||||
|
@ -541,7 +541,7 @@ def migrate_sellings():
|
||||
op = User.objects.filter(id=r['id_utilisateur']).first() or root
|
||||
customer = Customer.objects.filter(user__id=r['id_utilisateur_client']).first() or root.customer
|
||||
new = Selling(
|
||||
label=product.name,
|
||||
label=product.name or "Produit supprimé",
|
||||
counter=counter,
|
||||
club=club,
|
||||
product=product,
|
||||
|
@ -44,6 +44,7 @@ INSTALLED_APPS = (
|
||||
'django.contrib.sites',
|
||||
'django_jinja',
|
||||
'rest_framework',
|
||||
'ajax_select',
|
||||
'core',
|
||||
'club',
|
||||
'subscription',
|
||||
|
@ -17,6 +17,7 @@ from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
from django.conf.urls.static import static
|
||||
from django.conf import settings
|
||||
from ajax_select import urls as ajax_select_urls
|
||||
|
||||
handler403 = "core.views.forbidden"
|
||||
handler404 = "core.views.not_found"
|
||||
@ -31,5 +32,6 @@ urlpatterns = [
|
||||
url(r'^launderette/', include('launderette.urls', namespace="launderette", app_name="launderette")),
|
||||
url(r'^api/v1/', include('api.urls', namespace="api", app_name="api")),
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
url(r'^ajax_select/', include(ajax_select_urls)),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # TODO: remove me for production!!!
|
||||
|
||||
|
@ -27,16 +27,18 @@
|
||||
{{ super() }}
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
$( function() {
|
||||
select = $("#subscription_form select[name=member]");
|
||||
select = $("#id_member");
|
||||
member_block = $("#subscription_form #new_member");
|
||||
user_info = $("#user_info");
|
||||
function display_new_member() {
|
||||
if (select.val()) {
|
||||
member_block.hide();
|
||||
user_info.load("/user/"+select.val()+"/mini");
|
||||
user_info.show();
|
||||
} else {
|
||||
member_block.show();
|
||||
user_info.empty();
|
||||
user_info.hide();
|
||||
}
|
||||
}
|
||||
select.on("change", display_new_member);
|
||||
|
@ -8,6 +8,8 @@ from django import forms
|
||||
from django.forms import Select
|
||||
from django.conf import settings
|
||||
|
||||
from ajax_select.fields import AutoCompleteSelectField
|
||||
|
||||
from subscription.models import Subscriber, Subscription
|
||||
from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin
|
||||
from core.models import User
|
||||
@ -20,6 +22,7 @@ class SubscriptionForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Subscription
|
||||
fields = ['member', 'subscription_type', 'payment_method', 'location']
|
||||
member = AutoCompleteSelectField('users', required=False, help_text=None)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SubscriptionForm, self).__init__(*args, **kwargs)
|
||||
@ -31,8 +34,15 @@ class SubscriptionForm(forms.ModelForm):
|
||||
self.fields.move_to_end('payment_method')
|
||||
self.fields.move_to_end('location')
|
||||
|
||||
def clean_member(self):
|
||||
subscriber = self.cleaned_data.get("member")
|
||||
if subscriber:
|
||||
subscriber = Subscriber.objects.filter(id=subscriber.id).first()
|
||||
return subscriber
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(SubscriptionForm, self).clean()
|
||||
print(cleaned_data)
|
||||
if (cleaned_data.get("member") is None
|
||||
and "last_name" not in self.errors.as_data()
|
||||
and "first_name" not in self.errors.as_data()
|
||||
|
Loading…
Reference in New Issue
Block a user