Add ajax-select app and improve some templates

This commit is contained in:
Skia 2016-08-19 23:24:23 +02:00
parent bfb2dc5f82
commit 7e90e657a7
25 changed files with 468 additions and 411 deletions

39
core/lookups.py Normal file
View 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

View File

@ -351,6 +351,20 @@ class User(AbstractBaseUser):
def can_be_edited_by(self, user): def can_be_edited_by(self, user):
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_root 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): class AnonymousUser(AuthAnonymousUser):
def __init__(self, request): def __init__(self, request):

View File

@ -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;
}

View File

@ -160,22 +160,31 @@ tbody>tr:hover {
float: right; float: right;
padding: 10px; padding: 10px;
} }
#user_info_container {
}
#user_info { #user_info {
float: right;
padding: 5px;
width: 40%; width: 40%;
margin: 0px auto; margin: 0px auto;
background: lightgrey; background: lightgrey;
} }
/*-----------------------------USER PROFILE----------------------------*/ /*-----------------------------USER PROFILE----------------------------*/
#user_profile { #user_profile_container {
width: 80%; width: 80%;
margin: 0px auto; margin: 0px auto;
}
#user_profile {
width: 100%;
margin: 0px auto;
padding: 10px; padding: 10px;
overflow: auto; overflow: auto;
} }
#user_profile h4 { border-bottom: 1px solid grey; max-width: 60%; } #user_profile h4 { border-bottom: 1px solid grey; max-width: 60%; }
#user_profile #pictures { #user_profile #pictures {
width: 30%; max-width: 250px;
max-height: 300px;
float: right; float: right;
font-style: italic; font-style: italic;
} }
@ -238,7 +247,14 @@ footer{
text-align: center; text-align: center;
} }
/*--------------------------------MODALE-------------------------------*/ /*---------------------------------FORMS-------------------------------*/
form {
margin: 0px auto;
width: 60%;
}
label {
display: block;
}
.choose_file_widget { .choose_file_widget {
display: none; display: none;
} }
@ -247,24 +263,10 @@ footer{
position: absolute; position: absolute;
width: 97%; width: 97%;
} }
.form-wrapper { #user_edit * {
display: inline-block; text-align: center;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #DDD;
opacity: 0.8;
padding: 15px;
border-radius: 4px;
} }
.form-wrapper>form { #user_edit img {
display: inline-block; width: 100px;
position: absolute;
padding: 15px;
border-radius: 4px;
left: 30%;
top: 40%;
background-color: white;
} }

View File

@ -4,9 +4,9 @@
{% block head %} {% block head %}
<title>{% block title %}{% trans %}Welcome!{% endtrans %}{% endblock %}</title> <title>{% block title %}{% trans %}Welcome!{% endtrans %}{% endblock %}</title>
<link rel="stylesheet" href="{{ static('core/base.css') }}"> <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/multiple-select.css') }}">
<link rel="stylesheet" href="{{ static('core/js/ui/jquery-ui.min.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') }}"> <link rel="stylesheet" href="{{ static('core/style.css') }}">
{% endblock %} {% endblock %}
</head> </head>
@ -70,6 +70,7 @@
<script src="{{ static('core/js/jquery-3.1.0.min.js') }}"></script> <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/ui/jquery-ui.min.js') }}"></script>
<script src="{{ static('core/js/multiple-select.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 src="{{ static('core/js/script.js') }}"></script>
<script> <script>
$('.select_single').multipleSelect({ $('.select_single').multipleSelect({

View File

@ -14,3 +14,24 @@
<em>{{ user.get_display_name() }}</em> <em>{{ user.get_display_name() }}</em>
</a> </a>
{%- endmacro %} {%- 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">&laquo; {{ user.nick_name }} &raquo;</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 %}

View File

@ -6,31 +6,33 @@
{% block infos %} {% block infos %}
<div id="user_profile"> <div id="user_profile_container">
<div id="pictures"> <div id="user_profile">
{% if profile.profile_pict %} <div id="pictures">
<img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" /> {% 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">&laquo; {{ profile.nick_name }} &raquo;</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 %} {% endif %}
<p><em>{{ profile.quote }}</em></p>
</div> </div>
<h4>{{ profile.get_full_name() }}</h4>
{% if profile.nick_name %}
<p id="nickname">&laquo; {{ profile.nick_name }} &raquo;</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> </div>
{% if user.membership.filter(end_date=None).exists() or user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) %} {% if user.membership.filter(end_date=None).exists() or user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) %}

View File

@ -6,19 +6,19 @@
{% block infos %} {% block infos %}
<h2>{% trans %}Edit user profile{% endtrans %}</h2> <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 %} {% csrf_token %}
{% for field in form %} {% for field in form %}
<p>{{ field.errors }}<label for="{{ field.name }}">{{ field.label }} <p>{{ field.errors }}<label for="{{ field.name }}">{{ field.label }}
{%- if field.name == "profile_pict" and form.instance.profile_pict -%} {%- if field.name == "profile_pict" and form.instance.profile_pict -%}
<br>{% trans %}Current profile: {% endtrans %} <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 -%} {%- elif field.name == "avatar_pict" and form.instance.avatar_pict -%}
<br>{% trans %}Current avatar: {% endtrans %} <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 -%} {%- elif field.name == "scrub_pict" and form.instance.scrub_pict -%}
<br>{% trans %}Current scrub: {% endtrans %} <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> {%- endif %}</label> {{ field }}</p>
{% endfor %} {% endfor %}
<p><input type="submit" value="{% trans %}Update{% endtrans %}" /></p> <p><input type="submit" value="{% trans %}Update{% endtrans %}" /></p>

