mirror of
https://framagit.org/la-chariotte/la-chariotte.git
synced 2025-05-01 11:22:24 +02:00
Merge branch '186/mettre-le-lien-du-gitlab-dans-le-footer' of gitlab.com:la-chariotte/la_chariotte into 186/mettre-le-lien-du-gitlab-dans-le-footer
This commit is contained in:
commit
b03e67e737
25 changed files with 208 additions and 97 deletions
|
@ -7,6 +7,5 @@ La Chariotte is a web application to handle grouped orders.
|
||||||
- [Public server](https://chariotte.fr)
|
- [Public server](https://chariotte.fr)
|
||||||
- [Online documentation](https://docs.chariotte.fr)
|
- [Online documentation](https://docs.chariotte.fr)
|
||||||
|
|
||||||
This project has been initially developped and published by [Hashbang](https://
|
This project has been initially developed and published by [Hashbang](https://hashbang.fr/)
|
||||||
hashbang.fr/) under an [Affero GPLv3](LICENSE) license, and is now maintained
|
under an [Affero GPLv3](LICENSE) license, and is now maintained and developed by a team of volunteers.
|
||||||
and developed by a team of volunteers.
|
|
||||||
|
|
|
@ -60,3 +60,7 @@ build it locally, just run:
|
||||||
```bash
|
```bash
|
||||||
mkdocs serve
|
mkdocs serve
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Merging rules
|
||||||
|
|
||||||
|
In order to be merged, your code needs to be reviewed by two maintainers. Don't hesitate to ping us if needed.
|
||||||
|
|
|
@ -50,6 +50,61 @@ en/latest/), managed by AlwaysData.
|
||||||
The production settings are stored in `~/ la_chariotte/prod_settings.py`, and
|
The production settings are stored in `~/ la_chariotte/prod_settings.py`, and
|
||||||
the secrets are defined in the admin console.
|
the secrets are defined in the admin console.
|
||||||
|
|
||||||
|
Here are the settings, with some comments that might be useful.
|
||||||
|
|
||||||
|
```python title="prod_settings.py"
|
||||||
|
SECRET_KEY = "YOUR SECRET KEY HERE, used to hash the passwords. CHANGE IT."
|
||||||
|
|
||||||
|
# We're connecting to a psql server, AD manages the access and the backups.
|
||||||
|
DATABASES = {
|
||||||
|
"default": {
|
||||||
|
"ENGINE": "django.db.backends.postgresql",
|
||||||
|
"NAME": "chariotte_prod",
|
||||||
|
"USER": "chariotte_prod",
|
||||||
|
"PASSWORD": "",
|
||||||
|
"HOST": "host",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = ["chariotte.fr",]
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
# We're sending mails using AD infrastructure
|
||||||
|
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||||
|
EMAIL_FROM = 'notification@chariotte.fr'
|
||||||
|
EMAIL_HOST = 'smtp-chariotte.alwaysdata.net'
|
||||||
|
EMAIL_PORT = 587
|
||||||
|
EMAIL_HOST_USER = 'notification@chariotte.fr'
|
||||||
|
EMAIL_HOST_PASSWORD = "XXX"
|
||||||
|
EMAIL_USE_TLS = True
|
||||||
|
|
||||||
|
DEFAULT_FROM_EMAIL = os.getenv(
|
||||||
|
"DJANGO_DEFAULT_FROM_EMAIL", "La Chariotte <notification@chariotte.fr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
CONTACT_MAIL = "contact@chariotte.fr"
|
||||||
|
|
||||||
|
# We're collecting the static files on this specific folder.
|
||||||
|
STATIC_ROOT = "/home/chariotte/static/"
|
||||||
|
```
|
||||||
|
|
||||||
|
We're using sentry (sentry.io) to be alerted when an error happens on the server
|
||||||
|
|
||||||
|
```python title="prod_settings.py"
|
||||||
|
import sentry_sdk
|
||||||
|
|
||||||
|
sentry_sdk.init(
|
||||||
|
dsn="PUT YOUR DSN HERE",
|
||||||
|
# Set traces_sample_rate to 1.0 to capture 100%
|
||||||
|
# of transactions for performance monitoring.
|
||||||
|
traces_sample_rate=1.0,
|
||||||
|
# Set profiles_sample_rate to 1.0 to profile 100%
|
||||||
|
# of sampled transactions.
|
||||||
|
# We recommend adjusting this value in production.
|
||||||
|
profiles_sample_rate=1.0,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
### The different sites
|
### The different sites
|
||||||
|
|
||||||
In the AD console, here are the defined sites:
|
In the AD console, here are the defined sites:
|
||||||
|
|
|
@ -3,6 +3,8 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class CustomUser(AbstractUser):
|
class CustomUser(AbstractUser):
|
||||||
|
EMAIL_FIELD = "username"
|
||||||
|
|
||||||
username = models.EmailField(
|
username = models.EmailField(
|
||||||
"Email",
|
"Email",
|
||||||
unique=True,
|
unique=True,
|
||||||
|
|
|
@ -2,7 +2,6 @@ import pytest
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from la_chariotte import settings
|
from la_chariotte import settings
|
||||||
from la_chariotte.order.models import GroupedOrder
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ import html2text
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.html import strip_tags
|
|
||||||
|
|
||||||
|
|
||||||
def send_order_confirmation_mail(order):
|
def send_order_confirmation_mail(order):
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
import datetime
|
import datetime
|
||||||
from typing import Any, Optional, Sequence, Type, Union
|
|
||||||
|
|
||||||
import pytz
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
|
|
||||||
from django.db import IntegrityError
|
|
||||||
from django.forms.utils import to_current_timezone
|
from django.forms.utils import to_current_timezone
|
||||||
from django.forms.widgets import Widget
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from la_chariotte import settings
|
|
||||||
from la_chariotte.order.models import GroupedOrder, Item
|
from la_chariotte.order.models import GroupedOrder, Item
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,27 +5,52 @@ import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = []
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Grouped_order',
|
name="Grouped_order",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('orga', models.CharField(max_length=100)),
|
"id",
|
||||||
('date', models.DateField(verbose_name='Date de livraison')),
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("orga", models.CharField(max_length=100)),
|
||||||
|
("date", models.DateField(verbose_name="Date de livraison")),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Order',
|
name="Order",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('author', models.CharField(max_length=100, verbose_name='Personne qui passe la commande')),
|
"id",
|
||||||
('grouped_order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='order.grouped_order')),
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"author",
|
||||||
|
models.CharField(
|
||||||
|
max_length=100, verbose_name="Personne qui passe la commande"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"grouped_order",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="order.grouped_order",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,15 +4,14 @@ from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('order', '0001_initial'),
|
("order", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='grouped_order',
|
model_name="grouped_order",
|
||||||
name='name',
|
name="name",
|
||||||
field=models.CharField(max_length=100, null=True),
|
field=models.CharField(max_length=100, null=True),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,18 +5,31 @@ import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('order', '0002_grouped_order_name'),
|
("order", "0002_grouped_order_name"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Item',
|
name="Item",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=100)),
|
"id",
|
||||||
('grouped_order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='order.grouped_order')),
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=100)),
|
||||||
|
(
|
||||||
|
"grouped_order",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="order.grouped_order",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,15 +4,14 @@ from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('order', '0003_item'),
|
("order", "0003_item"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='item',
|
model_name="item",
|
||||||
name='ordered_nb',
|
name="ordered_nb",
|
||||||
field=models.IntegerField(default=0),
|
field=models.IntegerField(default=0),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,28 +5,53 @@ import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('order', '0004_item_ordered_nb'),
|
("order", "0004_item_ordered_nb"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(
|
||||||
model_name='item',
|
model_name="item",
|
||||||
name='ordered_nb',
|
name="ordered_nb",
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='order',
|
model_name="order",
|
||||||
name='grouped_order',
|
name="grouped_order",
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='order_set', to='order.grouped_order'),
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="order_set",
|
||||||
|
to="order.grouped_order",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='OrderedItem',
|
name="OrderedItem",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('nb', models.PositiveSmallIntegerField(default=0)),
|
"id",
|
||||||
('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ordered_items', to='order.item')),
|
models.BigAutoField(
|
||||||
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='order.order')),
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("nb", models.PositiveSmallIntegerField(default=0)),
|
||||||
|
(
|
||||||
|
"item",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="ordered_items",
|
||||||
|
to="order.item",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"order",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="orders",
|
||||||
|
to="order.order",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,15 +4,14 @@ from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('order', '0005_remove_item_ordered_nb_alter_order_grouped_order_and_more'),
|
("order", "0005_remove_item_ordered_nb_alter_order_grouped_order_and_more"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='item',
|
model_name="item",
|
||||||
name='ordered_nb',
|
name="ordered_nb",
|
||||||
field=models.IntegerField(default=0),
|
field=models.IntegerField(default=0),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,20 +5,27 @@ import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('order', '0006_item_ordered_nb'),
|
("order", "0006_item_ordered_nb"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='ordereditem',
|
model_name="ordereditem",
|
||||||
name='item',
|
name="item",
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='order.item'),
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="orders",
|
||||||
|
to="order.item",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='ordereditem',
|
model_name="ordereditem",
|
||||||
name='order',
|
name="order",
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ordered_items', to='order.order'),
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="ordered_items",
|
||||||
|
to="order.order",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,16 +5,20 @@ from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('order', '0007_alter_ordereditem_item_alter_ordereditem_order'),
|
("order", "0007_alter_ordereditem_item_alter_ordereditem_order"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='grouped_order',
|
model_name="grouped_order",
|
||||||
name='deadline',
|
name="deadline",
|
||||||
field=models.DateTimeField(default=datetime.datetime(2023, 3, 23, 14, 38, 17, 365192, tzinfo=datetime.timezone.utc), verbose_name='Date limite de commande'),
|
field=models.DateTimeField(
|
||||||
|
default=datetime.datetime(
|
||||||
|
2023, 3, 23, 14, 38, 17, 365192, tzinfo=datetime.timezone.utc
|
||||||
|
),
|
||||||
|
verbose_name="Date limite de commande",
|
||||||
|
),
|
||||||
preserve_default=False,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,15 +4,14 @@ from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('order', '0008_grouped_order_deadline'),
|
("order", "0008_grouped_order_deadline"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RenameField(
|
migrations.RenameField(
|
||||||
model_name='grouped_order',
|
model_name="grouped_order",
|
||||||
old_name='date',
|
old_name="date",
|
||||||
new_name='delivery_date',
|
new_name="delivery_date",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -14,9 +14,12 @@ def create_code_from_pk(pk):
|
||||||
The use of pk in the beginning of the string guarantees the uniqueness, and the random part makes that we cannot guess the url path.
|
The use of pk in the beginning of the string guarantees the uniqueness, and the random part makes that we cannot guess the url path.
|
||||||
"""
|
"""
|
||||||
base_36_pk = base36.dumps(pk)
|
base_36_pk = base36.dumps(pk)
|
||||||
random_string = base36.dumps(random.randint(1727605,60466175)) # generates a 5 digits long string
|
random_string = base36.dumps(
|
||||||
|
random.randint(1727605, 60466175)
|
||||||
|
) # generates a 5 digits long string
|
||||||
return f"{base_36_pk}{random_string}"
|
return f"{base_36_pk}{random_string}"
|
||||||
|
|
||||||
|
|
||||||
def set_code_default(apps, schema_editor):
|
def set_code_default(apps, schema_editor):
|
||||||
"""Provides a default code to existing grouped orders during migration"""
|
"""Provides a default code to existing grouped orders during migration"""
|
||||||
GroupedOrder = apps.get_model("order", "GroupedOrder")
|
GroupedOrder = apps.get_model("order", "GroupedOrder")
|
||||||
|
@ -24,11 +27,12 @@ def set_code_default(apps, schema_editor):
|
||||||
grouped_order.code = create_code_from_pk(grouped_order.pk)
|
grouped_order.code = create_code_from_pk(grouped_order.pk)
|
||||||
grouped_order.save()
|
grouped_order.save()
|
||||||
|
|
||||||
|
|
||||||
def reverse_set_code_default(apps, schema_editor):
|
def reverse_set_code_default(apps, schema_editor):
|
||||||
"""Reverse the set_code default function"""
|
"""Reverse the set_code default function"""
|
||||||
GroupedOrder = apps.get_model("order", "GroupedOrder")
|
GroupedOrder = apps.get_model("order", "GroupedOrder")
|
||||||
for grouped_order in GroupedOrder.objects.all().iterator():
|
for grouped_order in GroupedOrder.objects.all().iterator():
|
||||||
grouped_order.code = ''
|
grouped_order.code = ""
|
||||||
grouped_order.save()
|
grouped_order.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th style="font-size: 0.5em; width: 2em">OK</th>
|
||||||
<th style="text-align: center">Nom</th>
|
<th style="text-align: center">Nom</th>
|
||||||
{% for item in items %}
|
{% for item in items %}
|
||||||
<th class="item_name" style="font-weight: normal;">
|
<th class="item_name" style="font-weight: normal;">
|
||||||
|
@ -91,11 +92,11 @@
|
||||||
</th>
|
</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<th style="width: 2cm">Prix</th>
|
<th style="width: 2cm">Prix</th>
|
||||||
<th style="font-size: 0.5em; width: 2em">OK</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr style="background-color: #bababa">
|
<tr style="background-color: #bababa">
|
||||||
|
<td></td>
|
||||||
<td style="text-align: left">Prix unitaire</td>
|
<td style="text-align: left">Prix unitaire</td>
|
||||||
{% for item in items %}
|
{% for item in items %}
|
||||||
<td>
|
<td>
|
||||||
|
@ -103,9 +104,9 @@
|
||||||
</td>
|
</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="background-color: #bababa">
|
<tr style="background-color: #bababa">
|
||||||
|
<th></th>
|
||||||
<th style="text-align: left">TOTAL</th>
|
<th style="text-align: left">TOTAL</th>
|
||||||
{% for item in items %}
|
{% for item in items %}
|
||||||
<th>
|
<th>
|
||||||
|
@ -113,10 +114,10 @@
|
||||||
</th>
|
</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<th>{{ grouped_order.total_price }} €</th>
|
<th>{{ grouped_order.total_price }} €</th>
|
||||||
<th></th>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% for order, ordered_items in orders_dict.items %}
|
{% for order, ordered_items in orders_dict.items %}
|
||||||
<tr>
|
<tr>
|
||||||
|
<td></td>
|
||||||
<td>
|
<td>
|
||||||
{{ order.author.last_name|upper }} {{ order.author.first_name }}
|
{{ order.author.last_name|upper }} {{ order.author.first_name }}
|
||||||
</td>
|
</td>
|
||||||
|
@ -130,7 +131,6 @@
|
||||||
<td>
|
<td>
|
||||||
{{ order.price }} €
|
{{ order.price }} €
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import datetime
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
from la_chariotte.order import models
|
from la_chariotte.order import models
|
||||||
from la_chariotte.order.tests.utils import create_grouped_order
|
from la_chariotte.order.tests.utils import create_grouped_order
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import datetime
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
from la_chariotte.order import models
|
from la_chariotte.order import models
|
||||||
from la_chariotte.order.tests.utils import (
|
from la_chariotte.order.tests.utils import (
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.contrib import auth
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from la_chariotte.order import models
|
from la_chariotte.order import models
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from django.views.generic.base import TemplateView
|
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
import csv
|
import csv
|
||||||
import json
|
import json
|
||||||
from datetime import timedelta
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||||
from django.core.serializers.json import DjangoJSONEncoder
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.template.loader import get_template
|
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
from django_weasyprint import WeasyTemplateResponseMixin
|
from django_weasyprint import WeasyTemplateResponseMixin
|
||||||
from django_weasyprint.views import WeasyTemplateResponse, WeasyTemplateView
|
|
||||||
from icalendar import Calendar, Event, vCalAddress, vText
|
from icalendar import Calendar, Event, vCalAddress, vText
|
||||||
from xhtml2pdf import pisa
|
|
||||||
|
|
||||||
from ..forms import GroupedOrderForm, Item, JoinGroupedOrderForm
|
from ..forms import GroupedOrderForm, Item, JoinGroupedOrderForm
|
||||||
from ..models import GroupedOrder, OrderAuthor
|
from ..models import GroupedOrder, OrderAuthor
|
||||||
|
|
|
@ -19,7 +19,6 @@ from django.contrib.auth.views import (
|
||||||
PasswordResetCompleteView,
|
PasswordResetCompleteView,
|
||||||
PasswordResetConfirmView,
|
PasswordResetConfirmView,
|
||||||
PasswordResetDoneView,
|
PasswordResetDoneView,
|
||||||
PasswordResetView,
|
|
||||||
)
|
)
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
|
|
BIN
mails.sqlite
BIN
mails.sqlite
Binary file not shown.
Loading…
Reference in a new issue