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):
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):

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

View File

@ -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({

View File

@ -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">&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 %}
<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">&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 %}
<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 %}
</div>
{% 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 %}
<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>

View File

@ -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">&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>
{% from "core/macros.jinja" import user_mini_profile %}
{{ user_mini_profile(profile) }}

View File

@ -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'),

View File

@ -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):

View File

@ -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)

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" %}
{% 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 %}

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

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

View File

@ -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.

View File

@ -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 : "

View File

@ -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,

View File

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

View File

@ -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!!!

View File

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

View File

@ -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()