View File

@ -1,19 +1,3 @@
<div id="user_profile"> {% from "core/macros.jinja" import user_mini_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">&laquo; {{ profile.nick_name }} &raquo;</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>
{{ user_mini_profile(profile) }}

View File

@ -6,6 +6,7 @@ urlpatterns = [
url(r'^$', index, name='index'), url(r'^$', index, name='index'),
url(r'^search/$', search_view, name='search'), url(r'^search/$', search_view, name='search'),
url(r'^search_json/$', search_json, name='search_json'), url(r'^search_json/$', search_json, name='search_json'),
url(r'^search_user/$', search_user_json, name='search_user'),
# Login and co # Login and co
url(r'^login/$', login, name='login'), url(r'^login/$', login, name='login'),

View File

@ -52,6 +52,20 @@ class SelectFile(TextInput):
output += '<span name="' + name + '" class="choose_file_button">' + _("Choose file") + '</span>' output += '<span name="' + name + '" class="choose_file_button">' + _("Choose file") + '</span>'
return output 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 # Forms
class RegisteringForm(UserCreationForm): class RegisteringForm(UserCreationForm):

View File

@ -15,36 +15,57 @@ from club.models import Club
def index(request, context=None): def index(request, context=None):
return render(request, "core/index.jinja") return render(request, "core/index.jinja")
def search(query, as_json=False): def search_user(query, as_json=False):
result = {'users': None, 'clubs': None} users = []
if query: if query:
exact_nick = User.objects.filter(nick_name__iexact=query).all() exact_nick = User.objects.filter(nick_name__iexact=query).all()
nicks = User.objects.filter(nick_name__icontains=query).exclude(id__in=exact_nick).all() nicks = User.objects.filter(nick_name__icontains=query).exclude(id__in=exact_nick).all()
users = User.objects.filter(Q(first_name__icontains=query) | users = User.objects.filter(Q(first_name__icontains=query) |
Q(last_name__icontains=query)).exclude(id__in=exact_nick).exclude(id__in=nicks).all() 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] nicks = nicks[:5]
users = users[:5] users = users[:5]
clubs = clubs[:5]
if as_json: # Re-loads json to avoid double encoding by JsonResponse, but still benefit from serializers 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'))) 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'))) 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'))) 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: else:
exact_nick = list(exact_nick) exact_nick = list(exact_nick)
nicks = list(nicks) nicks = list(nicks)
users = list(users) 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) clubs = list(clubs)
result['users'] = exact_nick + nicks + users return clubs
result['clubs'] = clubs
return result
@login_required @login_required
def search_view(request): 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 @login_required
def search_json(request): 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)

View 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'),
),
]

View File

@ -1,4 +1,6 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% from "core/macros.jinja" import user_mini_profile %}
{% macro add_product(id, content) %} {% 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"> <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 %} {% endblock %}
{% block content %} {% block content %}
<h3>{% trans %}Counter{% endtrans %}</h3>
<h4>{{ counter }}</h4> <h4>{{ counter }}</h4>
<p><strong>{% trans %}Club: {% endtrans %}</strong> {{ counter.club }}</p>
<div> <div id="user_info">
<h5>{% trans %}Customer{% endtrans %}</h5> <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> </div>
{% if counter.type == 'BAR' %} {% if counter.type == 'BAR' %}
<div> <div>
@ -48,7 +49,7 @@
<form method="post" action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}"> <form method="post" action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="action" value="code"> <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 %}" /> <input type="submit" value="{% trans %}Go{% endtrans %}" />
</form> </form>
<p>{% trans %}Basket: {% endtrans %}</p> <p>{% trans %}Basket: {% endtrans %}</p>
@ -79,5 +80,45 @@
{% endblock %} {% 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 %}

View 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 %}

View File

@ -10,13 +10,19 @@
<h3>{% trans %}Counter admin list{% endtrans %}</h3> <h3>{% trans %}Counter admin list{% endtrans %}</h3>
<ul> <ul>
{% for c in counter_list %} {% for c in counter_list %}
<li>
{% if c.type == "EBOUTIC" %} {% if c.type == "EBOUTIC" %}
<li><a href="{{ url('eboutic:main') }}">{{ c }}</a> - <a href="{{ url('eboutic:main') }}">{{ c }}</a> -
<a href="{{ url('counter:admin', counter_id=c.id) }}">{% trans %}Edit{% endtrans %}</a></li>
{% else %} {% else %}
<li><a href="{{ url('counter:details', counter_id=c.id) }}">{{ c }}</a> - <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>
{% endif %} {% 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 %} {% endfor %}
</ul> </ul>
{% else %} {% else %}

View File

