mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-11-04 01:12:08 +01:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			053225c6dc
			...
			sheets
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						51d60d064c
	
				 | 
					
					
						|||
| 
						
						
							
						
						45334e4e02
	
				 | 
					
					
						|||
| 
						
						
							
						
						5174c84b33
	
				 | 
					
					
						|||
| 
						
						
							
						
						51e5e3669e
	
				 | 
					
					
						|||
| 
						
						
							
						
						44994a3ae7
	
				 | 
					
					
						|||
| 
						
						
							
						
						ba017c38c0
	
				 | 
					
					
						
@@ -26,6 +26,10 @@ if "note" in settings.INSTALLED_APPS:
 | 
				
			|||||||
    from note.api.urls import register_note_urls
 | 
					    from note.api.urls import register_note_urls
 | 
				
			||||||
    register_note_urls(router, 'note')
 | 
					    register_note_urls(router, 'note')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if "sheets" in settings.INSTALLED_APPS:
 | 
				
			||||||
 | 
					    from sheets.api.urls import register_sheets_urls
 | 
				
			||||||
 | 
					    register_sheets_urls(router, 'sheets')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if "treasury" in settings.INSTALLED_APPS:
 | 
					if "treasury" in settings.INSTALLED_APPS:
 | 
				
			||||||
    from treasury.api.urls import register_treasury_urls
 | 
					    from treasury.api.urls import register_treasury_urls
 | 
				
			||||||
    register_treasury_urls(router, 'treasury')
 | 
					    register_treasury_urls(router, 'treasury')
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								apps/member/migrations/0009_auto_20220818_1301.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								apps/member/migrations/0009_auto_20220818_1301.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					# Generated by Django 2.2.27 on 2022-08-18 11:01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ('member', '0008_auto_20211005_1544'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.AlterField(
 | 
				
			||||||
 | 
					            model_name='profile',
 | 
				
			||||||
 | 
					            name='promotion',
 | 
				
			||||||
 | 
					            field=models.PositiveSmallIntegerField(default=2022, help_text='Year of entry to the school (None if not ENS student)', null=True, verbose_name='promotion'),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
							
								
								
									
										17
									
								
								apps/note/templates/sheets/order.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								apps/note/templates/sheets/order.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					{% comment %}
 | 
				
			||||||
 | 
					SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					{% endcomment %}
 | 
				
			||||||
 | 
					{% load crispy_forms_tags %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<div class="card bg-light mb-3">
 | 
				
			||||||
 | 
					    <h3 class="card-header text-center">
 | 
				
			||||||
 | 
					        {{ title }}
 | 
				
			||||||
 | 
					    </h3>
 | 
				
			||||||
 | 
					    <div class="card-body">
 | 
				
			||||||
 | 
					        {% crispy form %}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										88
									
								
								apps/note/templates/sheets/waiting_list.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								apps/note/templates/sheets/waiting_list.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<div class="card">
 | 
				
			||||||
 | 
					<div class="card-header text-center">
 | 
				
			||||||
 | 
					    <h1>{{ food.name }}</h1>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="card-body">
 | 
				
			||||||
 | 
					    <div class="row">
 | 
				
			||||||
 | 
					        <div class="card col-xl-6">
 | 
				
			||||||
 | 
					            <div class="card-header text-center">
 | 
				
			||||||
 | 
					                <h2>{% trans "queued"|capfirst %}{% if queue %} ({{ queue|length }}){% endif %}</h2>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="card-body">
 | 
				
			||||||
 | 
					                <ul>
 | 
				
			||||||
 | 
					                    {% for ordered_food in queue %}
 | 
				
			||||||
 | 
					                        <li>
 | 
				
			||||||
 | 
					                            {{ ordered_food.order.note }}
 | 
				
			||||||
 | 
					                            {% if ordered_food.priority %}
 | 
				
			||||||
 | 
					                                <span class="badge badge-secondary">{{ ordered_food.priority }}</span>
 | 
				
			||||||
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    {% empty %}
 | 
				
			||||||
 | 
					                        <div class="alert alert-warning">
 | 
				
			||||||
 | 
					                            {% trans "There is no queued order." %}
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    {% endfor %}
 | 
				
			||||||
 | 
					                </ul>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="card col-xl-6">
 | 
				
			||||||
 | 
					            <div class="card-header text-center">
 | 
				
			||||||
 | 
					                <h2>{% trans "ready"|capfirst %}</h2>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="card-body">
 | 
				
			||||||
 | 
					                <ul>
 | 
				
			||||||
 | 
					                    {% for ordered_food in ready %}
 | 
				
			||||||
 | 
					                        <li>{{ ordered_food.order.note }}</li>
 | 
				
			||||||
 | 
					                    {% empty %}
 | 
				
			||||||
 | 
					                        <div class="alert alert-warning">
 | 
				
			||||||
 | 
					                            {% trans "There is no ready order." %}
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    {% endfor %}
 | 
				
			||||||
 | 
					                </ul>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <hr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <h3>{% trans "Other waiting lists:" %}</h3>
 | 
				
			||||||
 | 
					    <ul>
 | 
				
			||||||
 | 
					        {% for other_food in food.sheet.food_set.all %}
 | 
				
			||||||
 | 
					            {% if other_food != food %}
 | 
				
			||||||
 | 
					                <li>
 | 
				
			||||||
 | 
					                    <a href="{% url 'sheets:waiting_list' pk=other_food.pk %}">{{ other_food }}</a>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					        {% endfor %}
 | 
				
			||||||
 | 
					    </ul>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="card-footer text-center">
 | 
				
			||||||
 | 
					    <a href="{% url 'sheets:queued_list' pk=food.pk %}" class="btn btn-primary">
 | 
				
			||||||
 | 
					        {% trans "Queued orders" %}
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					    <a href="{% url 'sheets:ready_list' pk=food.pk %}" class="btn btn-primary">
 | 
				
			||||||
 | 
					        {% trans "Ready orders" %}
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					    <a href="{% url 'sheets:sheet_detail' pk=food.sheet_id %}" class="btn btn-secondary">
 | 
				
			||||||
 | 
					        {% trans "Back to note sheet detail" %}
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block extrajavascript %}
 | 
				
			||||||
 | 
					    <script>
 | 
				
			||||||
 | 
					        function reload() {
 | 
				
			||||||
 | 
					            reloadWithTurbolinks()
 | 
				
			||||||
 | 
					            timeout = setTimeout(reload, 15000)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (timeout === undefined)
 | 
				
			||||||
 | 
					            var timeout = setTimeout(reload, 15000)
 | 
				
			||||||
 | 
					    </script>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										152
									
								
								apps/note/templates/sheets/waiting_list_detail.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								apps/note/templates/sheets/waiting_list_detail.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<div class="card">
 | 
				
			||||||
 | 
					<div class="card-header text-center">
 | 
				
			||||||
 | 
					    <h1>{{ title }}</h1>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="card-body">
 | 
				
			||||||
 | 
					    {% for of in orders %}
 | 
				
			||||||
 | 
					        <div class="card mb-4">
 | 
				
			||||||
 | 
					            <div class="card-header text-center">
 | 
				
			||||||
 | 
					                <h3>{{ of.order.note }}</h3>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="card-body">
 | 
				
			||||||
 | 
					                <dl class="row">
 | 
				
			||||||
 | 
					                    <dt class="col-xl-3">{% trans 'date'|capfirst %}</dt>
 | 
				
			||||||
 | 
					                    <dd class="col-xl-9">{{ of.order.date }}</dd>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    {% if of.number > 1 %}
 | 
				
			||||||
 | 
					                        <dt class="col-xl-3">{% trans 'order number'|capfirst %}</dt>
 | 
				
			||||||
 | 
					                        <dd class="col-xl-9">{{ of.number }}</dd>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    {% if of.priority %}
 | 
				
			||||||
 | 
					                        <dt class="col-xl-3">{% trans 'priority request'|capfirst %}</dt>
 | 
				
			||||||
 | 
					                        <dd class="col-xl-9">{{ of.priority }}</dd>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    {% if of.remark %}
 | 
				
			||||||
 | 
					                        <dt class="col-xl-3">{% trans 'remark'|capfirst %}</dt>
 | 
				
			||||||
 | 
					                        <dd class="col-xl-9">{{ of.remark }}</dd>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    {% if of.options.count %}
 | 
				
			||||||
 | 
					                        <dt class="col-xl-3">{% trans 'options'|capfirst %}</dt>
 | 
				
			||||||
 | 
					                        <dd class="col-xl-9">{{ of.options.all|join:', ' }}</dd>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                </dl>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="card-footer text-center">
 | 
				
			||||||
 | 
					                {% if list_type != 'READY' %}
 | 
				
			||||||
 | 
					                    <a href="#" class="btn btn-success" onclick="setOrderStatus({{ of.pk }}, 'READY')">
 | 
				
			||||||
 | 
					                        {% trans "Mark as ready" %}
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					                {% if list_type != 'SERVED' %}
 | 
				
			||||||
 | 
					                    <a href="#" class="btn btn-primary" onclick="setOrderStatus({{ of.pk }}, 'SERVED')">
 | 
				
			||||||
 | 
					                        {% trans "Mark as served" %}
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					                {% if list_type != 'QUEUED' %}
 | 
				
			||||||
 | 
					                    <a href="#" class="btn btn-warning" onclick="setOrderStatus({{ of.pk }}, 'QUEUED')">
 | 
				
			||||||
 | 
					                        {% trans "Re-queue" %}
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					                {% if list_type != 'CANCELED' %}
 | 
				
			||||||
 | 
					                    <a href="#" class="btn btn-danger" onclick="setOrderStatus({{ of.pk }}, 'CANCELED')">
 | 
				
			||||||
 | 
					                        {% trans "Cancel" %}
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    {% empty %}
 | 
				
			||||||
 | 
					        <div class="alert alert-warning">
 | 
				
			||||||
 | 
					            {% trans "There is no queued order." %}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    {% endfor %}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="card mt-5">
 | 
				
			||||||
 | 
					<div class="card-body">
 | 
				
			||||||
 | 
					    <h3>{% trans "Other waiting lists:" %}</h3>
 | 
				
			||||||
 | 
					    <ul>
 | 
				
			||||||
 | 
					        {% for other_food in food.sheet.food_set.all %}
 | 
				
			||||||
 | 
					            {% if other_food != food %}
 | 
				
			||||||
 | 
					                <li>
 | 
				
			||||||
 | 
					                    {% if list_type == 'QUEUED' %}
 | 
				
			||||||
 | 
					                        <a href="{% url 'sheets:queued_list' pk=other_food.pk %}">{{ other_food }}</a>
 | 
				
			||||||
 | 
					                    {% else %}
 | 
				
			||||||
 | 
					                        <a href="{% url 'sheets:ready_list' pk=other_food.pk %}">{{ other_food }}</a>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					        {% endfor %}
 | 
				
			||||||
 | 
					    </ul>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="card-footer text-center">
 | 
				
			||||||
 | 
					    {% if list_type != 'QUEUED' %}
 | 
				
			||||||
 | 
					        <a href="{% url 'sheets:queued_list' pk=food.pk %}" class="btn btn-primary">
 | 
				
			||||||
 | 
					            {% trans "Queued orders" %}
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					    {% if list_type != 'READY' %}
 | 
				
			||||||
 | 
					        <a href="{% url 'sheets:ready_list' pk=food.pk %}" class="btn btn-success">
 | 
				
			||||||
 | 
					            {% trans "Ready orders" %}
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					    {% if list_type != 'SERVED' %}
 | 
				
			||||||
 | 
					        <a href="{% url 'sheets:served_list' pk=food.pk %}" class="btn btn-secondary">
 | 
				
			||||||
 | 
					            {% trans "Served orders" %}
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					    {% if list_type != 'CANCELED' %}
 | 
				
			||||||
 | 
					        <a href="{% url 'sheets:canceled_list' pk=food.pk %}" class="btn btn-danger">
 | 
				
			||||||
 | 
					            {% trans "Canceled orders" %}
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					    <a href="{% url 'sheets:waiting_list' pk=food.pk %}" class="btn btn-primary">
 | 
				
			||||||
 | 
					        {% trans "Waiting list" %}
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					    <a href="{% url 'sheets:sheet_detail' pk=food.sheet_id %}" class="btn btn-secondary">
 | 
				
			||||||
 | 
					        {% trans "Back to note sheet detail" %}
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block extrajavascript %}
 | 
				
			||||||
 | 
					    <script>
 | 
				
			||||||
 | 
					        function reload() {
 | 
				
			||||||
 | 
					            reloadWithTurbolinks()
 | 
				
			||||||
 | 
					            timeout = setTimeout(reload, 15000)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (timeout === undefined)
 | 
				
			||||||
 | 
					            var timeout = setTimeout(reload, 15000)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function setOrderStatus(ordered_food_id, status) {
 | 
				
			||||||
 | 
					            fetch('/api/sheets/orderedfood/' + ordered_food_id + '/', {
 | 
				
			||||||
 | 
					                method: 'PATCH',
 | 
				
			||||||
 | 
					                body: JSON.stringify({
 | 
				
			||||||
 | 
					                    status: status,
 | 
				
			||||||
 | 
					                    served_date: status === 'QUEUED' ? null : new Date().toISOString(),
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					                headers: {
 | 
				
			||||||
 | 
					                    'Content-Type': "application/json; charset=UTF-8",
 | 
				
			||||||
 | 
					                    'X-CSRFTOKEN': "{{ csrf_token }}"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }).then(response => response.json()).then(response => {
 | 
				
			||||||
 | 
					                if ('detail' in response)
 | 
				
			||||||
 | 
					                    addMsg("{% trans "An error occurred" %}" + " : " + response['detail'], "danger")
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    clearTimeout(timeout)
 | 
				
			||||||
 | 
					                    reload()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    </script>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
@@ -2928,7 +2928,7 @@
 | 
				
			|||||||
				"application"
 | 
									"application"
 | 
				
			||||||
			],
 | 
								],
 | 
				
			||||||
			"query": "{\"user\": [\"user\"]}",
 | 
								"query": "{\"user\": [\"user\"]}",
 | 
				
			||||||
			"type": "create",
 | 
								"type": "add",
 | 
				
			||||||
			"mask": 1,
 | 
								"mask": 1,
 | 
				
			||||||
			"field": "",
 | 
								"field": "",
 | 
				
			||||||
			"permanent": true,
 | 
								"permanent": true,
 | 
				
			||||||
@@ -3114,10 +3114,10 @@
 | 
				
			|||||||
				187,
 | 
									187,
 | 
				
			||||||
				188,
 | 
									188,
 | 
				
			||||||
				189,
 | 
									189,
 | 
				
			||||||
                190,
 | 
									190,
 | 
				
			||||||
                191,
 | 
									191,
 | 
				
			||||||
                195,
 | 
									195,
 | 
				
			||||||
                196
 | 
									196
 | 
				
			||||||
			]
 | 
								]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
@@ -3159,8 +3159,8 @@
 | 
				
			|||||||
				159,
 | 
									159,
 | 
				
			||||||
				160,
 | 
									160,
 | 
				
			||||||
				179,
 | 
									179,
 | 
				
			||||||
                189,
 | 
									189,
 | 
				
			||||||
                190
 | 
									190
 | 
				
			||||||
			]
 | 
								]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
@@ -3310,10 +3310,10 @@
 | 
				
			|||||||
				176,
 | 
									176,
 | 
				
			||||||
				177,
 | 
									177,
 | 
				
			||||||
				178,
 | 
									178,
 | 
				
			||||||
                188,
 | 
									188,
 | 
				
			||||||
				183,
 | 
									183,
 | 
				
			||||||
                186,
 | 
									186,
 | 
				
			||||||
                187
 | 
									187
 | 
				
			||||||
			]
 | 
								]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