@ -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]+)/login$', CounterLogin.as_view(), name='login'),
url(r'^(?P<counter_id>[0-9]+)/logout$', CounterLogout.as_view(), name='logout'), 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]+)$', 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$', CounterListView.as_view(), name='admin_list'),
url(r'^admin/new$', CounterCreateView.as_view(), name='new'), url(r'^admin/new$', CounterCreateView.as_view(), name='new'),
url(r'^admin/delete/(?P<counter_id>[0-9]+)$', CounterDeleteView.as_view(), name='delete'), url(r'^admin/delete/(?P<counter_id>[0-9]+)$', CounterDeleteView.as_view(), name='delete'),

View File

@ -14,8 +14,11 @@ from django.db import DataError, transaction
import re import re
from datetime import date, timedelta 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 import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
from core.views.forms import SelectUser
from subscription.models import Subscriber from subscription.models import Subscriber
from subscription.views import get_subscriber from subscription.views import get_subscriber
from counter.models import Counter, Customer, Product, Selling, Refilling, ProductType 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) some nickname, first name, or last name (TODO)
""" """
code = forms.CharField(label="Code", max_length=10, required=False) code = forms.CharField(label="Code", max_length=10, required=False)
id = forms.IntegerField(label="ID", required=False) id = AutoCompleteSelectField('users', required=False, label=_("Select user"), help_text=None)
# TODO: add a nice JS widget to search for users
def as_p(self): def as_p(self):
self.fields['code'].widget.attrs['autofocus'] = True self.fields['code'].widget.attrs['autofocus'] = True
@ -327,15 +329,31 @@ class CounterListView(CanViewMixin, ListView):
model = Counter model = Counter
template_name = 'counter/counter_list.jinja' 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) Edit a counter's main informations (for the counter's admin)
""" """
model = Counter model = Counter
form_class = modelform_factory(Counter, fields=['name', 'club', 'type', 'sellers', 'products'], form_class = modelform_factory(Counter, fields=['name', 'club', 'type'])
widgets={
'products':CheckboxSelectMultiple,
'sellers':CheckboxSelectMultiple})
pk_url_kwarg = "counter_id" pk_url_kwarg = "counter_id"
template_name = 'core/edit.jinja' template_name = 'core/edit.jinja'
@ -399,13 +417,20 @@ class ProductCreateView(CanCreateMixin, CreateView):
'selling_price', 'special_selling_price', 'icon', 'club'] 'selling_price', 'special_selling_price', 'icon', 'club']
template_name = 'core/create.jinja' 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): class ProductEditView(CanEditPropMixin, UpdateView):
""" """
An edit view for the admins An edit view for the admins
""" """
model = Product model = Product
fields = ['name', 'description', 'product_type', 'code', 'purchase_price', form_class = ProductEditForm
'selling_price', 'special_selling_price', 'icon', 'club']
pk_url_kwarg = "product_id" pk_url_kwarg = "product_id"
template_name = 'core/edit.jinja' template_name = 'core/edit.jinja'
# TODO: add management of the 'counters' ForeignKey # TODO: add management of the 'counters' ForeignKey

Binary file not shown.

View File