@@ -3508,13 +3508,13 @@
 | 
				
			|||||||
				187,
 | 
									187,
 | 
				
			||||||
				188,
 | 
									188,
 | 
				
			||||||
				189,
 | 
									189,
 | 
				
			||||||
                190,
 | 
									190,
 | 
				
			||||||
                191,
 | 
									191,
 | 
				
			||||||
                192,
 | 
									192,
 | 
				
			||||||
                193,
 | 
									193,
 | 
				
			||||||
                194,
 | 
									194,
 | 
				
			||||||
                195,
 | 
									195,
 | 
				
			||||||
                196
 | 
									196
 | 
				
			||||||
			]
 | 
								]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								apps/sheets/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								apps/sheets/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					default_app_config = 'sheets.apps.SheetsConfig'
 | 
				
			||||||
							
								
								
									
										46
									
								
								apps/sheets/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								apps/sheets/admin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					from note_kfet.admin import admin_site
 | 
				
			||||||
 | 
					from sheets.models import Sheet, Food, FoodOption, Meal, Order, OrderedMeal, OrderedFood, SheetOrderTransaction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(Sheet, site=admin_site)
 | 
				
			||||||
 | 
					class SheetAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(Food, site=admin_site)
 | 
				
			||||||
 | 
					class FoodAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(FoodOption, site=admin_site)
 | 
				
			||||||
 | 
					class FoodOptionAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(Meal, site=admin_site)
 | 
				
			||||||
 | 
					class MealAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(Order, site=admin_site)
 | 
				
			||||||
 | 
					class OrderAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(OrderedMeal, site=admin_site)
 | 
				
			||||||
 | 
					class OrderedMealAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(OrderedFood, site=admin_site)
 | 
				
			||||||
 | 
					class OrderedFoodAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(SheetOrderTransaction, site=admin_site)
 | 
				
			||||||
 | 
					class SheetOrderTransactionAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
							
								
								
									
										0
									
								
								apps/sheets/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								apps/sheets/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										55
									
								
								apps/sheets/api/serializers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								apps/sheets/api/serializers.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from rest_framework import serializers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..models import Sheet, Food, FoodOption, Meal, Order, OrderedMeal, OrderedFood, SheetOrderTransaction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Sheet
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FoodSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Food
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FoodOptionSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = FoodOption
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MealSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Meal
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrderSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Order
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrderedMealSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = OrderedMeal
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrderedFoodSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = OrderedFood
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetOrderTransactionSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = SheetOrderTransaction
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
							
								
								
									
										19
									
								
								apps/sheets/api/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								apps/sheets/api/urls.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from sheets.api.views import SheetViewSet, FoodViewSet, FoodOptionViewSet, MealViewSet, OrderViewSet, \
 | 
				
			||||||
 | 
					    OrderedMealViewSet, OrderedFoodViewSet, SheetOrderTransactionViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def register_sheets_urls(router, path):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Configure router for Sheets REST API.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    router.register(path + '/sheet', SheetViewSet)
 | 
				
			||||||
 | 
					    router.register(path + '/food', FoodViewSet)
 | 
				
			||||||
 | 
					    router.register(path + '/foodoption', FoodOptionViewSet)
 | 
				
			||||||
 | 
					    router.register(path + '/meal', MealViewSet)
 | 
				
			||||||
 | 
					    router.register(path + '/order', OrderViewSet)
 | 
				
			||||||
 | 
					    router.register(path + '/orderedmeal', OrderedMealViewSet)
 | 
				
			||||||
 | 
					    router.register(path + '/orderedfood', OrderedFoodViewSet)
 | 
				
			||||||
 | 
					    router.register(path + '/sheetordertransaction', SheetOrderTransactionViewSet)
 | 
				
			||||||
							
								
								
									
										78
									
								
								apps/sheets/api/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								apps/sheets/api/views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from api.viewsets import ReadProtectedModelViewSet
 | 
				
			||||||
 | 
					from django_filters.rest_framework import DjangoFilterBackend
 | 
				
			||||||
 | 
					from rest_framework.filters import SearchFilter, OrderingFilter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .serializers import SheetSerializer, FoodSerializer, FoodOptionSerializer, MealSerializer, OrderSerializer, \
 | 
				
			||||||
 | 
					    OrderedMealSerializer, OrderedFoodSerializer, SheetOrderTransactionSerializer
 | 
				
			||||||
 | 
					from ..models import Sheet, Food, FoodOption, Meal, Order, OrderedMeal, OrderedFood, SheetOrderTransaction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetViewSet(ReadProtectedModelViewSet):
 | 
				
			||||||
 | 
					    queryset = Sheet.objects.order_by('id')
 | 
				
			||||||
 | 
					    serializer_class = SheetSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
				
			||||||
 | 
					    filterset_fields = ['name', 'date', ]
 | 
				
			||||||
 | 
					    search_fields = ['$name', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FoodViewSet(ReadProtectedModelViewSet):
 | 
				
			||||||
 | 
					    queryset = Food.objects.order_by('id')
 | 
				
			||||||
 | 
					    serializer_class = FoodSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
				
			||||||
 | 
					    filterset_fields = ['name', 'sheet', 'price', 'club', 'available', ]
 | 
				
			||||||
 | 
					    search_fields = ['$name', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FoodOptionViewSet(ReadProtectedModelViewSet):
 | 
				
			||||||
 | 
					    queryset = FoodOption.objects.order_by('id')
 | 
				
			||||||
 | 
					    serializer_class = FoodOptionSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
				
			||||||
 | 
					    filterset_fields = ['name', 'food', 'extra_cost', 'available', ]
 | 
				
			||||||
 | 
					    search_fields = ['$name', '$food__name', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MealViewSet(ReadProtectedModelViewSet):
 | 
				
			||||||
 | 
					    queryset = Meal.objects.order_by('id')
 | 
				
			||||||
 | 
					    serializer_class = MealSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
				
			||||||
 | 
					    filterset_fields = ['name', 'content', 'price', 'available', ]
 | 
				
			||||||
 | 
					    search_fields = ['$name', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrderViewSet(ReadProtectedModelViewSet):
 | 
				
			||||||
 | 
					    queryset = Order.objects.order_by('id')
 | 
				
			||||||
 | 
					    serializer_class = OrderSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
				
			||||||
 | 
					    filterset_fields = ['sheet', 'note', 'date', 'gift', ]
 | 
				
			||||||
 | 
					    search_fields = ['$sheet__name', '$note__alias__name', '$note__alias__normalized_name', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrderedMealViewSet(ReadProtectedModelViewSet):
 | 
				
			||||||
 | 
					    queryset = OrderedMeal.objects.order_by('id')
 | 
				
			||||||
 | 
					    serializer_class = OrderedMealSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend]
 | 
				
			||||||
 | 
					    filterset_fields = ['order', 'meal', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrderedFoodViewSet(ReadProtectedModelViewSet):
 | 
				
			||||||
 | 
					    queryset = OrderedFood.objects.order_by('id')
 | 
				
			||||||
 | 
					    serializer_class = OrderedFoodSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend]
 | 
				
			||||||
 | 
					    filterset_fields = ['order', 'meal', 'food', 'options', 'number', 'status', 'served_date', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetOrderTransactionViewSet(ReadProtectedModelViewSet):
 | 
				
			||||||
 | 
					    queryset = SheetOrderTransaction.objects.order_by('-created_at')
 | 
				
			||||||
 | 
					    serializer_class = SheetOrderTransactionSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
 | 
				
			||||||
 | 
					    filterset_fields = ['source', 'source_alias', 'source__alias__name', 'source__alias__normalized_name',
 | 
				
			||||||
 | 
					                        'destination', 'destination_alias', 'destination__alias__name',
 | 
				
			||||||
 | 
					                        'destination__alias__normalized_name', 'quantity', 'polymorphic_ctype', 'amount',
 | 
				
			||||||
 | 
					                        'created_at', 'valid', 'invalidity_reason', 'ordered_food', ]
 | 
				
			||||||
 | 
					    search_fields = ['$reason', '$source_alias', '$source__alias__name', '$source__alias__normalized_name',
 | 
				
			||||||
 | 
					                     '$destination_alias', '$destination__alias__name', '$destination__alias__normalized_name',
 | 
				
			||||||
 | 
					                     '$invalidity_reason', ]
 | 
				
			||||||
 | 
					    ordering_fields = ['created_at', 'amount', ]
 | 
				
			||||||
							
								
								
									
										10
									
								
								apps/sheets/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								apps/sheets/apps.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetsConfig(AppConfig):
 | 
				
			||||||
 | 
					    name = 'sheets'
 | 
				
			||||||
 | 
					    verbose_name = _('note sheets')
 | 
				
			||||||
							
								
								
									
										67
									
								
								apps/sheets/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								apps/sheets/forms.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					from crispy_forms.helper import FormHelper
 | 
				
			||||||
 | 
					from django import forms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from member.models import Club
 | 
				
			||||||
 | 
					from note_kfet.inputs import AmountInput, Autocomplete, DateTimePickerInput
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .models import Food, FoodOption, Meal, Sheet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetForm(forms.ModelForm):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Sheet
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					        widgets = {
 | 
				
			||||||
 | 
					            'date': DateTimePickerInput(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FoodForm(forms.ModelForm):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Food
 | 
				
			||||||
 | 
					        exclude = ('sheet', )
 | 
				
			||||||
 | 
					        widgets = {
 | 
				
			||||||
 | 
					            'price': AmountInput(),
 | 
				
			||||||
 | 
					            'club': Autocomplete(
 | 
				
			||||||
 | 
					                model=Club,
 | 
				
			||||||
 | 
					                attrs={"api_url": "/api/members/club/"},
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FoodOptionForm(forms.ModelForm):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = FoodOption
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					        widgets = {
 | 
				
			||||||
 | 
					            'extra_cost': AmountInput(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FoodOptionsFormSet = forms.inlineformset_factory(
 | 
				
			||||||
 | 
					    Food,
 | 
				
			||||||
 | 
					    FoodOption,
 | 
				
			||||||
 | 
					    form=FoodOptionForm,
 | 
				
			||||||
 | 
					    extra=0,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FoodOptionFormSetHelper(FormHelper):
 | 
				
			||||||
 | 
					    def __init__(self, form=None):
 | 
				
			||||||
 | 
					        super().__init__(form)
 | 
				
			||||||
 | 
					        self.form_tag = False
 | 
				
			||||||
 | 
					        self.form_method = 'POST'
 | 
				
			||||||
 | 
					        self.form_class = 'form-inline'
 | 
				
			||||||
 | 
					        self.template = 'bootstrap4/table_inline_formset.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MealForm(forms.ModelForm):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Meal
 | 
				
			||||||
 | 
					        exclude = ('sheet', )
 | 
				
			||||||
 | 
					        widgets = {
 | 
				
			||||||
 | 
					            'content': forms.CheckboxSelectMultiple(),
 | 
				
			||||||
 | 
					            'price': AmountInput(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
							
								
								
									
										157
									
								
								apps/sheets/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								apps/sheets/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,157 @@
 | 
				
			|||||||
 | 
					# Generated by Django 2.2.27 on 2022-08-18 11:01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					import django.db.models.deletion
 | 
				
			||||||
 | 
					import django.utils.timezone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    initial = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ('member', '0009_auto_20220818_1301'),
 | 
				
			||||||
 | 
					        ('note', '0006_trust'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='Food',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('name', models.CharField(max_length=255, verbose_name='food')),
 | 
				
			||||||
 | 
					                ('price', models.IntegerField(verbose_name='price')),
 | 
				
			||||||
 | 
					                ('available', models.BooleanField(default=True, help_text="If set to false, this option won't be offered (in case of out of stock)", verbose_name='available')),
 | 
				
			||||||
 | 
					                ('club', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='member.Club', verbose_name='destination club')),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                'verbose_name': 'food',
 | 
				
			||||||
 | 
					                'verbose_name_plural': 'food',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='FoodOption',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('name', models.CharField(max_length=255, verbose_name='name')),
 | 
				
			||||||
 | 
					                ('extra_cost', models.IntegerField(default=0, verbose_name='extra cost')),
 | 
				
			||||||
 | 
					                ('available', models.BooleanField(default=True, help_text="If set to false, this option won't be offered (in case of out of stock)", verbose_name='available')),
 | 
				
			||||||
 | 
					                ('food', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sheets.Food', verbose_name='food')),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                'verbose_name': 'food option',
 | 
				
			||||||
 | 
					                'verbose_name_plural': 'food options',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='Meal',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('name', models.CharField(max_length=255, verbose_name='name')),
 | 
				
			||||||
 | 
					                ('price', models.IntegerField(verbose_name='price')),
 | 
				
			||||||
 | 
					                ('available', models.BooleanField(default=True, help_text="If set to false, this option won't be offered (in case of out of stock)", verbose_name='available')),
 | 
				
			||||||
 | 
					                ('content', models.ManyToManyField(to='sheets.Food', verbose_name='content')),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                'verbose_name': 'meal',
 | 
				
			||||||
 | 
					                'verbose_name_plural': 'meals',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='Order',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('date', models.DateTimeField(auto_now_add=True, verbose_name='date')),
 | 
				
			||||||
 | 
					                ('gift', models.IntegerField(verbose_name='gift')),
 | 
				
			||||||
 | 
					                ('note', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='note.Note', verbose_name='note')),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                'verbose_name': 'order',
 | 
				
			||||||
 | 
					                'verbose_name_plural': 'orders',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='OrderedFood',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('remark', models.TextField(blank=True, default='', verbose_name='remark')),
 | 
				
			||||||
 | 
					                ('priority', models.CharField(blank=True, default='', max_length=64, verbose_name='priority request')),
 | 
				
			||||||
 | 
					                ('number', models.IntegerField(help_text='How many times the user ordered this.', verbose_name='number')),
 | 
				
			||||||
 | 
					                ('status', models.CharField(choices=[('QUEUED', 'queued'), ('READY', 'ready'), ('SERVED', 'served'), ('CANCELED', 'canceled')], max_length=8, verbose_name='status')),
 | 
				
			||||||
 | 
					                ('served_date', models.DateTimeField(default=None, null=True, verbose_name='served date')),
 | 
				
			||||||
 | 
					                ('food', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.Food', verbose_name='food')),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                'verbose_name': 'ordered food',
 | 
				
			||||||
 | 
					                'verbose_name_plural': 'ordered food',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='Sheet',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('name', models.CharField(max_length=255, verbose_name='name')),
 | 
				
			||||||
 | 
					                ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='start date')),
 | 
				
			||||||
 | 
					                ('description', models.TextField(verbose_name='description')),
 | 
				
			||||||
 | 
					                ('visible', models.BooleanField(default=False, help_text='the note sheet will be private until this field is checked.', verbose_name='visible')),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                'verbose_name': 'note sheet',
 | 
				
			||||||
 | 
					                'verbose_name_plural': 'note sheets',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='SheetOrderTransaction',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('transaction_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='note.Transaction')),
 | 
				
			||||||
 | 
					                ('ordered_food', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.OrderedFood', verbose_name='ordered food')),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                'verbose_name': 'sheet order transaction',
 | 
				
			||||||
 | 
					                'verbose_name_plural': 'sheet order transactions',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            bases=('note.transaction',),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='OrderedMeal',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('meal', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.Meal', verbose_name='meal')),
 | 
				
			||||||
 | 
					                ('order', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.Order', verbose_name='order')),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                'verbose_name': 'ordered meal',
 | 
				
			||||||
 | 
					                'verbose_name_plural': 'ordered meals',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name='orderedfood',
 | 
				
			||||||
 | 
					            name='meal',
 | 
				
			||||||
 | 
					            field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='sheets.OrderedMeal', verbose_name='ordered meal'),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name='orderedfood',
 | 
				
			||||||
 | 
					            name='options',
 | 
				
			||||||
 | 
					            field=models.ManyToManyField(blank=True, to='sheets.FoodOption', verbose_name='options'),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name='orderedfood',
 | 
				
			||||||
 | 
					            name='order',
 | 
				
			||||||
 | 
					            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.Order', verbose_name='order'),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name='order',
 | 
				
			||||||
 | 
					            name='sheet',
 | 
				
			||||||
 | 
					            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.Sheet', verbose_name='note sheet'),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name='meal',
 | 
				
			||||||
 | 
					            name='sheet',
 | 
				
			||||||
 | 
					            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sheets.Sheet', verbose_name='note sheet'),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name='food',
 | 
				
			||||||
 | 
					            name='sheet',
 | 
				
			||||||
 | 
					            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sheets.Sheet', verbose_name='note sheet'),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
							
								
								
									
										34
									
								
								apps/sheets/migrations/0002_auto_20220818_1713.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								apps/sheets/migrations/0002_auto_20220818_1713.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					# Generated by Django 2.2.27 on 2022-08-18 15:13
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ('sheets', '0001_initial'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.RemoveField(
 | 
				
			||||||
 | 
					            model_name='order',
 | 
				
			||||||
 | 
					            name='gift',
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name='orderedfood',
 | 
				
			||||||
 | 
					            name='gift',
 | 
				
			||||||
 | 
					            field=models.IntegerField(default=0, verbose_name='gift'),
 | 
				
			||||||
 | 
					            preserve_default=False,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name='orderedmeal',
 | 
				
			||||||
 | 
					            name='gift',
 | 
				
			||||||
 | 
					            field=models.IntegerField(default=0, verbose_name='gift'),
 | 
				
			||||||
 | 
					            preserve_default=False,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AlterField(
 | 
				
			||||||
 | 
					            model_name='orderedfood',
 | 
				
			||||||
 | 
					            name='status',
 | 
				
			||||||
 | 
					            field=models.CharField(choices=[('QUEUED', 'queued'), ('READY', 'ready'), ('SERVED', 'served'), ('CANCELED', 'canceled')], default='QUEUED', max_length=8, verbose_name='status'),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
							
								
								
									
										0
									
								
								apps/sheets/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								apps/sheets/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										289
									
								
								apps/sheets/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								apps/sheets/models.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,289 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.urls import reverse_lazy
 | 
				
			||||||
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from member.models import Club
 | 
				
			||||||
 | 
					from note.models import Note, Transaction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Sheet(models.Model):
 | 
				
			||||||
 | 
					    name = models.CharField(
 | 
				
			||||||
 | 
					        max_length=255,
 | 
				
			||||||
 | 
					        verbose_name=_("name"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    date = models.DateTimeField(
 | 
				
			||||||
 | 
					        verbose_name=_("start date"),
 | 
				
			||||||
 | 
					        default=timezone.now,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    description = models.TextField(
 | 
				
			||||||
 | 
					        verbose_name=_("description"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    visible = models.BooleanField(
 | 
				
			||||||
 | 
					        default=False,
 | 
				
			||||||
 | 
					        verbose_name=_("visible"),
 | 
				
			||||||
 | 
					        help_text=_("the note sheet will be private until this field is checked."),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_absolute_url(self):
 | 
				
			||||||
 | 
					        return reverse_lazy('sheets:sheet_detail', args=(self.pk,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return self.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("note sheet")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("note sheets")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Food(models.Model):
 | 
				
			||||||
 | 
					    name = models.CharField(
 | 
				
			||||||
 | 
					        max_length=255,
 | 
				
			||||||
 | 
					        verbose_name=_("food"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sheet = models.ForeignKey(
 | 
				
			||||||
 | 
					        Sheet,
 | 
				
			||||||
 | 
					        on_delete=models.CASCADE,
 | 
				
			||||||
 | 
					        verbose_name=_("note sheet"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    price = models.IntegerField(
 | 
				
			||||||
 | 
					        verbose_name=_("price"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    club = models.ForeignKey(
 | 
				
			||||||
 | 
					        Club,
 | 
				
			||||||
 | 
					        on_delete=models.PROTECT,
 | 
				
			||||||
 | 
					        verbose_name=_("destination club"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    available = models.BooleanField(
 | 
				
			||||||
 | 
					        default=True,
 | 
				
			||||||
 | 
					        verbose_name=_("available"),
 | 
				
			||||||
 | 
					        help_text=_("If set to false, this option won't be offered (in case of out of stock)"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return self.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("food")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("food")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FoodOption(models.Model):
 | 
				
			||||||
 | 
					    name = models.CharField(
 | 
				
			||||||
 | 
					        max_length=255,
 | 
				
			||||||
 | 
					        verbose_name=_("name"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    food = models.ForeignKey(
 | 
				
			||||||
 | 
					        Food,
 | 
				
			||||||
 | 
					        on_delete=models.CASCADE,
 | 
				
			||||||
 | 
					        verbose_name=_("food"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    extra_cost = models.IntegerField(
 | 
				
			||||||
 | 
					        default=0,
 | 
				
			||||||
 | 
					        verbose_name=_("extra cost"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    available = models.BooleanField(
 | 
				
			||||||
 | 
					        default=True,
 | 
				
			||||||
 | 
					        verbose_name=_("available"),
 | 
				
			||||||
 | 
					        help_text=_("If set to false, this option won't be offered (in case of out of stock)"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return self.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("food option")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("food options")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Meal(models.Model):
 | 
				
			||||||
 | 
					    sheet = models.ForeignKey(
 | 
				
			||||||
 | 
					        Sheet,
 | 
				
			||||||
 | 
					        on_delete=models.CASCADE,
 | 
				
			||||||
 | 
					        verbose_name=_("note sheet"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = models.CharField(
 | 
				
			||||||
 | 
					        max_length=255,
 | 
				
			||||||
 | 
					        verbose_name=_("name"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    content = models.ManyToManyField(
 | 
				
			||||||
 | 
					        Food,
 | 
				
			||||||
 | 
					        verbose_name=_("content"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    price = models.IntegerField(
 | 
				
			||||||
 | 
					        verbose_name=_("price"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    available = models.BooleanField(
 | 
				
			||||||
 | 
					        default=True,
 | 
				
			||||||
 | 
					        verbose_name=_("available"),
 | 
				
			||||||
 | 
					        help_text=_("If set to false, this option won't be offered (in case of out of stock)"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return _("meal").capitalize() + " " + self.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("meal")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("meals")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Order(models.Model):
 | 
				
			||||||
 | 
					    sheet = models.ForeignKey(
 | 
				
			||||||
 | 
					        Sheet,
 | 
				
			||||||
 | 
					        on_delete=models.PROTECT,
 | 
				
			||||||
 | 
					        verbose_name=_("note sheet"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    note = models.ForeignKey(
 | 
				
			||||||
 | 
					        Note,
 | 
				
			||||||
 | 
					        on_delete=models.PROTECT,
 | 
				
			||||||
 | 
					        verbose_name=_("note"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    date = models.DateTimeField(
 | 
				
			||||||
 | 
					        verbose_name=_("date"),
 | 
				
			||||||
 | 
					        auto_now_add=True,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("order")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("orders")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrderedMeal(models.Model):
 | 
				
			||||||
 | 
					    order = models.ForeignKey(
 | 
				
			||||||
 | 
					        Order,
 | 
				
			||||||
 | 
					        on_delete=models.PROTECT,
 | 
				
			||||||
 | 
					        verbose_name=_("order"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    meal = models.ForeignKey(
 | 
				
			||||||
 | 
					        Meal,
 | 
				
			||||||
 | 
					        on_delete=models.PROTECT,
 | 
				
			||||||
 | 
					        verbose_name=_("meal"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gift = models.IntegerField(
 | 
				
			||||||
 | 
					        verbose_name=_("gift"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("ordered meal")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("ordered meals")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrderedFood(models.Model):
 | 
				
			||||||
 | 
					    order = models.ForeignKey(
 | 
				
			||||||
 | 
					        Order,
 | 
				
			||||||
 | 
					        on_delete=models.PROTECT,
 | 
				
			||||||
 | 
					        verbose_name=_("order"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    meal = models.ForeignKey(
 | 
				
			||||||
 | 
					        OrderedMeal,
 | 
				
			||||||
 | 
					        on_delete=models.SET_NULL,
 | 
				
			||||||
 | 
					        null=True,
 | 
				
			||||||
 | 
					        default=None,
 | 
				
			||||||
 | 
					        verbose_name=_("ordered meal"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    food = models.ForeignKey(
 | 
				
			||||||
 | 
					        Food,
 | 
				
			||||||
 | 
					        on_delete=models.PROTECT,
 | 
				
			||||||
 | 
					        verbose_name=_("food"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    options = models.ManyToManyField(
 | 
				
			||||||
 | 
					        FoodOption,
 | 
				
			||||||
 | 
					        blank=True,
 | 
				
			||||||
 | 
					        verbose_name=_("options"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    remark = models.TextField(
 | 
				
			||||||
 | 
					        blank=True,
 | 
				
			||||||
 | 
					        default="",
 | 
				
			||||||
 | 
					        verbose_name=_("remark"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priority = models.CharField(
 | 
				
			||||||
 | 
					        max_length=64,
 | 
				
			||||||
 | 
					        blank=True,
 | 
				
			||||||
 | 
					        default="",
 | 
				
			||||||
 | 
					        verbose_name=_("priority request"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gift = models.IntegerField(
 | 
				
			||||||
 | 
					        verbose_name=_("gift"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    number = models.IntegerField(
 | 
				
			||||||
 | 
					        verbose_name=_("number"),
 | 
				
			||||||
 | 
					        help_text=_("How many times the user ordered this."),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    status = models.CharField(
 | 
				
			||||||
 | 
					        max_length=8,
 | 
				
			||||||
 | 
					        choices=[
 | 
				
			||||||
 | 
					            ('QUEUED', _("queued")),
 | 
				
			||||||
 | 
					            ('READY', _("ready")),
 | 
				
			||||||
 | 
					            ('SERVED', _("served")),
 | 
				
			||||||
 | 
					            ('CANCELED', _("canceled")),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        default='QUEUED',
 | 
				
			||||||
 | 
					        verbose_name=_("status"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    served_date = models.DateTimeField(
 | 
				
			||||||
 | 
					        null=True,
 | 
				
			||||||
 | 
					        default=None,
 | 
				
			||||||
 | 
					        verbose_name=_("served date")
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("ordered food")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("ordered food")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetOrderTransaction(Transaction):
 | 
				
			||||||
 | 
					    ordered_food = models.ForeignKey(
 | 
				
			||||||
 | 
					        OrderedFood,
 | 
				
			||||||
 | 
					        on_delete=models.PROTECT,
 | 
				
			||||||
 | 
					        verbose_name=_("ordered food"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def type(self):
 | 
				
			||||||
 | 
					        return _("note sheet")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def get_price(self):
 | 
				
			||||||
 | 
					        if self.ordered_food.meal:
 | 
				
			||||||
 | 
					            return self.ordered_food.meal.meal.price + self.ordered_food.meal.gift + sum(
 | 
				
			||||||
 | 
					                sum(opt.extra_cost for opt in ordered_food.options.all())
 | 
				
			||||||
 | 
					                for ordered_food in self.ordered_food.meal.orderedfood_set.exclude(status='CANCELED').all())
 | 
				
			||||||
 | 
					        elif self.ordered_food.status == 'CANCELED':
 | 
				
			||||||
 | 
					            return 0
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return self.ordered_food.food.price + self.ordered_food.gift \
 | 
				
			||||||
 | 
					                   + sum(opt.extra_cost for opt in self.ordered_food.options.all())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("sheet order transaction")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("sheet order transactions")
 | 
				
			||||||
							
								
								
									
										22
									
								
								apps/sheets/tables.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								apps/sheets/tables.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import django_tables2 as tables
 | 
				
			||||||
 | 
					from django.urls import reverse_lazy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from sheets.models import Sheet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetTable(tables.Table):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        attrs = {
 | 
				
			||||||
 | 
					            'class': 'table table-condensed table-striped table-hover'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        model = Sheet
 | 
				
			||||||
 | 
					        template_name = 'django_tables2/bootstrap4.html'
 | 
				
			||||||
 | 
					        fields = ('name', 'date', )
 | 
				
			||||||
 | 
					        row_attrs = {
 | 
				
			||||||
 | 
					            'class': 'table-row',
 | 
				
			||||||
 | 
					            'id': lambda record: "row-" + str(record.pk),
 | 
				
			||||||
 | 
					            'data-href': lambda record: reverse_lazy('sheets:sheet_detail', args=(record.pk,))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
							
								
								
									
										86
									
								
								apps/sheets/templates/sheets/food_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								apps/sheets/templates/sheets/food_form.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					{% comment %}
 | 
				
			||||||
 | 
					SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					{% endcomment %}
 | 
				
			||||||
 | 
					{% load crispy_forms_tags %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<div class="card bg-light mb-3">
 | 
				
			||||||
 | 
					    <h3 class="card-header text-center">
 | 
				
			||||||
 | 
					        {{ title }}
 | 
				
			||||||
 | 
					    </h3>
 | 
				
			||||||
 | 
					    <div class="card-body">
 | 
				
			||||||
 | 
					        <form method="post">
 | 
				
			||||||
 | 
					            {% csrf_token %}
 | 
				
			||||||
 | 
					            {{ form|crispy }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {# The next part concerns the option formset #}
 | 
				
			||||||
 | 
					            {# Generate some hidden fields that manage the number of options, and make easier the parsing #}
 | 
				
			||||||
 | 
					            {{ formset.management_form }}
 | 
				
			||||||
 | 
					            <table class="table table-condensed table-striped">
 | 
				
			||||||
 | 
					                {# Fill initial data #}
 | 
				
			||||||
 | 
					                {% for form in formset %}
 | 
				
			||||||
 | 
					                {% if forloop.first %}
 | 
				
			||||||
 | 
					                <thead>
 | 
				
			||||||
 | 
					                    <tr>
 | 
				
			||||||
 | 
					                        <th>{{ form.name.label }}<span class="asteriskField">*</span></th>
 | 
				
			||||||
 | 
					                        <th>{{ form.extra_cost.label }}<span class="asteriskField">*</span></th>
 | 
				
			||||||
 | 
					                        <th>{{ form.available.label }}<span class="asteriskField">*</span></th>
 | 
				
			||||||
 | 
					                    </tr>
 | 
				
			||||||
 | 
					                </thead>
 | 
				
			||||||
 | 
					                <tbody id="form_body">
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                    <tr class="row-formset">
 | 
				
			||||||
 | 
					                        <td>{{ form.name }}</td>
 | 
				
			||||||
 | 
					                        <td>{{ form.extra_cost }}</td>
 | 
				
			||||||
 | 
					                        <td>{{ form.available }}</td>
 | 
				
			||||||
 | 
					                        {# These fields are hidden but handled by the formset to link the id and the invoice id #}
 | 
				
			||||||
 | 
					                        {{ form.food }}
 | 
				
			||||||
 | 
					                        {{ form.id }}
 | 
				
			||||||
 | 
					                    </tr>
 | 
				
			||||||
 | 
					                    {% endfor %}
 | 
				
			||||||
 | 
					                </tbody>
 | 
				
			||||||
 | 
					            </table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {# Display buttons to add and remove options #}
 | 
				
			||||||
 | 
					            <div class="card-body">
 | 
				
			||||||
 | 
					            <button type="button" id="add_more" class="btn btn-success">{% trans "Add option" %}</button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{# Hidden div that store an empty product form, to be copied into new forms #}
 | 
				
			||||||
 | 
					<div id="empty_form" style="display: none;">
 | 
				
			||||||
 | 
					    <table class='no_error'>
 | 
				
			||||||
 | 
					        <tbody id="for_real">
 | 
				
			||||||
 | 
					            <tr class="row-formset">
 | 
				
			||||||
 | 
					                <td>{{ formset.empty_form.name }}</td>
 | 
				
			||||||
 | 
					                <td>{{ formset.empty_form.extra_cost }} </td>
 | 
				
			||||||
 | 
					                <td>{{ formset.empty_form.available }}</td>
 | 
				
			||||||
 | 
					                {{ formset.empty_form.food }}
 | 
				
			||||||
 | 
					                {{ formset.empty_form.id }}
 | 
				
			||||||
 | 
					            </tr>
 | 
				
			||||||
 | 
					        </tbody>
 | 
				
			||||||
 | 
					    </table>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block extrajavascript %}
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					    /* script that handles add and remove lines */
 | 
				
			||||||
 | 
					    IDS = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $("#id_foodoption_set-TOTAL_FORMS").val($(".row-formset").length - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $('#add_more').click(function () {
 | 
				
			||||||
 | 
					        let form_idx = $('#id_foodoption_set-TOTAL_FORMS').val();
 | 
				
			||||||
 | 
					        $('#form_body').append($('#for_real').html().replace(/__prefix__/g, form_idx));
 | 
				
			||||||
 | 
					        $('#id_foodoption_set-TOTAL_FORMS').val(parseInt(form_idx) + 1);
 | 
				
			||||||
 | 
					        $('#id_foodoption_set-' + parseInt(form_idx) + '-id').val(IDS[parseInt(form_idx)]);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										21
									
								
								apps/sheets/templates/sheets/meal_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								apps/sheets/templates/sheets/meal_form.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					{% comment %}
 | 
				
			||||||
 | 
					SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					{% endcomment %}
 | 
				
			||||||
 | 
					{% load crispy_forms_tags %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<div class="card bg-light mb-3">
 | 
				
			||||||
 | 
					    <h3 class="card-header text-center">
 | 
				
			||||||
 | 
					        {{ title }}
 | 
				
			||||||
 | 
					    </h3>
 | 
				
			||||||
 | 
					    <div class="card-body">
 | 
				
			||||||
 | 
					        <form method="post">
 | 
				
			||||||
 | 
					            {% csrf_token %}
 | 
				
			||||||
 | 
					            {{ form|crispy }}
 | 
				
			||||||
 | 
					            <button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										87
									
								
								apps/sheets/templates/sheets/sheet_detail.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								apps/sheets/templates/sheets/sheet_detail.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					{% load pretty_money %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<div class="card">
 | 
				
			||||||
 | 
					<div class="card-header text-center">
 | 
				
			||||||
 | 
					    <h1>{{ sheet.name }}</h1>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="card-body">
 | 
				
			||||||
 | 
					    <div class="alert alert-secondary">
 | 
				
			||||||
 | 
					        <div class="row">
 | 
				
			||||||
 | 
					            <div class="col-sm-11">
 | 
				
			||||||
 | 
					                {{ sheet.description }}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            {% if can_change_sheet %}
 | 
				
			||||||
 | 
					                <div class="col-sm-1">
 | 
				
			||||||
 | 
					                    <a class="badge badge-primary" href="{% url 'sheets:sheet_update' pk=sheet.pk %}">
 | 
				
			||||||
 | 
					                        <i class="fa fa-edit"></i>
 | 
				
			||||||
 | 
					                        {% trans "Edit" %}
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <h3>{% trans "menu"|capfirst %} :</h3>
 | 
				
			||||||
 | 
					    <ul>
 | 
				
			||||||
 | 
					        {% for meal in sheet.meal_set.all %}
 | 
				
			||||||
 | 
					            <li{% if not meal.available %} class="text-danger" style="text-decoration: line-through !important;" title="{% trans "This product is unavailable." %}"{% endif %}>
 | 
				
			||||||
 | 
					                {{ meal }} ({{ meal.price|pretty_money }})
 | 
				
			||||||
 | 
					                {% if can_change_sheet %}
 | 
				
			||||||
 | 
					                    <a href="{% url 'sheets:meal_update' pk=meal.pk %}" class="badge badge-primary">
 | 
				
			||||||
 | 
					                        <i class="fa fa-edit"></i>
 | 
				
			||||||
 | 
					                        {% trans "Edit" %}
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					        {% endfor %}
 | 
				
			||||||
 | 
					        <hr>
 | 
				
			||||||
 | 
					        {% for food in sheet.food_set.all %}
 | 
				
			||||||
 | 
					            <li{% if not food.available %} class="text-danger" style="text-decoration: line-through !important;" title="{% trans "This product is unavailable." %}"{% endif %}>
 | 
				
			||||||
 | 
					                {{ food }} ({{ food.price|pretty_money }})
 | 
				
			||||||
 | 
					                <a href="{% url 'sheets:waiting_list' pk=food.pk %}" class="badge badge-primary">
 | 
				
			||||||
 | 
					                    <i class="fa fa-list"></i>
 | 
				
			||||||
 | 
					                    {% trans "Waiting list" %}
 | 
				
			||||||
 | 
					                </a>
 | 
				
			||||||
 | 
					                {% if can_change_sheet %}
 | 
				
			||||||
 | 
					                    <a href="{% url 'sheets:food_update' pk=food.pk %}" class="badge badge-primary">
 | 
				
			||||||
 | 
					                        <i class="fa fa-edit"></i>
 | 
				
			||||||
 | 
					                        {% trans "Edit" %}
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					                {% if food.foodoption_set.all %}
 | 
				
			||||||
 | 
					                    <ul>
 | 
				
			||||||
 | 
					                        {% for option in food.foodoption_set.all %}
 | 
				
			||||||
 | 
					                            <li{% if not option.available %} class="text-danger" style="text-decoration: line-through !important;" title="{% trans "This product is unavailable." %}"{% endif %}>
 | 
				
			||||||
 | 
					                                {{ option }}{% if option.extra_cost %} ({{ option.extra_cost|pretty_money }}){% endif %}
 | 
				
			||||||
 | 
					                            </li>
 | 
				
			||||||
 | 
					                        {% endfor %}
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					        {% empty %}
 | 
				
			||||||
 | 
					            <div class="alert alert-warning">
 | 
				
			||||||
 | 
					                {% trans "The menu is empty for now." %}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        {% endfor %}
 | 
				
			||||||
 | 
					    </ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="text-center">
 | 
				
			||||||
 | 
					        {% if can_add_food %}
 | 
				
			||||||
 | 
					            <a href="{% url 'sheets:food_create' pk=sheet.pk %}" class="btn btn-primary">{% trans "Add new food" %}</a>
 | 
				
			||||||
 | 
					        {% endif %}
 | 
				
			||||||
 | 
					        {% if can_add_meal %}
 | 
				
			||||||
 | 
					            <a href="{% url 'sheets:meal_create' pk=sheet.pk %}" class="btn btn-primary">{% trans "Add new meal" %}</a>
 | 
				
			||||||
 | 
					        {% endif %}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="card-footer text-center">
 | 
				
			||||||
 | 
					    <a href="{% url 'sheets:sheet_order' pk=sheet.pk %}" class="btn btn-success">
 | 
				
			||||||
 | 
					        {% trans "Order now" %}
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										21
									
								
								apps/sheets/templates/sheets/sheet_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								apps/sheets/templates/sheets/sheet_form.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					{% comment %}
 | 
				
			||||||
 | 
					SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					{% endcomment %}
 | 
				
			||||||
 | 
					{% load crispy_forms_tags %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<div class="card bg-light mb-3">
 | 
				
			||||||
 | 
					    <h3 class="card-header text-center">
 | 
				
			||||||
 | 
					        {{ title }}
 | 
				
			||||||
 | 
					    </h3>
 | 
				
			||||||
 | 
					    <div class="card-body">
 | 
				
			||||||
 | 
					        <form method="post">
 | 
				
			||||||
 | 
					            {% csrf_token %}
 | 
				
			||||||
 | 
					            {{ form|crispy }}
 | 
				
			||||||
 | 
					            <button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										74
									
								
								apps/sheets/templates/sheets/sheet_list.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								apps/sheets/templates/sheets/sheet_list.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					{% comment %}
 | 
				
			||||||
 | 
					SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					{% endcomment %}
 | 
				
			||||||
 | 
					{% load render_table from django_tables2 %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<div class="row justify-content-center mb-4">
 | 
				
			||||||
 | 
					    <div class="col-md-10 text-center">
 | 
				
			||||||
 | 
					        <input class="form-control mx-auto w-25" type="text" onkeyup="search_field_moved()" id="search_field"/>
 | 
				
			||||||
 | 
					        {% if can_create_sheet %}
 | 
				
			||||||
 | 
					            <hr>
 | 
				
			||||||
 | 
					            <a class="btn btn-primary text-center my-4" href="{% url 'sheets:sheet_create' %}">{% trans "Create a sheet" %}</a>
 | 
				
			||||||
 | 
					        {% endif %}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="row justify-content-center">   
 | 
				
			||||||
 | 
					    <div class="col-md-10">
 | 
				
			||||||
 | 
					        <div class="card card-border shadow">
 | 
				
			||||||
 | 
					            <div class="card-header text-center">
 | 
				
			||||||
 | 
					                <h5> {% trans "Note sheet listing" %}</h5>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="card-body px-0 py-0" id="sheets_table">
 | 
				
			||||||
 | 
					                {% render_table table %}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					{% block extrajavascript %}
 | 
				
			||||||
 | 
					<script type="text/javascript">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getInfo() {
 | 
				
			||||||
 | 
					    var asked = $("#search_field").val();
 | 
				
			||||||
 | 
					    /* on ne fait la requête que si on a au moins un caractère pour chercher */
 | 
				
			||||||
 | 
					    var sel = $(".table-row");
 | 
				
			||||||
 | 
					    if (asked.length >= 1) {
 | 
				
			||||||
 | 
					        $.getJSON("/api/sheets/sheet/?format=json&search="+asked, function(buttons){
 | 
				
			||||||
 | 
					            let selected_id = buttons.results.map((a => "#row-"+a.id));
 | 
				
			||||||
 | 
					            if (selected_id.length)
 | 
				
			||||||
 | 
					                $(".table-row,"+selected_id.join()).show();
 | 
				
			||||||
 | 
					            $(".table-row").not(selected_id.join()).hide();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }else{
 | 
				
			||||||
 | 
					        // show everything
 | 
				
			||||||
 | 
					        $('table tr').show();
 | 
				
			||||||
 | 
					    }       
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					var timer;
 | 
				
			||||||
 | 
					var timer_on;
 | 
				
			||||||
 | 
					/* Fontion appelée quand le texte change (délenche le timer) */
 | 
				
			||||||
 | 
					function search_field_moved(secondfield) {
 | 
				
			||||||
 | 
					    if (timer_on) { // Si le timer a déjà été lancé, on réinitialise le compteur.
 | 
				
			||||||
 | 
					        clearTimeout(timer);
 | 
				
			||||||
 | 
					        timer = setTimeout("getInfo(" + secondfield + ")", 300);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else { // Sinon, on le lance et on enregistre le fait qu'il tourne.
 | 
				
			||||||
 | 
					        timer = setTimeout("getInfo(" + secondfield + ")", 300);
 | 
				
			||||||
 | 
					        timer_on = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// clickable row 
 | 
				
			||||||
 | 
					$(document).ready(function($) {
 | 
				
			||||||
 | 
					    $(".table-row").click(function() {
 | 
				
			||||||
 | 
					        window.document.location = $(this).data("href");
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										0
									
								
								apps/sheets/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								apps/sheets/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										26
									
								
								apps/sheets/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								apps/sheets/urls.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from sheets.views import FoodCreateView, FoodUpdateView, MealCreateView, MealUpdateView, OrderView, \
 | 
				
			||||||
 | 
					    SheetCreateView, SheetDetailView, SheetListView, SheetUpdateView, WaitingListDetailView, WaitingListView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app_name = 'sheets'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					urlpatterns = [
 | 
				
			||||||
 | 
					    path('list/', SheetListView.as_view(), name="sheet_list"),
 | 
				
			||||||
 | 
					    path('create/', SheetCreateView.as_view(), name="sheet_create"),
 | 
				
			||||||
 | 
					    path('update/<int:pk>/', SheetUpdateView.as_view(), name="sheet_update"),
 | 
				
			||||||
 | 
					    path('detail/<int:pk>/', SheetDetailView.as_view(), name="sheet_detail"),
 | 
				
			||||||
 | 
					    path('food/create/<int:pk>/', FoodCreateView.as_view(), name="food_create"),
 | 
				
			||||||
 | 
					    path('food/<int:pk>/update/', FoodUpdateView.as_view(), name="food_update"),
 | 
				
			||||||
 | 
					    path('meal/create/<int:pk>/', MealCreateView.as_view(), name="meal_create"),
 | 
				
			||||||
 | 
					    path('meal/<int:pk>/update/', MealUpdateView.as_view(), name="meal_update"),
 | 
				
			||||||
 | 
					    path('order/<int:pk>/', OrderView.as_view(), name="sheet_order"),
 | 
				
			||||||
 | 
					    path('waiting-list/<int:pk>/', WaitingListView.as_view(), name="waiting_list"),
 | 
				
			||||||
 | 
					    path('waiting-list/<int:pk>/queued/', WaitingListDetailView.as_view(), name="queued_list"),
 | 
				
			||||||
 | 
					    path('waiting-list/<int:pk>/ready/', WaitingListDetailView.as_view(), name="ready_list"),
 | 
				
			||||||
 | 
					    path('waiting-list/<int:pk>/served/', WaitingListDetailView.as_view(), name="served_list"),
 | 
				
			||||||
 | 
					    path('waiting-list/<int:pk>/canceled/', WaitingListDetailView.as_view(), name="canceled_list"),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										444
									
								
								apps/sheets/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										444
									
								
								apps/sheets/views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,444 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					from datetime import timedelta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from crispy_forms.bootstrap import Accordion, AccordionGroup, FormActions
 | 
				
			||||||
 | 
					from crispy_forms.helper import FormHelper
 | 
				
			||||||
 | 
					from crispy_forms.layout import Fieldset, Submit, Row, Field
 | 
				
			||||||
 | 
					from django import forms
 | 
				
			||||||
 | 
					from django.contrib.auth.mixins import LoginRequiredMixin
 | 
				
			||||||
 | 
					from django.db import transaction
 | 
				
			||||||
 | 
					from django.forms import Form
 | 
				
			||||||
 | 
					from django.urls import reverse_lazy
 | 
				
			||||||
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					from django.views.generic import DetailView, UpdateView, FormView
 | 
				
			||||||
 | 
					from django_tables2 import SingleTableView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from note.models import Alias, Note
 | 
				
			||||||
 | 
					from note.templatetags.pretty_money import pretty_money
 | 
				
			||||||
 | 
					from note_kfet.inputs import AmountInput, Autocomplete
 | 
				
			||||||
 | 
					from permission.backends import PermissionBackend
 | 
				
			||||||
 | 
					from permission.views import ProtectQuerysetMixin, ProtectedCreateView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .forms import FoodForm, MealForm, SheetForm, FoodOptionsFormSet, FoodOptionFormSetHelper
 | 
				
			||||||
 | 
					from .models import Sheet, Food, Meal, Order, OrderedMeal, OrderedFood, SheetOrderTransaction
 | 
				
			||||||
 | 
					from .tables import SheetTable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
 | 
				
			||||||
 | 
					    model = Sheet
 | 
				
			||||||
 | 
					    table_class = SheetTable
 | 
				
			||||||
 | 
					    ordering = '-date'
 | 
				
			||||||
 | 
					    extra_context = {"title": _("Search note sheet")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
 | 
					        context = super().get_context_data(**kwargs)
 | 
				
			||||||
 | 
					        context["can_create_sheet"] = PermissionBackend.check_perm(self.request, "sheets.add_sheet", Sheet(
 | 
				
			||||||
 | 
					            name="Test",
 | 
				
			||||||
 | 
					            date=timezone.now(),
 | 
				
			||||||
 | 
					            description="Test sheet",
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetCreateView(ProtectQuerysetMixin, ProtectedCreateView):
 | 
				
			||||||
 | 
					    model = Sheet
 | 
				
			||||||
 | 
					    form_class = SheetForm
 | 
				
			||||||
 | 
					    extra_context = {"title": _("Create note sheet")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_sample_object(self):
 | 
				
			||||||
 | 
					        return Sheet(
 | 
				
			||||||
 | 
					            name="Test",
 | 
				
			||||||
 | 
					            date=timezone.now(),
 | 
				
			||||||
 | 
					            description="Test",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
 | 
				
			||||||
 | 
					    model = Sheet
 | 
				
			||||||
 | 
					    form_class = SheetForm
 | 
				
			||||||
 | 
					    extra_context = {"title": _("Update note sheet")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SheetDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
 | 
				
			||||||
 | 
					    model = Sheet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
 | 
					        context = super().get_context_data()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        context['can_change_sheet'] = PermissionBackend.check_perm(self.request, 'sheets.change_sheet', self.object)
 | 
				
			||||||
 | 
					        context['can_add_meal'] = PermissionBackend.check_perm(self.request,
 | 
				
			||||||
 | 
					                                                               'sheets.add_meal',
 | 
				
			||||||
 | 
					                                                               Meal(sheet=self.object, name="Test", price=500))
 | 
				
			||||||
 | 
					        context['can_add_food'] = PermissionBackend.check_perm(self.request,
 | 
				
			||||||
 | 
					                                                               'sheets.add_food',
 | 
				
			||||||
 | 
					                                                               Food(sheet=self.object, name="Test", price=500))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
 | 
				
			||||||
 | 
					    model = Food
 | 
				
			||||||
 | 
					    form_class = FoodForm
 | 
				
			||||||
 | 
					    extra_context = {"title": _("Create new food")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_sample_object(self):
 | 
				
			||||||
 | 
					        return Food(
 | 
				
			||||||
 | 
					            sheet_id=self.kwargs['pk'],
 | 
				
			||||||
 | 
					            name="Test",
 | 
				
			||||||
 | 
					            price=500,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
 | 
					        context = super().get_context_data(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        form = context['form']
 | 
				
			||||||
 | 
					        form.helper = FormHelper()
 | 
				
			||||||
 | 
					        # Remove form tag on the generation of the form in the template (already present on the template)
 | 
				
			||||||
 | 
					        form.helper.form_tag = False
 | 
				
			||||||
 | 
					        # The formset handles the set of the products
 | 
				
			||||||
 | 
					        form_set = FoodOptionsFormSet(instance=form.instance)
 | 
				
			||||||
 | 
					        context['formset'] = form_set
 | 
				
			||||||
 | 
					        context['helper'] = FoodOptionFormSetHelper()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def form_valid(self, form):
 | 
				
			||||||
 | 
					        form.instance.sheet_id = self.kwargs['pk']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # For each product, we save it
 | 
				
			||||||
 | 
					        formset = FoodOptionsFormSet(self.request.POST, instance=form.instance)
 | 
				
			||||||
 | 
					        if formset.is_valid():
 | 
				
			||||||
 | 
					            for f in formset:
 | 
				
			||||||
 | 
					                # We don't save the product if the designation is not entered, ie. if the line is empty
 | 
				
			||||||
 | 
					                if f.is_valid() and f.instance.name:
 | 
				
			||||||
 | 
					                    f.save()
 | 
				
			||||||
 | 
					                    f.instance.save()
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    f.instance = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return super().form_valid(form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_success_url(self):
 | 
				
			||||||
 | 
					        return reverse_lazy('sheets:sheet_detail', args=(self.kwargs['pk'],))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
 | 
				
			||||||
 | 
					    model = Food
 | 
				
			||||||
 | 
					    form_class = FoodForm
 | 
				
			||||||
 | 
					    extra_context = {"title": _("Update food")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
 | 
					        context = super().get_context_data(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        form = context['form']
 | 
				
			||||||
 | 
					        form.helper = FormHelper()
 | 
				
			||||||
 | 
					        # Remove form tag on the generation of the form in the template (already present on the template)
 | 
				
			||||||
 | 
					        form.helper.form_tag = False
 | 
				
			||||||
 | 
					        # The formset handles the set of the products
 | 
				
			||||||
 | 
					        form_set = FoodOptionsFormSet(instance=form.instance)
 | 
				
			||||||
 | 
					        context['formset'] = form_set
 | 
				
			||||||
 | 
					        context['helper'] = FoodOptionFormSetHelper()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def form_valid(self, form):
 | 
				
			||||||
 | 
					        # For each product, we save it
 | 
				
			||||||
 | 
					        formset = FoodOptionsFormSet(self.request.POST, instance=form.instance)
 | 
				
			||||||
 | 
					        if formset.is_valid():
 | 
				
			||||||
 | 
					            for f in formset:
 | 
				
			||||||
 | 
					                # We don't save the product if the designation is not entered, ie. if the line is empty
 | 
				
			||||||
 | 
					                if f.is_valid() and f.instance.name:
 | 
				
			||||||
 | 
					                    f.save()
 | 
				
			||||||
 | 
					                    f.instance.save()
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    f.instance = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return super().form_valid(form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_success_url(self):
 | 
				
			||||||
 | 
					        return reverse_lazy('sheets:sheet_detail', args=(self.object.sheet_id,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MealCreateView(ProtectQuerysetMixin, ProtectedCreateView):
 | 
				
			||||||
 | 
					    model = Meal
 | 
				
			||||||
 | 
					    form_class = MealForm
 | 
				
			||||||
 | 
					    extra_context = {"title": _("Create new meal")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_sample_object(self):
 | 
				
			||||||
 | 
					        return Meal(
 | 
				
			||||||
 | 
					            sheet_id=self.kwargs['pk'],
 | 
				
			||||||
 | 
					            name="Test",
 | 
				
			||||||
 | 
					            price=500,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_form(self, form_class=None):
 | 
				
			||||||
 | 
					        form = super().get_form(form_class)
 | 
				
			||||||
 | 
					        form.fields['content'].queryset = form.fields['content'].queryset.filter(sheet_id=self.kwargs['pk'])
 | 
				
			||||||
 | 
					        return form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def form_valid(self, form):
 | 
				
			||||||
 | 
					        form.instance.sheet_id = self.kwargs['pk']
 | 
				
			||||||
 | 
					        return super().form_valid(form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_success_url(self):
 | 
				
			||||||
 | 
					        return reverse_lazy('sheets:sheet_detail', args=(self.object.sheet_id,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MealUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
 | 
				
			||||||
 | 
					    model = Meal
 | 
				
			||||||
 | 
					    form_class = MealForm
 | 
				
			||||||
 | 
					    extra_context = {"title": _("Update meal")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_form(self, form_class=None):
 | 
				
			||||||
 | 
					        form = super().get_form(form_class)
 | 
				
			||||||
 | 
					        form.fields['content'].queryset = form.fields['content'].queryset.filter(sheet=self.object.sheet)
 | 
				
			||||||
 | 
					        return form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_success_url(self):
 | 
				
			||||||
 | 
					        return reverse_lazy('sheets:sheet_detail', args=(self.object.sheet_id,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrderView(LoginRequiredMixin, FormView, DetailView):
 | 
				
			||||||
 | 
					    model = Sheet
 | 
				
			||||||
 | 
					    template_name = 'sheets/order.html'
 | 
				
			||||||
 | 
					    extra_context = {'title': _("Order now")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_form(self, form_class=None):
 | 
				
			||||||
 | 
					        form = Form()
 | 
				
			||||||
 | 
					        form.helper = FormHelper()
 | 
				
			||||||
 | 
					        layout_fields = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.object = self.get_object()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        form.fields['note'] = forms.ModelChoiceField(
 | 
				
			||||||
 | 
					            queryset=Note.objects.filter(PermissionBackend.filter_queryset(self.request, Note, 'note.view_note')),
 | 
				
			||||||
 | 
					            label=_("Orderer"),
 | 
				
			||||||
 | 
					            initial=self.request.user.note,
 | 
				
			||||||
 | 
					            widget=Autocomplete(
 | 
				
			||||||
 | 
					                model=Note,
 | 
				
			||||||
 | 
					                attrs={
 | 
				
			||||||
 | 
					                    "api_url": "/api/note/note/",
 | 
				
			||||||
 | 
					                    'placeholder': _("Who orders")
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        layout_fields.append(Field('note', css_class='is-valid'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for meal in self.object.meal_set.filter(available=True).all():
 | 
				
			||||||
 | 
					            form.fields[f'meal_{meal.id}_quantity'] = forms.IntegerField(
 | 
				
			||||||
 | 
					                label=_("Quantity"),
 | 
				
			||||||
 | 
					                initial=0,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            form.fields[f'meal_{meal.id}_gift'] = forms.IntegerField(
 | 
				
			||||||
 | 
					                label=_("gift").capitalize(),
 | 
				
			||||||
 | 
					                initial=0,
 | 
				
			||||||
 | 
					                widget=AmountInput(),
 | 
				
			||||||
 | 
					                help_text=_("Be careful: this gift will be multiplied for each order."),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            form.fields[f'meal_{meal.id}_remark'] = forms.CharField(
 | 
				
			||||||
 | 
					                max_length=255,
 | 
				
			||||||
 | 
					                required=False,
 | 
				
			||||||
 | 
					                label=_("remark").capitalize(),
 | 
				
			||||||
 | 
					                help_text=_("Allergies,…"),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            form.fields[f'meal_{meal.id}_priority'] = forms.CharField(
 | 
				
			||||||
 | 
					                max_length=64,
 | 
				
			||||||
 | 
					                required=False,
 | 
				
			||||||
 | 
					                label=_("priority request").capitalize(),
 | 
				
			||||||
 | 
					                help_text=_("Lesson at 13h30,…"),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ag = AccordionGroup(f"{meal} ({pretty_money(meal.price)})",
 | 
				
			||||||
 | 
					                                Row(Field(f'meal_{meal.id}_quantity', wrapper_class='col-sm-9'),
 | 
				
			||||||
 | 
					                                    Field(f'meal_{meal.id}_gift', wrapper_class='col-sm-3')),
 | 
				
			||||||
 | 
					                                Row(Field(f'meal_{meal.id}_remark', wrapper_class='col-sm-9'),
 | 
				
			||||||
 | 
					                                    Field(f'meal_{meal.id}_priority', wrapper_class='col-sm-3')))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for food in meal.content.filter(available=True).all():
 | 
				
			||||||
 | 
					                if food.foodoption_set.count():
 | 
				
			||||||
 | 
					                    options_fieldset = Fieldset(_("Options for ") + str(food))
 | 
				
			||||||
 | 
					                    options_row = Row(css_class='ml-0')
 | 
				
			||||||
 | 
					                    for option in food.foodoption_set.filter(available=True).all():
 | 
				
			||||||
 | 
					                        form.fields[f'meal_{meal.id}_food_{food.id}_option_{option.id}'] = forms.BooleanField(
 | 
				
			||||||
 | 
					                            label=f"{option}{f' ({pretty_money(option.extra_cost)})' if option.extra_cost else ''}",
 | 
				
			||||||
 | 
					                            required=False,
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                        options_row.fields.append(
 | 
				
			||||||
 | 
					                            Field(f'meal_{meal.id}_food_{food.id}_option_{option.id}', wrapper_class='col-sm-12'))
 | 
				
			||||||
 | 
					                    options_fieldset.fields.append(options_row)
 | 
				
			||||||
 | 
					                    ag.fields.append(options_fieldset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            layout_fields.append(ag)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for food in self.object.food_set.filter(available=True).all():
 | 
				
			||||||
 | 
					            form.fields[f'food_{food.id}_quantity'] = forms.IntegerField(
 | 
				
			||||||
 | 
					                label=_("quantity").capitalize(),
 | 
				
			||||||
 | 
					                initial=0,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            form.fields[f'food_{food.id}_gift'] = forms.IntegerField(
 | 
				
			||||||
 | 
					                label=_("gift").capitalize(),
 | 
				
			||||||
 | 
					                initial=0,
 | 
				
			||||||
 | 
					                widget=AmountInput(),
 | 
				
			||||||
 | 
					                help_text=_("Be careful: this gift will be multiplied for each order."),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            form.fields[f'food_{food.id}_remark'] = forms.CharField(
 | 
				
			||||||
 | 
					                max_length=255,
 | 
				
			||||||
 | 
					                required=False,
 | 
				
			||||||
 | 
					                label=_("remark").capitalize(),
 | 
				
			||||||
 | 
					                help_text=_("Allergies,…"),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            form.fields[f'food_{food.id}_priority'] = forms.CharField(
 | 
				
			||||||
 | 
					                max_length=255,
 | 
				
			||||||
 | 
					                required=False,
 | 
				
			||||||
 | 
					                label=_("priority request").capitalize(),
 | 
				
			||||||
 | 
					                help_text=_("Lesson at 13h30,…"),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ag = AccordionGroup(f"{food} ({pretty_money(food.price)})",
 | 
				
			||||||
 | 
					                                Row(Field(f'food_{food.id}_quantity', wrapper_class='col-sm-9'),
 | 
				
			||||||
 | 
					                                    Field(f'food_{food.id}_gift', wrapper_class='col-sm-3')),
 | 
				
			||||||
 | 
					                                Row(Field(f'food_{food.id}_remark', wrapper_class='col-sm-9'),
 | 
				
			||||||
 | 
					                                    Field(f'food_{food.id}_priority', wrapper_class='col-sm-3')))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if food.foodoption_set.count():
 | 
				
			||||||
 | 
					                options_fieldset = Fieldset(_("Options"))
 | 
				
			||||||
 | 
					                options_row = Row(css_class='ml-0')
 | 
				
			||||||
 | 
					                for option in food.foodoption_set.all():
 | 
				
			||||||
 | 
					                    form.fields[f'food_{food.id}_option_{option.id}'] = forms.BooleanField(
 | 
				
			||||||
 | 
					                        label=f"{option}{f' ({pretty_money(option.extra_cost)})' if option.extra_cost else ''}",
 | 
				
			||||||
 | 
					                        required=False,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    options_row.fields.append(Field(f'food_{food.id}_option_{option.id}', wrapper_class='col-sm-12'))
 | 
				
			||||||
 | 
					                options_fieldset.fields.append(options_row)
 | 
				
			||||||
 | 
					                ag.fields.append(options_fieldset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            layout_fields.append(ag)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        layout_fields.append(FormActions(Submit('submit', _("Order now"))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        form.helper.layout = Accordion(*layout_fields)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.request.method in ['PUT', 'POST']:
 | 
				
			||||||
 | 
					            form.data = self.request.POST
 | 
				
			||||||
 | 
					            form.files = self.request.FILES
 | 
				
			||||||
 | 
					            form.is_bound = not form.data or not form.files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def form_valid(self, form):
 | 
				
			||||||
 | 
					        data = form.cleaned_data
 | 
				
			||||||
 | 
					        sheet = self.get_object()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with transaction.atomic():
 | 
				
			||||||
 | 
					            order = Order.objects.create(sheet_id=self.kwargs['pk'], note=data['note'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            total_quantity = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for meal in sheet.meal_set.filter(available=True).all():
 | 
				
			||||||
 | 
					                quantity = data[f'meal_{meal.id}_quantity']
 | 
				
			||||||
 | 
					                if not quantity:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                total_quantity += quantity
 | 
				
			||||||
 | 
					                gift = data[f'meal_{meal.id}_gift']
 | 
				
			||||||
 | 
					                remark = data[f'meal_{meal.id}_remark'] or ''
 | 
				
			||||||
 | 
					                priority = data[f'meal_{meal.id}_priority'] or ''
 | 
				
			||||||
 | 
					                ordered_meal = OrderedMeal.objects.create(order=order, meal=meal, gift=gift)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for ignored in range(quantity):
 | 
				
			||||||
 | 
					                    for food in meal.content.filter(available=True).all():
 | 
				
			||||||
 | 
					                        n = OrderedFood.objects.filter(order__sheet_id=self.kwargs['pk'],
 | 
				
			||||||
 | 
					                                                       order__note=order.note,
 | 
				
			||||||
 | 
					                                                       order__date__gte=timezone.now() - timedelta(hours=6),
 | 
				
			||||||
 | 
					                                                       food=food).exclude(status='CANCELED').count()
 | 
				
			||||||
 | 
					                        of = OrderedFood.objects.create(order=order, meal=ordered_meal, food=food,
 | 
				
			||||||
 | 
					                                                        remark=remark, priority=priority, number=n + 1, gift=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        for option in food.foodoption_set.filter(available=True).all():
 | 
				
			||||||
 | 
					                            if data[f'meal_{meal.id}_food_{food.id}_option_{option.id}']:
 | 
				
			||||||
 | 
					                                of.options.add(option)
 | 
				
			||||||
 | 
					                        of.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                first_food = ordered_meal.orderedfood_set.first()
 | 
				
			||||||
 | 
					                tr = SheetOrderTransaction(source_id=order.note_id, destination=first_food.food.club.note,
 | 
				
			||||||
 | 
					                                           source_alias=str(order.note), destination_alias=first_food.food.club.name,
 | 
				
			||||||
 | 
					                                           quantity=quantity, ordered_food=first_food,
 | 
				
			||||||
 | 
					                                           reason=f"{meal.name} - {sheet.name}")
 | 
				
			||||||
 | 
					                tr.amount = tr.get_price / tr.quantity
 | 
				
			||||||
 | 
					                tr.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for food in sheet.food_set.filter(available=True).all():
 | 
				
			||||||
 | 
					                quantity = data[f'food_{food.id}_quantity']
 | 
				
			||||||
 | 
					                if not quantity:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                total_quantity += quantity
 | 
				
			||||||
 | 
					                gift = data[f'food_{meal.id}_gift']
 | 
				
			||||||
 | 
					                remark = data[f'food_{meal.id}_remark'] or ''
 | 
				
			||||||
 | 
					                priority = data[f'food_{meal.id}_priority'] or ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for ignored in range(quantity):
 | 
				
			||||||
 | 
					                    n = OrderedFood.objects.filter(order__sheet_id=self.kwargs['pk'],
 | 
				
			||||||
 | 
					                                                   order__note=order.note,
 | 
				
			||||||
 | 
					                                                   order__date__gte=timezone.now() - timedelta(hours=6),
 | 
				
			||||||
 | 
					                                                   food=food).exclude(state='CANCELED').count()
 | 
				
			||||||
 | 
					                    of = OrderedFood.objects.create(order=order, food=food, gift=gift,
 | 
				
			||||||
 | 
					                                                    remark=remark, priority=priority, number=n + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    for option in food.foodoption_set.filter(available=True).all():
 | 
				
			||||||
 | 
					                        if data[f'meal_{meal.id}_food_{food.id}_option_{option.id}']:
 | 
				
			||||||
 | 
					                            of.options.add(option)
 | 
				
			||||||
 | 
					                    of.options.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    tr = SheetOrderTransaction(source_id=order.note_id, destination_id=first_food.club.note,
 | 
				
			||||||
 | 
					                                               source_alias=str(order.note), destination_alias=first_food.club.name,
 | 
				
			||||||
 | 
					                                               quantity=quantity, ordered_food=of,
 | 
				
			||||||
 | 
					                                               reason=f"{food.name} - {sheet.name}")
 | 
				
			||||||
 | 
					                    tr.amount = tr.get_price / tr.quantity
 | 
				
			||||||
 | 
					                    tr.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if total_quantity == 0:
 | 
				
			||||||
 | 
					            form.add_error(None, _("You didn't select anything."))
 | 
				
			||||||
 | 
					            transaction.rollback()
 | 
				
			||||||
 | 
					            return self.form_invalid(form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return super().form_valid(form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_success_url(self):
 | 
				
			||||||
 | 
					        return reverse_lazy('sheets:sheet_detail', args=(self.kwargs['pk'],))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WaitingListView(ProtectQuerysetMixin, DetailView):
 | 
				
			||||||
 | 
					    model = Food
 | 
				
			||||||
 | 
					    template_name = 'sheets/waiting_list.html'
 | 
				
			||||||
 | 
					    extra_context = {'title': _("Waiting list")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
 | 
					        content = super().get_context_data(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        content['queue'] = OrderedFood.objects.filter(food_id=self.kwargs['pk'], status='QUEUED')\
 | 
				
			||||||
 | 
					            .order_by('-priority', 'number', 'order__date').all()
 | 
				
			||||||
 | 
					        content['ready'] = OrderedFood.objects.filter(food_id=self.kwargs['pk'], status='READY')\
 | 
				
			||||||
 | 
					            .order_by('served_date').all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return content
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WaitingListDetailView(ProtectQuerysetMixin, DetailView):
 | 
				
			||||||
 | 
					    model = Food
 | 
				
			||||||
 | 
					    template_name = 'sheets/waiting_list_detail.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
 | 
					        context = super().get_context_data(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        list_type = 'CANCELED' if 'canceled' in self.request.path else \
 | 
				
			||||||
 | 
					            'SERVED' if 'served' in self.request.path else \
 | 
				
			||||||
 | 
					            'READY' if 'ready' in self.request.path else 'QUEUED'
 | 
				
			||||||
 | 
					        context['list_type'] = list_type
 | 
				
			||||||
 | 
					        context['orders'] = OrderedFood.objects.filter(food_id=self.kwargs['pk'], status=list_type)\
 | 
				
			||||||
 | 
					            .order_by('served_date', '-priority', 'number', 'order__date').all()
 | 
				
			||||||
 | 
					        context['title'] = self.object.name + " - " + _(list_type.lower()).capitalize()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
@@ -7,7 +7,7 @@ msgid ""
 | 
				
			|||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Project-Id-Version: \n"
 | 
					"Project-Id-Version: \n"
 | 
				
			||||||
"Report-Msgid-Bugs-To: \n"
 | 
					"Report-Msgid-Bugs-To: \n"
 | 
				
			||||||
"POT-Creation-Date: 2022-04-10 22:34+0200\n"
 | 
					"POT-Creation-Date: 2022-08-18 22:51+0200\n"
 | 
				
			||||||
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
 | 
					"PO-Revision-Date: 2022-04-11 22:05+0200\n"
 | 
				
			||||||
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
 | 
					"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
 | 
				
			||||||
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
 | 
					"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
 | 
				
			||||||
@@ -60,6 +60,7 @@ msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité."
 | 
				
			|||||||
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:301
 | 
					#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:301
 | 
				
			||||||
#: apps/permission/models.py:330
 | 
					#: apps/permission/models.py:330
 | 
				
			||||||
#: apps/registration/templates/registration/future_profile_detail.html:16
 | 
					#: apps/registration/templates/registration/future_profile_detail.html:16
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:16 apps/sheets/models.py:84 apps/sheets/models.py:121
 | 
				
			||||||
#: apps/wei/models.py:67 apps/wei/models.py:131 apps/wei/tables.py:282
 | 
					#: apps/wei/models.py:67 apps/wei/models.py:131 apps/wei/tables.py:282
 | 
				
			||||||
#: apps/wei/templates/wei/base.html:26
 | 
					#: apps/wei/templates/wei/base.html:26
 | 
				
			||||||
#: apps/wei/templates/wei/weimembership_form.html:14
 | 
					#: apps/wei/templates/wei/weimembership_form.html:14
 | 
				
			||||||
@@ -95,7 +96,8 @@ msgstr "types d'activité"
 | 
				
			|||||||
#: apps/activity/models.py:68
 | 
					#: apps/activity/models.py:68
 | 
				
			||||||
#: apps/activity/templates/activity/includes/activity_info.html:19
 | 
					#: apps/activity/templates/activity/includes/activity_info.html:19
 | 
				
			||||||
#: apps/note/models/transactions.py:81 apps/permission/models.py:110
 | 
					#: apps/note/models/transactions.py:81 apps/permission/models.py:110
 | 
				
			||||||
#: apps/permission/models.py:189 apps/wei/models.py:78 apps/wei/models.py:142
 | 
					#: apps/permission/models.py:189 apps/sheets/models.py:25 apps/wei/models.py:78
 | 
				
			||||||
 | 
					#: apps/wei/models.py:142
 | 
				
			||||||
msgid "description"
 | 
					msgid "description"
 | 
				
			||||||
msgstr "description"
 | 
					msgstr "description"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -143,6 +145,7 @@ msgstr ""
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#: apps/activity/models.py:109
 | 
					#: apps/activity/models.py:109
 | 
				
			||||||
#: apps/activity/templates/activity/includes/activity_info.html:25
 | 
					#: apps/activity/templates/activity/includes/activity_info.html:25
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:20
 | 
				
			||||||
msgid "start date"
 | 
					msgid "start date"
 | 
				
			||||||
msgstr "date de début"
 | 
					msgstr "date de début"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -171,7 +174,7 @@ msgid "entry time"
 | 
				
			|||||||
msgstr "heure d'entrée"
 | 
					msgstr "heure d'entrée"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/activity/models.py:178 apps/note/apps.py:14
 | 
					#: apps/activity/models.py:178 apps/note/apps.py:14
 | 
				
			||||||
#: apps/note/models/notes.py:77
 | 
					#: apps/note/models/notes.py:77 apps/sheets/models.py:157
 | 
				
			||||||
msgid "note"
 | 
					msgid "note"
 | 
				
			||||||
msgstr "note"
 | 
					msgstr "note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -334,7 +337,10 @@ msgstr "Entrée effectuée !"
 | 
				
			|||||||
#: apps/member/templates/member/add_members.html:46
 | 
					#: apps/member/templates/member/add_members.html:46
 | 
				
			||||||
#: apps/member/templates/member/club_form.html:16
 | 
					#: apps/member/templates/member/club_form.html:16
 | 
				
			||||||
#: apps/note/templates/note/transactiontemplate_form.html:18
 | 
					#: apps/note/templates/note/transactiontemplate_form.html:18
 | 
				
			||||||
#: apps/treasury/forms.py:89 apps/treasury/forms.py:143
 | 
					#: apps/sheets/templates/sheets/food_form.html:51
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/meal_form.html:17
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_form.html:17 apps/treasury/forms.py:89
 | 
				
			||||||
 | 
					#: apps/treasury/forms.py:143
 | 
				
			||||||
#: apps/treasury/templates/treasury/invoice_form.html:74
 | 
					#: apps/treasury/templates/treasury/invoice_form.html:74
 | 
				
			||||||
#: apps/wei/templates/wei/bus_form.html:17
 | 
					#: apps/wei/templates/wei/bus_form.html:17
 | 
				
			||||||
#: apps/wei/templates/wei/busteam_form.html:17
 | 
					#: apps/wei/templates/wei/busteam_form.html:17
 | 
				
			||||||
@@ -1470,7 +1476,7 @@ msgstr "modèles de transaction"
 | 
				
			|||||||
msgid "used alias"
 | 
					msgid "used alias"
 | 
				
			||||||
msgstr "alias utilisé"
 | 
					msgstr "alias utilisé"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/transactions.py:136
 | 
					#: apps/note/models/transactions.py:136 apps/sheets/views.py:277
 | 
				
			||||||
msgid "quantity"
 | 
					msgid "quantity"
 | 
				
			||||||
msgstr "quantité"
 | 
					msgstr "quantité"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1592,8 +1598,10 @@ msgid "Delete"
 | 
				
			|||||||
msgstr "Supprimer"
 | 
					msgstr "Supprimer"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/tables.py:222 apps/note/templates/note/conso_form.html:132
 | 
					#: apps/note/tables.py:222 apps/note/templates/note/conso_form.html:132
 | 
				
			||||||
#: apps/wei/tables.py:49 apps/wei/tables.py:50
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:21
 | 
				
			||||||
#: apps/wei/templates/wei/base.html:89
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:36
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:52 apps/wei/tables.py:49
 | 
				
			||||||
 | 
					#: apps/wei/tables.py:50 apps/wei/templates/wei/base.html:89
 | 
				
			||||||
#: apps/wei/templates/wei/bus_detail.html:20
 | 
					#: apps/wei/templates/wei/bus_detail.html:20
 | 
				
			||||||
#: apps/wei/templates/wei/busteam_detail.html:20
 | 
					#: apps/wei/templates/wei/busteam_detail.html:20
 | 
				
			||||||
#: apps/wei/templates/wei/busteam_detail.html:40
 | 
					#: apps/wei/templates/wei/busteam_detail.html:40
 | 
				
			||||||
@@ -1746,6 +1754,98 @@ msgstr "Bouton affiché"
 | 
				
			|||||||
msgid "An error occured"
 | 
					msgid "An error occured"
 | 
				
			||||||
msgstr "Une erreur s'est produite"
 | 
					msgstr "Une erreur s'est produite"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list.html:14 apps/sheets/models.py:244
 | 
				
			||||||
 | 
					msgid "queued"
 | 
				
			||||||
 | 
					msgstr "en attente"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list.html:27
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:67
 | 
				
			||||||
 | 
					msgid "There is no queued order."
 | 
				
			||||||
 | 
					msgstr "Il n'y a pas de commande en attente."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list.html:35 apps/sheets/models.py:245
 | 
				
			||||||
 | 
					msgid "ready"
 | 
				
			||||||
 | 
					msgstr "prêt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list.html:43
 | 
				
			||||||
 | 
					msgid "There is no ready order."
 | 
				
			||||||
 | 
					msgstr "Il n'y a pas de commande prête."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list.html:53
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:75
 | 
				
			||||||
 | 
					msgid "Other waiting lists:"
 | 
				
			||||||
 | 
					msgstr "Autres listes d'attente :"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list.html:66
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:93
 | 
				
			||||||
 | 
					msgid "Queued orders"
 | 
				
			||||||
 | 
					msgstr "Commandes en attente"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list.html:69
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:98
 | 
				
			||||||
 | 
					msgid "Ready orders"
 | 
				
			||||||
 | 
					msgstr "Commandes prêtes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list.html:72
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:115
 | 
				
			||||||
 | 
					msgid "Back to note sheet detail"
 | 
				
			||||||
 | 
					msgstr "Retour aux détails de la feuille de note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:18
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:161
 | 
				
			||||||
 | 
					msgid "date"
 | 
				
			||||||
 | 
					msgstr "date"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:22
 | 
				
			||||||
 | 
					msgid "order number"
 | 
				
			||||||
 | 
					msgstr "nombre de commandes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:27
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:229 apps/sheets/views.py:249 apps/sheets/views.py:295
 | 
				
			||||||
 | 
					msgid "priority request"
 | 
				
			||||||
 | 
					msgstr "demande de priorité"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:32
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:222 apps/sheets/views.py:243 apps/sheets/views.py:289
 | 
				
			||||||
 | 
					msgid "remark"
 | 
				
			||||||
 | 
					msgstr "remarques"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:37
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:216
 | 
				
			||||||
 | 
					msgid "options"
 | 
				
			||||||
 | 
					msgstr "options"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:45
 | 
				
			||||||
 | 
					msgid "Mark as ready"
 | 
				
			||||||
 | 
					msgstr "Marquer comme prêt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:50
 | 
				
			||||||
 | 
					msgid "Mark as served"
 | 
				
			||||||
 | 
					msgstr "Marquer comme servi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:55
 | 
				
			||||||
 | 
					msgid "Re-queue"
 | 
				
			||||||
 | 
					msgstr "Remettre en attente"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:60
 | 
				
			||||||
 | 
					#: note_kfet/templates/oauth2_provider/application_confirm_delete.html:17
 | 
				
			||||||
 | 
					#: note_kfet/templates/oauth2_provider/authorize.html:28
 | 
				
			||||||
 | 
					msgid "Cancel"
 | 
				
			||||||
 | 
					msgstr "Annuler"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:103
 | 
				
			||||||
 | 
					msgid "Served orders"
 | 
				
			||||||
 | 
					msgstr "Commandes servies"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:108
 | 
				
			||||||
 | 
					msgid "Canceled orders"
 | 
				
			||||||
 | 
					msgstr "Commandes annulées"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/note/templates/sheets/waiting_list_detail.html:112
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:47 apps/sheets/views.py:416
 | 
				
			||||||
 | 
					msgid "Waiting list"
 | 
				
			||||||
 | 
					msgstr "Liste d'attente"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/views.py:36
 | 
					#: apps/note/views.py:36
 | 
				
			||||||
msgid "Transfer money"
 | 
					msgid "Transfer money"
 | 
				
			||||||
msgstr "Transférer de l'argent"
 | 
					msgstr "Transférer de l'argent"
 | 
				
			||||||
@@ -2167,6 +2267,237 @@ msgstr ""
 | 
				
			|||||||
msgid "Invalidate pre-registration"
 | 
					msgid "Invalidate pre-registration"
 | 
				
			||||||
msgstr "Invalider l'inscription"
 | 
					msgstr "Invalider l'inscription"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/apps.py:10 apps/sheets/models.py:42
 | 
				
			||||||
 | 
					msgid "note sheets"
 | 
				
			||||||
 | 
					msgstr "feuilles de notes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:30
 | 
				
			||||||
 | 
					msgid "visible"
 | 
				
			||||||
 | 
					msgstr "visible"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:31
 | 
				
			||||||
 | 
					msgid "the note sheet will be private until this field is checked."
 | 
				
			||||||
 | 
					msgstr "la feuille de note restera privée tant que ce champ n'est pas coché."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:41 apps/sheets/models.py:54 apps/sheets/models.py:116
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:151 apps/sheets/models.py:273
 | 
				
			||||||
 | 
					msgid "note sheet"
 | 
				
			||||||
 | 
					msgstr "feuille de note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:48 apps/sheets/models.py:77 apps/sheets/models.py:78
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:90 apps/sheets/models.py:210
 | 
				
			||||||
 | 
					msgid "food"
 | 
				
			||||||
 | 
					msgstr "nourriture"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:58 apps/sheets/models.py:130
 | 
				
			||||||
 | 
					msgid "price"
 | 
				
			||||||
 | 
					msgstr "prix"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:64
 | 
				
			||||||
 | 
					msgid "destination club"
 | 
				
			||||||
 | 
					msgstr "club de destination"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:69 apps/sheets/models.py:100 apps/sheets/models.py:135
 | 
				
			||||||
 | 
					msgid "available"
 | 
				
			||||||
 | 
					msgstr "disponible"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:70 apps/sheets/models.py:101 apps/sheets/models.py:136
 | 
				
			||||||
 | 
					msgid "If set to false, this option won't be offered (in case of out of stock)"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					"Si mis à faux, cette option ne sera pas présentée (en cas de rupture de "
 | 
				
			||||||
 | 
					"stock)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:95
 | 
				
			||||||
 | 
					msgid "extra cost"
 | 
				
			||||||
 | 
					msgstr "surcoût"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:108
 | 
				
			||||||
 | 
					msgid "food option"
 | 
				
			||||||
 | 
					msgstr "option de nourriture"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:109
 | 
				
			||||||
 | 
					msgid "food options"
 | 
				
			||||||
 | 
					msgstr "options de nourriture"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:126
 | 
				
			||||||
 | 
					msgid "content"
 | 
				
			||||||
 | 
					msgstr "contenu"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:140 apps/sheets/models.py:143
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:180
 | 
				
			||||||
 | 
					msgid "meal"
 | 
				
			||||||
 | 
					msgstr "menu"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:144
 | 
				
			||||||
 | 
					msgid "meals"
 | 
				
			||||||
 | 
					msgstr "menus"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:166 apps/sheets/models.py:174
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:196
 | 
				
			||||||
 | 
					msgid "order"
 | 
				
			||||||
 | 
					msgstr "commande"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:167
 | 
				
			||||||
 | 
					msgid "orders"
 | 
				
			||||||
 | 
					msgstr "commandes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:184 apps/sheets/models.py:233 apps/sheets/views.py:235
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:281
 | 
				
			||||||
 | 
					msgid "gift"
 | 
				
			||||||
 | 
					msgstr "don"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:188 apps/sheets/models.py:204
 | 
				
			||||||
 | 
					msgid "ordered meal"
 | 
				
			||||||
 | 
					msgstr "menu commandé"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:189
 | 
				
			||||||
 | 
					msgid "ordered meals"
 | 
				
			||||||
 | 
					msgstr "menus commandés"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:237
 | 
				
			||||||
 | 
					msgid "number"
 | 
				
			||||||
 | 
					msgstr "numéro"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:238
 | 
				
			||||||
 | 
					msgid "How many times the user ordered this."
 | 
				
			||||||
 | 
					msgstr "Combien de fois cet⋅te utilisateur⋅rice a commandé ceci."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:246
 | 
				
			||||||
 | 
					msgid "served"
 | 
				
			||||||
 | 
					msgstr "servi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:247
 | 
				
			||||||
 | 
					msgid "canceled"
 | 
				
			||||||
 | 
					msgstr "annulé"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:250
 | 
				
			||||||
 | 
					msgid "status"
 | 
				
			||||||
 | 
					msgstr "statut"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:256
 | 
				
			||||||
 | 
					msgid "served date"
 | 
				
			||||||
 | 
					msgstr "date de service"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:260 apps/sheets/models.py:261
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:268
 | 
				
			||||||
 | 
					msgid "ordered food"
 | 
				
			||||||
 | 
					msgstr "nourriture commandée"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:288
 | 
				
			||||||
 | 
					msgid "sheet order transaction"
 | 
				
			||||||
 | 
					msgstr "transaction de commande sur feuille de note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/models.py:289
 | 
				
			||||||
 | 
					msgid "sheet order transactions"
 | 
				
			||||||
 | 
					msgstr "transactions de commande sur feuille de note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/food_form.html:48
 | 
				
			||||||
 | 
					msgid "Add option"
 | 
				
			||||||
 | 
					msgstr "Ajouter une option"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:28
 | 
				
			||||||
 | 
					msgid "menu"
 | 
				
			||||||
 | 
					msgstr "menu"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:31
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:43
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:58
 | 
				
			||||||
 | 
					msgid "This product is unavailable."
 | 
				
			||||||
 | 
					msgstr "Ce produit est indisponible."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:67
 | 
				
			||||||
 | 
					msgid "The menu is empty for now."
 | 
				
			||||||
 | 
					msgstr "Le menu est vide pour le moment."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:74
 | 
				
			||||||
 | 
					msgid "Add new food"
 | 
				
			||||||
 | 
					msgstr "Ajouter un plat"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:77
 | 
				
			||||||
 | 
					msgid "Add new meal"
 | 
				
			||||||
 | 
					msgstr "Ajouter un menu"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_detail.html:83 apps/sheets/views.py:206
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:319
 | 
				
			||||||
 | 
					msgid "Order now"
 | 
				
			||||||
 | 
					msgstr "Commander maintenant"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_list.html:14
 | 
				
			||||||
 | 
					msgid "Create a sheet"
 | 
				
			||||||
 | 
					msgstr "Créer une feuille de note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/templates/sheets/sheet_list.html:22
 | 
				
			||||||
 | 
					msgid "Note sheet listing"
 | 
				
			||||||
 | 
					msgstr "Liste des feuilles de notes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:33
 | 
				
			||||||
 | 
					msgid "Search note sheet"
 | 
				
			||||||
 | 
					msgstr "Chercher une feuille de note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:48
 | 
				
			||||||
 | 
					msgid "Create note sheet"
 | 
				
			||||||
 | 
					msgstr "Créer une feuille de note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:61
 | 
				
			||||||
 | 
					msgid "Update note sheet"
 | 
				
			||||||
 | 
					msgstr "Modifier une feuille de note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:84
 | 
				
			||||||
 | 
					msgid "Create new food"
 | 
				
			||||||
 | 
					msgstr "Créer un plat"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:130
 | 
				
			||||||
 | 
					msgid "Update food"
 | 
				
			||||||
 | 
					msgstr "Modifier un plat"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:167
 | 
				
			||||||
 | 
					msgid "Create new meal"
 | 
				
			||||||
 | 
					msgstr "Créer un menu"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:192
 | 
				
			||||||
 | 
					msgid "Update meal"
 | 
				
			||||||
 | 
					msgstr "Modifier un menu"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:217
 | 
				
			||||||
 | 
					msgid "Orderer"
 | 
				
			||||||
 | 
					msgstr "Commanditaire"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:223
 | 
				
			||||||
 | 
					msgid "Who orders"
 | 
				
			||||||
 | 
					msgstr "Qui commande"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:231 apps/treasury/models.py:140
 | 
				
			||||||
 | 
					msgid "Quantity"
 | 
				
			||||||
 | 
					msgstr "Quantité"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:238 apps/sheets/views.py:284
 | 
				
			||||||
 | 
					msgid "Be careful: this gift will be multiplied for each order."
 | 
				
			||||||
 | 
					msgstr "Attention : ce don sera multiplié pour chaque commande."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:244 apps/sheets/views.py:290
 | 
				
			||||||
 | 
					msgid "Allergies,…"
 | 
				
			||||||
 | 
					msgstr "Allergies,…"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:250 apps/sheets/views.py:296
 | 
				
			||||||
 | 
					msgid "Lesson at 13h30,…"
 | 
				
			||||||
 | 
					msgstr "Cours à 13h30,…"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:261
 | 
				
			||||||
 | 
					msgid "Options for "
 | 
				
			||||||
 | 
					msgstr "Options pour "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:306
 | 
				
			||||||
 | 
					msgid "Options"
 | 
				
			||||||
 | 
					msgstr "Options"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:403
 | 
				
			||||||
 | 
					msgid "You didn't select anything."
 | 
				
			||||||
 | 
					msgstr "Vous n'avez rien sélectionné."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/sheets/views.py:432
 | 
				
			||||||
 | 
					msgid "Detailed waiting list"
 | 
				
			||||||
 | 
					msgstr "Liste d'attente détaillée"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/treasury/apps.py:12 note_kfet/templates/base.html:96
 | 
					#: apps/treasury/apps.py:12 note_kfet/templates/base.html:96
 | 
				
			||||||
msgid "Treasury"
 | 
					msgid "Treasury"
 | 
				
			||||||
msgstr "Trésorerie"
 | 
					msgstr "Trésorerie"
 | 
				
			||||||
@@ -2253,10 +2584,6 @@ msgstr "Facture n°{id}"
 | 
				
			|||||||
msgid "Designation"
 | 
					msgid "Designation"
 | 
				
			||||||
msgstr "Désignation"
 | 
					msgstr "Désignation"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/treasury/models.py:140
 | 
					 | 
				
			||||||
msgid "Quantity"
 | 
					 | 
				
			||||||
msgstr "Quantité"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: apps/treasury/models.py:145
 | 
					#: apps/treasury/models.py:145
 | 
				
			||||||
msgid "Unit price"
 | 
					msgid "Unit price"
 | 
				
			||||||
msgstr "Prix unitaire"
 | 
					msgstr "Prix unitaire"
 | 
				
			||||||
@@ -3185,19 +3512,19 @@ msgstr "Répartir les 1A dans les bus"
 | 
				
			|||||||
msgid "Attribute bus"
 | 
					msgid "Attribute bus"
 | 
				
			||||||
msgstr "Attribuer un bus"
 | 
					msgstr "Attribuer un bus"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: note_kfet/settings/base.py:172
 | 
					#: note_kfet/settings/base.py:173
 | 
				
			||||||
msgid "German"
 | 
					msgid "German"
 | 
				
			||||||
msgstr "Allemand"
 | 
					msgstr "Allemand"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: note_kfet/settings/base.py:173
 | 
					#: note_kfet/settings/base.py:174
 | 
				
			||||||
msgid "English"
 | 
					msgid "English"
 | 
				
			||||||
msgstr "Anglais"
 | 
					msgstr "Anglais"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: note_kfet/settings/base.py:174
 | 
					#: note_kfet/settings/base.py:175
 | 
				
			||||||
msgid "Spanish"
 | 
					msgid "Spanish"
 | 
				
			||||||
msgstr "Espagnol"
 | 
					msgstr "Espagnol"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: note_kfet/settings/base.py:175
 | 
					#: note_kfet/settings/base.py:176
 | 
				
			||||||
msgid "French"
 | 
					msgid "French"
 | 
				
			||||||
msgstr "Français"
 | 
					msgstr "Français"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3347,11 +3674,6 @@ msgstr "Il n'y a pas de résultat."
 | 
				
			|||||||
msgid "Are you sure to delete the application"
 | 
					msgid "Are you sure to delete the application"
 | 
				
			||||||
msgstr "Êtes-vous sûr⋅e de vouloir supprimer l'application"
 | 
					msgstr "Êtes-vous sûr⋅e de vouloir supprimer l'application"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: note_kfet/templates/oauth2_provider/application_confirm_delete.html:17
 | 
					 | 
				
			||||||
#: note_kfet/templates/oauth2_provider/authorize.html:28
 | 
					 | 
				
			||||||
msgid "Cancel"
 | 
					 | 
				
			||||||
msgstr "Annuler"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: note_kfet/templates/oauth2_provider/application_detail.html:11
 | 
					#: note_kfet/templates/oauth2_provider/application_detail.html:11
 | 
				
			||||||
msgid "Client id"
 | 
					msgid "Client id"
 | 
				
			||||||
msgstr "ID client"
 | 
					msgstr "ID client"
 | 
				
			||||||
@@ -3562,3 +3884,6 @@ msgstr ""
 | 
				
			|||||||
"vous connecter. Vous devez vous rendre à la Kfet et payer les frais "
 | 
					"vous connecter. Vous devez vous rendre à la Kfet et payer les frais "
 | 
				
			||||||
"d'adhésion. Vous devez également valider votre adresse email en suivant le "
 | 
					"d'adhésion. Vous devez également valider votre adresse email en suivant le "
 | 
				
			||||||
"lien que vous avez reçu."
 | 
					"lien que vous avez reçu."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#~ msgid "Detailed view"
 | 
				
			||||||
 | 
					#~ msgstr "Vue détaillée"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,6 +75,7 @@ INSTALLED_APPS = [
 | 
				
			|||||||
    'permission',
 | 
					    'permission',
 | 
				
			||||||
    'registration',
 | 
					    'registration',
 | 
				
			||||||
    'scripts',
 | 
					    'scripts',
 | 
				
			||||||
 | 
					    'sheets',
 | 
				
			||||||
    'treasury',
 | 
					    'treasury',
 | 
				
			||||||
    'wei',
 | 
					    'wei',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -97,13 +97,16 @@ body {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.btn-primary:hover,
 | 
					.btn-primary:hover,
 | 
				
			||||||
.btn-primary:not(:disabled):not(.disabled).active,
 | 
					.btn-primary:not(:disabled):not(.disabled).active,
 | 
				
			||||||
.btn-primary:not(:disabled):not(.disabled):active {
 | 
					.btn-primary:not(:disabled):not(.disabled):active,
 | 
				
			||||||
 | 
					a.badge-primary:hover,
 | 
				
			||||||
 | 
					a.badge-primary:not(:disabled):not(.disabled).active,
 | 
				
			||||||
 | 
					a.badge-primary:not(:disabled):not(.disabled):active {
 | 
				
			||||||
    color: #fff;
 | 
					    color: #fff;
 | 
				
			||||||
    background-color: rgb(102, 83, 105);
 | 
					    background-color: rgb(102, 83, 105);
 | 
				
			||||||
    border-color: rgb(102, 83, 105);
 | 
					    border-color: rgb(102, 83, 105);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.btn-primary {
 | 
					.btn-primary, a.badge-primary {
 | 
				
			||||||
    color: rgba(248, 249, 250, 0.9); 
 | 
					    color: rgba(248, 249, 250, 0.9); 
 | 
				
			||||||
    background-color: rgb(102, 83, 105);
 | 
					    background-color: rgb(102, 83, 105);
 | 
				
			||||||
    border-color: rgb(102, 83, 105);
 | 
					    border-color: rgb(102, 83, 105);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ urlpatterns = [
 | 
				
			|||||||
    path('activity/', include('activity.urls')),
 | 
					    path('activity/', include('activity.urls')),
 | 
				
			||||||
    path('treasury/', include('treasury.urls')),
 | 
					    path('treasury/', include('treasury.urls')),
 | 
				
			||||||
    path('wei/', include('wei.urls')),
 | 
					    path('wei/', include('wei.urls')),
 | 
				
			||||||
 | 
					    path('sheets/', include('sheets.urls')),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Include Django Contrib and Core routers
 | 
					    # Include Django Contrib and Core routers
 | 
				
			||||||
    path('i18n/', include('django.conf.urls.i18n')),
 | 
					    path('i18n/', include('django.conf.urls.i18n')),
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user