@ -6,7 +6,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Report-Msgid-Bugs-To: \n" "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" "PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Skia <skia@libskia.so>\n" "Last-Translator: Skia <skia@libskia.so>\n"
"Language-Team: AE info <ae.info@utbm.fr>\n" "Language-Team: AE info <ae.info@utbm.fr>\n"
@ -66,7 +66,7 @@ msgstr "montant effectif"
msgid "number" msgid "number"
msgstr "numéro" 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 #: counter/models.py:209 counter/models.py:255 eboutic/models.py:14
#: eboutic/models.py:47 #: eboutic/models.py:47
msgid "date" msgid "date"
@ -122,7 +122,7 @@ msgstr "Compte"
msgid "Company" msgid "Company"
msgstr "Entreprise" 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" msgid "Other"
msgstr "Autre" msgstr "Autre"
@ -209,8 +209,7 @@ msgstr "Nouveau compte club"
#: club/templates/club/club_detail.jinja:7 core/templates/core/file.jinja:38 #: 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/page.jinja:31 core/templates/core/user_base.jinja:10
#: core/templates/core/user_tools.jinja:33 #: core/templates/core/user_tools.jinja:33
#: counter/templates/counter/counter_list.jinja:15 #: counter/templates/counter/counter_list.jinja:20
#: counter/templates/counter/counter_list.jinja:18
#: launderette/templates/launderette/launderette_list.jinja:14 #: launderette/templates/launderette/launderette_list.jinja:14
msgid "Edit" msgid "Edit"
msgstr "Éditer" msgstr "Éditer"
@ -303,6 +302,7 @@ msgstr "Voir"
#: accounting/templates/accounting/journal_details.jinja:10 #: accounting/templates/accounting/journal_details.jinja:10
#: core/templates/core/user_account.jinja:10 #: core/templates/core/user_account.jinja:10
#: counter/templates/counter/counter_click.jinja:31
msgid "Amount: " msgid "Amount: "
msgstr "Montant: " 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/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/file_edit.jinja:8 core/templates/core/page_prop.jinja:8
#: core/templates/core/pagerev_edit.jinja:24 #: 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 #: subscription/templates/subscription/subscription.jinja:22
msgid "Save" msgid "Save"
msgstr "Sauver" msgstr "Sauver"
@ -769,109 +771,116 @@ msgstr "adresse des parents"
msgid "A user with that username already exists" msgid "A user with that username already exists"
msgstr "Un utilisateur de ce nom d'utilisateur existe déjà" 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" msgid "Visitor"
msgstr "Visiteur" msgstr "Visiteur"
#: core/models.py:386 #: core/models.py:400
msgid "define if we show a users stats" msgid "define if we show a users stats"
msgstr "Definit si l'on montre les statistiques de l'utilisateur" 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" msgid "Show your account statistics to others"
msgstr "Montrez vos statistiques de compte aux autres" msgstr "Montrez vos statistiques de compte aux autres"
#: core/models.py:395 #: core/models.py:409
msgid "file name" msgid "file name"
msgstr "nom du fichier" msgstr "nom du fichier"
#: core/models.py:396 core/models.py:529 #: core/models.py:410 core/models.py:543
msgid "parent" msgid "parent"
msgstr "parent" msgstr "parent"
#: core/models.py:397 core/models.py:407 #: core/models.py:411 core/models.py:421
msgid "file" msgid "file"
msgstr "fichier" msgstr "fichier"
#: core/models.py:398 #: core/models.py:412
msgid "owner" msgid "owner"
msgstr "propriétaire" msgstr "propriétaire"
#: core/models.py:399 core/models.py:535 #: core/models.py:413 core/models.py:549
msgid "edit group" msgid "edit group"
msgstr "groupe d'édition" msgstr "groupe d'édition"
#: core/models.py:400 core/models.py:536 #: core/models.py:414 core/models.py:550
msgid "view group" msgid "view group"
msgstr "groupe de vue" msgstr "groupe de vue"
#: core/models.py:401 #: core/models.py:415
msgid "is folder" msgid "is folder"
msgstr "est un dossier" msgstr "est un dossier"
#: core/models.py:402 #: core/models.py:416
msgid "mime type" msgid "mime type"
msgstr "type mime" msgstr "type mime"
#: core/models.py:403 #: core/models.py:417
msgid "size" msgid "size"
msgstr "taille" msgstr "taille"
#: core/models.py:433 #: core/models.py:447
msgid "Character '/' not authorized in name" msgid "Character '/' not authorized in name"
msgstr "Le caractère '/' n'est pas autorisé dans les noms de fichier" 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" msgid "Loop in folder tree"
msgstr "Boucle dans l'arborescence des dossiers" 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" msgid "You can not make a file be a children of a non folder file"
msgstr "" msgstr ""
"Vous ne pouvez pas mettre un fichier enfant de quelque chose qui n'est pas " "Vous ne pouvez pas mettre un fichier enfant de quelque chose qui n'est pas "
"un dossier" "un dossier"
#: core/models.py:449 #: core/models.py:463
msgid "Duplicate file" msgid "Duplicate file"
msgstr "Un fichier de ce nom existe déjà" msgstr "Un fichier de ce nom existe déjà"
#: core/models.py:459 #: core/models.py:473
msgid "You must provide a file" msgid "You must provide a file"
msgstr "Vous devez fournir un fichier" msgstr "Vous devez fournir un fichier"
#: core/models.py:484 #: core/models.py:498
msgid "Folder: " msgid "Folder: "
msgstr "Dossier : " msgstr "Dossier : "
#: core/models.py:486 #: core/models.py:500
msgid "File: " msgid "File: "
msgstr "Fichier : " msgstr "Fichier : "
#: core/models.py:528 core/models.py:532 #: core/models.py:542 core/models.py:546
msgid "page name" msgid "page name"
msgstr "nom de la page" msgstr "nom de la page"
#: core/models.py:533 #: core/models.py:547
msgid "owner group" msgid "owner group"
msgstr "groupe propriétaire" msgstr "groupe propriétaire"
#: core/models.py:564 #: core/models.py:578
msgid "Duplicate page" msgid "Duplicate page"
msgstr "Une page de ce nom existe déjà" msgstr "Une page de ce nom existe déjà"
#: core/models.py:570 #: core/models.py:584
msgid "Loop in page tree" msgid "Loop in page tree"
msgstr "Boucle dans l'arborescence des pages" msgstr "Boucle dans l'arborescence des pages"
#: core/models.py:677 #: core/models.py:691
msgid "revision" msgid "revision"
msgstr "révision" msgstr "révision"
#: core/models.py:678 #: core/models.py:692
msgid "page title" msgid "page title"
msgstr "titre de la page" msgstr "titre de la page"
#: core/models.py:679 #: core/models.py:693
msgid "page content" msgid "page content"
msgstr "contenu de la page" msgstr "contenu de la page"
@ -883,7 +892,7 @@ msgstr "403. Non autorisé"
msgid "404, Not Found" msgid "404, Not Found"
msgstr "404. Non trouvé" msgstr "404. Non trouvé"
#: core/templates/core/base.jinja:5 #: core/templates/core/base.jinja:5 core/templates/core/index.jinja:4
msgid "Welcome!" msgid "Welcome!"
msgstr "Bienvenue!" msgstr "Bienvenue!"
@ -908,27 +917,31 @@ msgstr "Outils"
msgid "Logout" msgid "Logout"
msgstr "Déconnexion" 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" msgid "Users"
msgstr "Utilisateurs" msgstr "Utilisateurs"
#: core/templates/core/base.jinja:37 #: core/templates/core/base.jinja:41
msgid "Wiki" msgid "Wiki"
msgstr "Wiki" msgstr "Wiki"
#: core/templates/core/base.jinja:38 #: core/templates/core/base.jinja:42
msgid "Pages" msgid "Pages"
msgstr "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" msgid "Clubs"
msgstr "Clubs" msgstr "Clubs"
#: core/templates/core/base.jinja:40 #: core/templates/core/base.jinja:44
msgid "Services" msgid "Services"
msgstr "Services" msgstr "Services"
#: core/templates/core/base.jinja:56 #: core/templates/core/base.jinja:60
msgid "Site made by good people" msgid "Site made by good people"
msgstr "Site réalisé par des gens bons" msgstr "Site réalisé par des gens bons"
@ -957,12 +970,14 @@ msgstr "Confirmation"
#: core/templates/core/delete_confirm.jinja:14 #: core/templates/core/delete_confirm.jinja:14
#: core/templates/core/file_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" msgid "Cancel"
msgstr "Annuler" msgstr "Annuler"
#: core/templates/core/edit.jinja:4 core/templates/core/edit.jinja.py:8 #: core/templates/core/edit.jinja:4 core/templates/core/edit.jinja.py:8
#: core/templates/core/file_edit.jinja:4 #: core/templates/core/file_edit.jinja:4
#: counter/templates/counter/counter_edit.jinja:4
#: counter/templates/counter/counter_edit.jinja:8
#, python-format #, python-format
msgid "Edit %(obj)s" msgid "Edit %(obj)s"
msgstr "Éditer %(obj)s" msgstr "Éditer %(obj)s"
@ -1038,7 +1053,7 @@ msgstr "Liste des groupes"
msgid "New group" msgid "New group"
msgstr "Nouveau groupe" 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." msgid "Hello, world. You're at the core index using Jinja2."
msgstr "Hello, world! Vous êtes sur la page d'accueil utilisant Jinja2." msgstr "Hello, world! Vous êtes sur la page d'accueil utilisant Jinja2."
@ -1069,12 +1084,15 @@ msgstr "login"
msgid "Lost password?" msgid "Lost password?"
msgstr "Mot de passe perdu ?" msgstr "Mot de passe perdu ?"
#: core/templates/core/macros.jinja:9 core/templates/core/macros.jinja:11 #: core/templates/core/macros.jinja:30
#: core/templates/core/user_detail.jinja:12 #: core/templates/core/user_detail.jinja:22
#: core/templates/core/user_edit.jinja:15 msgid "Born: "
#: core/templates/core/user_mini.jinja:4 msgstr "Né le : "
msgid "Profile"
msgstr "Profil" #: 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.jinja:7 core/templates/core/page_list.jinja:4
#: core/templates/core/page_list.jinja:9 #: 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:17
#: core/templates/core/user_account.jinja:45 #: core/templates/core/user_account.jinja:45
#: counter/templates/counter/counter_click.jinja:24
msgid "Counter" msgid "Counter"
msgstr "Comptoir" msgstr "Comptoir"
@ -1299,30 +1316,20 @@ msgstr "Groupes"
msgid "%(user_name)s's profile" msgid "%(user_name)s's profile"
msgstr "Profil de %(user_name)s" msgstr "Profil de %(user_name)s"
#: core/templates/core/user_detail.jinja:21 #: core/templates/core/user_detail.jinja:28
#: core/templates/core/user_mini.jinja:12
msgid "Born: "
msgstr "Né le : "
#: core/templates/core/user_detail.jinja:27
msgid "Option: " msgid "Option: "
msgstr "Filière : " msgstr "Filière : "
#: core/templates/core/user_detail.jinja:32 #: core/templates/core/user_detail.jinja:42
#: core/templates/core/user_mini.jinja:16
msgid "Promo: "
msgstr "Promo : "
#: core/templates/core/user_detail.jinja:40
#, python-format #, python-format
msgid "User is subscriber until %(subscription_end)s" msgid "User is subscriber until %(subscription_end)s"
msgstr "L'utilisateur est cotisant jusqu'au %(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. " msgid "User is not subscribed. "
msgstr "L'utilisateur n'est pas cotisant." 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:4
#: subscription/templates/subscription/subscription.jinja:8 #: subscription/templates/subscription/subscription.jinja:8
msgid "New subscription" msgid "New subscription"
@ -1382,6 +1389,7 @@ msgid "User list"
msgstr "Liste d'utilisateurs" msgstr "Liste d'utilisateurs"
#: core/templates/core/user_stats.jinja:4 #: core/templates/core/user_stats.jinja:4
#, python-format
msgid "%(user_name)s's stats" msgid "%(user_name)s's stats"
msgstr "Stats de %(user_name)s" msgstr "Stats de %(user_name)s"
@ -1439,7 +1447,7 @@ msgstr "Ajouter un nouveau dossier"
msgid "Error creating folder %(folder_name)s: %(msg)s" msgid "Error creating folder %(folder_name)s: %(msg)s"
msgstr "Erreur de création du dossier %(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 #, python-format
msgid "Error uploading file %(file_name)s: %(msg)s" msgid "Error uploading file %(file_name)s: %(msg)s"
msgstr "Erreur d'envoie du fichier %(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" msgid "Choose file"
msgstr "Choisir un fichier" 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 "" msgid ""
"Profile: you need to be visible on the picture, in order to be recognized (e." "Profile: you need to be visible on the picture, in order to be recognized (e."
"g. by the barmen)" "g. by the barmen)"
@ -1456,15 +1468,15 @@ msgstr ""
"Photo de profil: vous devez être visible sur la photo afin d'être reconnu " "Photo de profil: vous devez être visible sur la photo afin d'être reconnu "
"(par exemple par les barmen)" "(par exemple par les barmen)"
#: core/views/forms.py:114 #: core/views/forms.py:128
msgid "Avatar: used on the forum" msgid "Avatar: used on the forum"
msgstr "Avatar : utilisé sur le 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!" msgid "Scrub: let other know how your scrub looks like!"
msgstr "Blouse : montrez aux autres à quoi ressemble votre blouse !" 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" 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" msgstr "Mauvais format d'image, seuls les jpeg, png, et gif sont acceptés"
@ -1480,7 +1492,7 @@ msgstr "client"
msgid "customers" msgid "customers"
msgstr "clients" 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" msgid "Not enough money"
msgstr "Solde insuffisant" msgstr "Solde insuffisant"
@ -1520,7 +1532,7 @@ msgstr "Bureau"
#: eboutic/templates/eboutic/eboutic_main.jinja:24 #: eboutic/templates/eboutic/eboutic_main.jinja:24
#: eboutic/templates/eboutic/eboutic_makecommand.jinja:8 #: eboutic/templates/eboutic/eboutic_makecommand.jinja:8
#: eboutic/templates/eboutic/eboutic_payment_result.jinja:4 #: 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" msgid "Eboutic"
msgstr "Eboutic" msgstr "Eboutic"
@ -1556,9 +1568,9 @@ msgstr "quantité"
msgid "Sith account" msgid "Sith account"
msgstr "Compte utilisateur" msgstr "Compte utilisateur"
#: counter/models.py:257 sith/settings.py:272 sith/settings.py:277 #: counter/models.py:257 sith/settings.py:276 sith/settings.py:281
#: sith/settings.py:298 sith/settings_sample.py:258 #: sith/settings.py:302 sith/settings_sample.py:259
#: sith/settings_sample.py:263 sith/settings_sample.py:284 #: sith/settings_sample.py:264 sith/settings_sample.py:285
msgid "Credit card" msgid "Credit card"
msgstr "Carte banquaire" msgstr "Carte banquaire"
@ -1570,47 +1582,43 @@ msgstr "vente"
msgid "permanency" msgid "permanency"
msgstr "permanence" msgstr "permanence"
#: counter/templates/counter/counter_click.jinja:26
msgid "Club: "
msgstr "Club : "
#: counter/templates/counter/counter_click.jinja:29 #: counter/templates/counter/counter_click.jinja:29
msgid "Customer" msgid "Customer"
msgstr "Client" msgstr "Client"
#: counter/templates/counter/counter_click.jinja:34 #: counter/templates/counter/counter_click.jinja:35
msgid "Refilling" msgid "Refilling"
msgstr "Rechargement" msgstr "Rechargement"
#: counter/templates/counter/counter_click.jinja:39 #: counter/templates/counter/counter_click.jinja:40
#: counter/templates/counter/counter_click.jinja:52 #: counter/templates/counter/counter_click.jinja:53
#: launderette/templates/launderette/launderette_admin.jinja:35 #: launderette/templates/launderette/launderette_admin.jinja:35
#: launderette/templates/launderette/launderette_click.jinja:14 #: launderette/templates/launderette/launderette_click.jinja:14
msgid "Go" msgid "Go"
msgstr "Valider" msgstr "Valider"
#: counter/templates/counter/counter_click.jinja:44 #: counter/templates/counter/counter_click.jinja:45
#: launderette/templates/launderette/launderette_admin.jinja:8 #: launderette/templates/launderette/launderette_admin.jinja:8
msgid "Selling" msgid "Selling"
msgstr "Vente" 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_main.jinja:27
#: eboutic/templates/eboutic/eboutic_makecommand.jinja:11 #: eboutic/templates/eboutic/eboutic_makecommand.jinja:11
msgid "Basket: " msgid "Basket: "
msgstr "Panier : " msgstr "Panier : "
#: counter/templates/counter/counter_click.jinja:62 #: counter/templates/counter/counter_click.jinja:63
#: counter/templates/counter/counter_main.jinja:24 #: counter/templates/counter/counter_main.jinja:24
#: eboutic/templates/eboutic/eboutic_main.jinja:34 #: eboutic/templates/eboutic/eboutic_main.jinja:34
msgid "Total: " msgid "Total: "
msgstr "Total : " msgstr "Total : "
#: counter/templates/counter/counter_click.jinja:66 #: counter/templates/counter/counter_click.jinja:67
msgid "Finish" msgid "Finish"
msgstr "Terminer" msgstr "Terminer"
#: counter/templates/counter/counter_click.jinja:73 #: counter/templates/counter/counter_click.jinja:74
msgid "Products: " msgid "Products: "
msgstr "Produits : " msgstr "Produits : "
@ -1624,6 +1632,10 @@ msgid "New counter"
msgstr "Nouveau comptoir" msgstr "Nouveau comptoir"
#: counter/templates/counter/counter_list.jinja:23 #: 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." msgid "There is no counters in this website."
msgstr "Il n'y a pas de comptoirs dans ce site web." 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." msgid "There is no product types in this website."
msgstr "Il n'y a pas de types de produit dans ce site web." 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" msgid "User not found"
msgstr "Utilisateur non trouvé" msgstr "Utilisateur non trouvé"
#: counter/views.py:212 #: counter/views.py:214
msgid "END" msgid "END"
msgstr "FIN" msgstr "FIN"
#: counter/views.py:214 #: counter/views.py:216
msgid "CAN" msgid "CAN"
msgstr "ANN" msgstr "ANN"
#: counter/views.py:244 #: counter/views.py:246
msgid "You have not enough money to buy all the basket" msgid "You have not enough money to buy all the basket"
msgstr "Vous n'avez pas assez d'argent pour acheter le panier" 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" msgstr "Lavage et séchage"
#: launderette/templates/launderette/launderette_book.jinja:26 #: 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" msgid "Washing"
msgstr "Lavage" msgstr "Lavage"
#: launderette/templates/launderette/launderette_book.jinja:30 #: 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" msgid "Drying"
msgstr "Séchage" msgstr "Séchage"
@ -1915,108 +1931,108 @@ msgstr "L'utilisateur n'a pas réservé de créneau"
msgid "Token not found" msgid "Token not found"
msgstr "Jeton non trouvé" msgstr "Jeton non trouvé"
#: sith/settings.py:269 sith/settings.py:276 sith/settings.py:296 #: sith/settings.py:273 sith/settings.py:280 sith/settings.py:300
#: sith/settings_sample.py:255 sith/settings_sample.py:262 #: sith/settings_sample.py:256 sith/settings_sample.py:263
#: sith/settings_sample.py:282 #: sith/settings_sample.py:283
msgid "Check" msgid "Check"
msgstr "Chèque" msgstr "Chèque"
#: sith/settings.py:270 sith/settings.py:278 sith/settings.py:297 #: sith/settings.py:274 sith/settings.py:282 sith/settings.py:301
#: sith/settings_sample.py:256 sith/settings_sample.py:264 #: sith/settings_sample.py:257 sith/settings_sample.py:265
#: sith/settings_sample.py:283 #: sith/settings_sample.py:284
msgid "Cash" msgid "Cash"
msgstr "Espèces" msgstr "Espèces"
#: sith/settings.py:271 sith/settings_sample.py:257 #: sith/settings.py:275 sith/settings_sample.py:258
msgid "Transfert" msgid "Transfert"
msgstr "Virement" msgstr "Virement"
#: sith/settings.py:284 sith/settings_sample.py:270 #: sith/settings.py:288 sith/settings_sample.py:271
msgid "Belfort" msgid "Belfort"
msgstr "Belfort" msgstr "Belfort"
#: sith/settings.py:285 sith/settings_sample.py:271 #: sith/settings.py:289 sith/settings_sample.py:272
msgid "Sevenans" msgid "Sevenans"
msgstr "Sevenans" msgstr "Sevenans"
#: sith/settings.py:286 sith/settings_sample.py:272 #: sith/settings.py:290 sith/settings_sample.py:273
msgid "Montbéliard" msgid "Montbéliard"
msgstr "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" msgid "One semester"
msgstr "Un semestre" msgstr "Un semestre"
#: sith/settings.py:326 sith/settings_sample.py:312 #: sith/settings.py:330 sith/settings_sample.py:313
msgid "Two semesters" msgid "Two semesters"
msgstr "Deux semestres" 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" msgid "Common core cursus"
msgstr "Cursus tronc commun" msgstr "Cursus tronc commun"
#: sith/settings.py:336 sith/settings.py:341 sith/settings_sample.py:322 #: sith/settings.py:340 sith/settings.py:345 sith/settings_sample.py:323
#: sith/settings_sample.py:327 #: sith/settings_sample.py:328
msgid "Branch cursus" msgid "Branch cursus"
msgstr "Cursus branche" msgstr "Cursus branche"
#: sith/settings.py:346 sith/settings_sample.py:332 #: sith/settings.py:350 sith/settings_sample.py:333
msgid "Honorary member" msgid "Honorary member"
msgstr "Membre honoraire" msgstr "Membre honoraire"
#: sith/settings.py:351 sith/settings_sample.py:337 #: sith/settings.py:355 sith/settings_sample.py:338
msgid "Assidu member" msgid "Assidu member"
msgstr "Membre d'Assidu" 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" msgid "Amicale/DOCEO member"
msgstr "Membre de l'Amicale/DOCEO" 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" msgid "UT network member"
msgstr "Cotisant du réseau UT" 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" msgid "CROUS member"
msgstr "Membres du CROUS" 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" msgid "Sbarro/ESTA member"
msgstr "Membre de Sbarro ou de l'ESTA" 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" msgid "President"
msgstr "Président" msgstr "Président"
#: sith/settings.py:380 sith/settings_sample.py:366 #: sith/settings.py:384 sith/settings_sample.py:367
msgid "Vice-President" msgid "Vice-President"
msgstr "Vice-Président" msgstr "Vice-Président"
#: sith/settings.py:381 sith/settings_sample.py:367 #: sith/settings.py:385 sith/settings_sample.py:368
msgid "Treasurer" msgid "Treasurer"
msgstr "Trésorier" msgstr "Trésorier"
#: sith/settings.py:382 sith/settings_sample.py:368 #: sith/settings.py:386 sith/settings_sample.py:369
msgid "Communication supervisor" msgid "Communication supervisor"
msgstr "Responsable com" msgstr "Responsable com"
#: sith/settings.py:383 sith/settings_sample.py:369 #: sith/settings.py:387 sith/settings_sample.py:370
msgid "Secretary" msgid "Secretary"
msgstr "Secrétaire" msgstr "Secrétaire"
#: sith/settings.py:384 sith/settings_sample.py:370 #: sith/settings.py:388 sith/settings_sample.py:371
msgid "IT supervisor" msgid "IT supervisor"
msgstr "Responsable info" msgstr "Responsable info"
#: sith/settings.py:385 sith/settings_sample.py:371 #: sith/settings.py:389 sith/settings_sample.py:372
msgid "Board member" msgid "Board member"
msgstr "Membre du bureau" 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" msgid "Active member"
msgstr "Membre actif" msgstr "Membre actif"
#: sith/settings.py:387 sith/settings_sample.py:373 #: sith/settings.py:391 sith/settings_sample.py:374
msgid "Curious" msgid "Curious"
msgstr "Curieux" 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" msgid "You are trying to create a subscription without member"
msgstr "Vous essayez de créer une cotisation sans membre" 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" msgid "A user with that email address already exists"
msgstr "Un utilisateur avec cette adresse email existe déjà" 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" msgid "You must either choose an existing user or create a new one properly"
msgstr "" msgstr ""
"Vous devez soit choisir un utilisateur existant, ou en créer un proprement." "Vous devez soit choisir un utilisateur existant, ou en créer un proprement."
#~ msgid "Club: "
#~ msgstr "Club : "

View File

@ -541,7 +541,7 @@ def migrate_sellings():
op = User.objects.filter(id=r['id_utilisateur']).first() or root 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 customer = Customer.objects.filter(user__id=r['id_utilisateur_client']).first() or root.customer
new = Selling( new = Selling(
label=product.name, label=product.name or "Produit supprimé",
counter=counter, counter=counter,
club=club, club=club,
product=product, product=product,

View File

@ -44,6 +44,7 @@ INSTALLED_APPS = (
'django.contrib.sites', 'django.contrib.sites',
'django_jinja', 'django_jinja',
'rest_framework', 'rest_framework',
'ajax_select',
'core', 'core',
'club', 'club',
'subscription', 'subscription',

View File

@ -17,6 +17,7 @@ from django.conf.urls import include, url
from django.contrib import admin from django.contrib import admin
from django.conf.urls.static import static from django.conf.urls.static import static
from django.conf import settings from django.conf import settings
from ajax_select import urls as ajax_select_urls
handler403 = "core.views.forbidden" handler403 = "core.views.forbidden"
handler404 = "core.views.not_found" handler404 = "core.views.not_found"
@ -31,5 +32,6 @@ urlpatterns = [
url(r'^launderette/', include('launderette.urls', namespace="launderette", app_name="launderette")), 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'^api/v1/', include('api.urls', namespace="api", app_name="api")),
url(r'^admin/', include(admin.site.urls)), 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!!! ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # TODO: remove me for production!!!

View File

@ -27,16 +27,18 @@
{{ super() }} {{ super() }}
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
$( function() { $( function() {
select = $("#subscription_form select[name=member]"); select = $("#id_member");
member_block = $("#subscription_form #new_member"); member_block = $("#subscription_form #new_member");
user_info = $("#user_info"); user_info = $("#user_info");
function display_new_member() { function display_new_member() {
if (select.val()) { if (select.val()) {
member_block.hide(); member_block.hide();
user_info.load("/user/"+select.val()+"/mini"); user_info.load("/user/"+select.val()+"/mini");
user_info.show();
} else { } else {
member_block.show(); member_block.show();
user_info.empty(); user_info.empty();
user_info.hide();
} }
} }
select.on("change", display_new_member); select.on("change", display_new_member);

View File

@ -8,6 +8,8 @@ from django import forms
from django.forms import Select from django.forms import Select
from django.conf import settings from django.conf import settings
from ajax_select.fields import AutoCompleteSelectField
from subscription.models import Subscriber, Subscription from subscription.models import Subscriber, Subscription
from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin
from core.models import User from core.models import User
@ -20,6 +22,7 @@ class SubscriptionForm(forms.ModelForm):
class Meta: class Meta:
model = Subscription model = Subscription
fields = ['member', 'subscription_type', 'payment_method', 'location'] fields = ['member', 'subscription_type', 'payment_method', 'location']
member = AutoCompleteSelectField('users', required=False, help_text=None)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(SubscriptionForm, self).__init__(*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('payment_method')
self.fields.move_to_end('location') 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): def clean(self):
cleaned_data = super(SubscriptionForm, self).clean() cleaned_data = super(SubscriptionForm, self).clean()
print(cleaned_data)
if (cleaned_data.get("member") is None if (cleaned_data.get("member") is None
and "last_name" not in self.errors.as_data() and "last_name" not in self.errors.as_data()
and "first_name" not in self.errors.as_data() and "first_name" not in self.errors.as_data()