mirror of https://gitlab.crans.org/bde/nk20
Compare commits
51 Commits
e9506fe133
...
da222c369a
Author | SHA1 | Date |
---|---|---|
charliep | da222c369a | |
korenstin | ae4213d087 | |
korenstin | 35ffbfcf55 | |
korenstin | b8f81048a5 | |
korenstin | af819f45a1 | |
korenstin | 076d065ffa | |
korenstin | 2da77d9c17 | |
korenstin | 01584d6330 | |
korenstin | 4c0a5922c4 | |
korenstin | f90b28fc7c | |
korenstin | 925e0f26f5 | |
quark | c912383f86 | |
quark | 32830e43fd | |
korenstin | 11c6a6fa7a | |
korenstin | 201d6b114a | |
korenstin | 19e77df299 | |
korenstin | 5fd6ec5668 | |
korenstin | 10a01c5bc2 | |
korenstin | 989905ea64 | |
korenstin | 0218d43a17 | |
test | 5d30b0e819 | |
korenstin | ec759dd3c0 | |
korenstin | 2eb965291d | |
quark | 7f182ee2ee | |
quark | 3132aa4c38 | |
quark | c7eb774859 | |
quark | 32f8d285b3 | |
quark | 050256ea13 | |
quark | 7afd15b1cc | |
korenstin | 258361f116 | |
korenstin | a307530579 | |
quark | 5de930bf40 | |
quark | f7ebe0e99b | |
quark | 73de6e2176 | |
test | 201611b105 | |
quark | 40c239e9da | |
quark | 2aaab2b454 | |
quark | fc088dec86 | |
korenstin | 2d60f1fd7b | |
test | 7b48b09329 | |
quark | ffac940511 | |
mcngnt | 50f98fd5ad | |
mcngnt | 402e19d1ce | |
korenstin | 0b0394b61f | |
test | 98422d8259 | |
quark | 29509b5b26 | |
quark | 0d64ad31e0 | |
quark | 5781cbd6a5 | |
quark | 5295e61a00 | |
quark | e79ed6226a | |
quark | 68152e6354 |
|
@ -8,19 +8,19 @@ variables:
|
|||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
|
||||
# Debian Buster
|
||||
py37-django22:
|
||||
stage: test
|
||||
image: debian:buster-backports
|
||||
before_script:
|
||||
- >
|
||||
apt-get update &&
|
||||
apt-get install --no-install-recommends -t buster-backports -y
|
||||
python3-django python3-django-crispy-forms
|
||||
python3-django-extensions python3-django-filters python3-django-polymorphic
|
||||
python3-djangorestframework python3-django-oauth-toolkit python3-psycopg2 python3-pil
|
||||
python3-babel python3-lockfile python3-pip python3-phonenumbers python3-memcache
|
||||
python3-bs4 python3-setuptools tox texlive-xetex
|
||||
script: tox -e py37-django22
|
||||
# py37-django22:
|
||||
# stage: test
|
||||
# image: debian:buster-backports
|
||||
# before_script:
|
||||
# - >
|
||||
# apt-get update &&
|
||||
# apt-get install --no-install-recommends -t buster-backports -y
|
||||
# python3-django python3-django-crispy-forms
|
||||
# python3-django-extensions python3-django-filters python3-django-polymorphic
|
||||
# python3-djangorestframework python3-django-oauth-toolkit python3-psycopg2 python3-pil
|
||||
# python3-babel python3-lockfile python3-pip python3-phonenumbers python3-memcache
|
||||
# python3-bs4 python3-setuptools tox texlive-xetex
|
||||
# script: tox -e py37-django22
|
||||
|
||||
# Ubuntu 20.04
|
||||
py38-django22:
|
||||
|
@ -56,7 +56,7 @@ py39-django22:
|
|||
|
||||
linters:
|
||||
stage: quality-assurance
|
||||
image: debian:buster-backports
|
||||
image: debian:bullseye
|
||||
before_script:
|
||||
- apt-get update && apt-get install -y tox
|
||||
script: tox -e linters
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.28 on 2024-03-23 13:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('activity', '0002_auto_20200904_2341'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='activity',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, default='', verbose_name='description'),
|
||||
),
|
||||
]
|
|
@ -66,6 +66,8 @@ class Activity(models.Model):
|
|||
|
||||
description = models.TextField(
|
||||
verbose_name=_('description'),
|
||||
blank=True,
|
||||
default="",
|
||||
)
|
||||
|
||||
location = models.CharField(
|
||||
|
|
|
@ -17,4 +17,27 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script>
|
||||
var date_end = document.getElementById("id_date_end");
|
||||
var date_start = document.getElementById("id_date_start");
|
||||
|
||||
function update_date_end (){
|
||||
if(date_end.value=="" || date_end.value<date_start.value){
|
||||
date_end.value = date_start.value;
|
||||
};
|
||||
};
|
||||
|
||||
function update_date_start (){
|
||||
if(date_start.value=="" || date_end.value<date_start.value){
|
||||
date_start.value = date_end.value;
|
||||
};
|
||||
};
|
||||
|
||||
date_start.addEventListener('focusout', update_date_end);
|
||||
date_end.addEventListener('focusout', update_date_start);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -46,4 +46,4 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
</h3>
|
||||
{% render_table table %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -17,7 +17,8 @@ from django.utils.translation import gettext_lazy as _
|
|||
from django.views import View
|
||||
from django.views.decorators.cache import cache_page
|
||||
from django.views.generic import DetailView, TemplateView, UpdateView
|
||||
from django_tables2.views import SingleTableView
|
||||
from django.views.generic.list import ListView
|
||||
from django_tables2.views import MultiTableMixin
|
||||
from note.models import Alias, NoteSpecial, NoteUser
|
||||
from permission.backends import PermissionBackend
|
||||
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
|
||||
|
@ -57,26 +58,40 @@ class ActivityCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||
return reverse_lazy('activity:activity_detail', kwargs={"pk": self.object.pk})
|
||||
|
||||
|
||||
class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
|
||||
class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView):
|
||||
"""
|
||||
Displays all Activities, and classify if they are on-going or upcoming ones.
|
||||
"""
|
||||
model = Activity
|
||||
table_class = ActivityTable
|
||||
ordering = ('-date_start',)
|
||||
tables = [ActivityTable, ActivityTable]
|
||||
extra_context = {"title": _("Activities")}
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
return super().get_queryset(**kwargs).distinct()
|
||||
|
||||
def get_tables(self):
|
||||
tables = super().get_tables()
|
||||
|
||||
tables[0].prefix = "all-"
|
||||
tables[1].prefix = "upcoming-"
|
||||
return tables
|
||||
|
||||
def get_tables_data(self):
|
||||
# first table = all activities, second table = upcoming
|
||||
return [
|
||||
self.get_queryset().order_by("-date_start"),
|
||||
Activity.objects.filter(date_end__gt=timezone.now())
|
||||
.filter(PermissionBackend.filter_queryset(self.request, Activity, "view"))
|
||||
.distinct()
|
||||
.order_by("date_start")
|
||||
]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
upcoming_activities = Activity.objects.filter(date_end__gt=timezone.now())
|
||||
context['upcoming'] = ActivityTable(
|
||||
data=upcoming_activities.filter(PermissionBackend.filter_queryset(self.request, Activity, "view")),
|
||||
prefix='upcoming-',
|
||||
)
|
||||
tables = context["tables"]
|
||||
for name, table in zip(["table", "upcoming"], tables):
|
||||
context[name] = table
|
||||
|
||||
started_activities = self.get_queryset().filter(open=True, valid=True).distinct().all()
|
||||
context["started_activities"] = started_activities
|
||||
|
|
|
@ -121,7 +121,7 @@ class ImageForm(forms.Form):
|
|||
frame = frame.crop((x, y, x + w, y + h))
|
||||
frame = frame.resize(
|
||||
(settings.PIC_WIDTH, settings.PIC_RATIO * settings.PIC_WIDTH),
|
||||
Image.ANTIALIAS,
|
||||
Image.LANCZOS,
|
||||
)
|
||||
frames.append(frame)
|
||||
|
||||
|
@ -138,6 +138,9 @@ class ImageForm(forms.Form):
|
|||
|
||||
return cleaned_data
|
||||
|
||||
def is_valid(self):
|
||||
return super().is_valid() or super().clean().get('image') is None
|
||||
|
||||
|
||||
class ClubForm(forms.ModelForm):
|
||||
def clean(self):
|
||||
|
@ -151,7 +154,7 @@ class ClubForm(forms.ModelForm):
|
|||
|
||||
class Meta:
|
||||
model = Club
|
||||
fields = '__all__'
|
||||
exclude = ("add_registration_form",)
|
||||
widgets = {
|
||||
"membership_fee_paid": AmountInput(),
|
||||
"membership_fee_unpaid": AmountInput(),
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.28 on 2024-07-15 09:24
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('member', '0011_profile_vss_charter_read'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='club',
|
||||
name='add_registration_form',
|
||||
field=models.BooleanField(default=False, verbose_name='add to registration form'),
|
||||
),
|
||||
]
|
|
@ -259,6 +259,11 @@ class Club(models.Model):
|
|||
help_text=_('Maximal date of a membership, after which members must renew it.'),
|
||||
)
|
||||
|
||||
add_registration_form = models.BooleanField(
|
||||
verbose_name=_("add to registration form"),
|
||||
default=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("club")
|
||||
verbose_name_plural = _("clubs")
|
||||
|
|
|
@ -14,6 +14,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
<form method="post" enctype="multipart/form-data" id="formUpload">
|
||||
{% csrf_token %}
|
||||
{{ form |crispy }}
|
||||
{% if user.note.display_image != "pic/default.png" %}
|
||||
<input type="submit" class="btn btn-primary" value="{% trans "Remove" %}">
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
<!-- MODAL TO CROP THE IMAGE -->
|
||||
|
|
|
@ -26,7 +26,7 @@ from permission.backends import PermissionBackend
|
|||
from permission.models import Role
|
||||
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
|
||||
|
||||
from .forms import UserForm, ProfileForm, ImageForm, ClubForm, MembershipForm,\
|
||||
from .forms import UserForm, ProfileForm, ImageForm, ClubForm, MembershipForm, \
|
||||
CustomAuthenticationForm, MembershipRolesForm
|
||||
from .models import Club, Membership
|
||||
from .tables import ClubTable, UserTable, MembershipTable, ClubManagerTable
|
||||
|
@ -326,12 +326,15 @@ class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, Det
|
|||
"""Save image to note"""
|
||||
image = form.cleaned_data['image']
|
||||
|
||||
# Rename as a PNG or GIF
|
||||
extension = image.name.split(".")[-1]
|
||||
if extension == "gif":
|
||||
image.name = "{}_pic.gif".format(self.object.note.pk)
|
||||
if image is None:
|
||||
image = "pic/default.png"
|
||||
else:
|
||||
image.name = "{}_pic.png".format(self.object.note.pk)
|
||||
# Rename as a PNG or GIF
|
||||
extension = image.name.split(".")[-1]
|
||||
if extension == "gif":
|
||||
image.name = "{}_pic.gif".format(self.object.note.pk)
|
||||
else:
|
||||
image.name = "{}_pic.png".format(self.object.note.pk)
|
||||
|
||||
# Save
|
||||
self.object.note.display_image = image
|
||||
|
|
|
@ -13,7 +13,7 @@ from rest_framework import status
|
|||
from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet
|
||||
from permission.backends import PermissionBackend
|
||||
|
||||
from .serializers import NotePolymorphicSerializer, AliasSerializer, ConsumerSerializer,\
|
||||
from .serializers import NotePolymorphicSerializer, AliasSerializer, ConsumerSerializer, \
|
||||
TemplateCategorySerializer, TransactionTemplateSerializer, TransactionPolymorphicSerializer, \
|
||||
TrustSerializer
|
||||
from ..models.notes import Note, Alias, NoteUser, NoteClub, NoteSpecial, Trust
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
<p>
|
||||
Par ailleurs, le BDE ne sert pas d'alcool aux adhérents dont le solde
|
||||
est inférieur à 0 € depuis plus de 24h.
|
||||
est inférieur à 0 €.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
@ -43,4 +43,4 @@
|
|||
{% trans "Mail generated by the Note Kfet on the" %} {% now "j F Y à H:i:s" %}
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -10,7 +10,7 @@ from django.urls import reverse
|
|||
from django.utils import timezone
|
||||
from permission.models import Role
|
||||
|
||||
from ..api.views import AliasViewSet, ConsumerViewSet, NotePolymorphicViewSet, TemplateCategoryViewSet,\
|
||||
from ..api.views import AliasViewSet, ConsumerViewSet, NotePolymorphicViewSet, TemplateCategoryViewSet, \
|
||||
TransactionTemplateViewSet, TransactionViewSet
|
||||
from ..models import NoteUser, Transaction, TemplateCategory, TransactionTemplate, RecurrentTransaction, \
|
||||
MembershipTransaction, SpecialTransaction, NoteSpecial, Alias, Note
|
||||
|
|
|
@ -2591,12 +2591,12 @@
|
|||
"note",
|
||||
"transaction"
|
||||
],
|
||||
"query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}}, {\"valid\": false}]",
|
||||
"query": "[\"OR\", {\"source__balance__gte\": 0}, [\"AND\", [\"NOT\", {\"recurrenttransaction__template__category__name\": \"Alcool\"}], {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}}], {\"valid\": false}]",
|
||||
"type": "add",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Créer une transaction quelconque tant que la source reste au-dessus de -20 €"
|
||||
"description": "Créer une transaction quelconque tant que la source reste positive s'il s'agit d'alcool, sinon au-dessus de -20€"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3228,7 +3228,8 @@
|
|||
"name": "Pr\u00e9sident\u00b7e de club",
|
||||
"permissions": [
|
||||
62,
|
||||
142
|
||||
142,
|
||||
135
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
@ -5,7 +5,6 @@ from django import forms
|
|||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from member.models import Club
|
||||
from note.models import NoteSpecial, Alias
|
||||
from note_kfet.inputs import AmountInput
|
||||
|
||||
|
@ -115,12 +114,3 @@ class ValidationForm(forms.Form):
|
|||
required=False,
|
||||
initial=True,
|
||||
)
|
||||
|
||||
# If the bda exists
|
||||
if Club.objects.filter(name__iexact="bda").exists():
|
||||
# The user can join the bda club at the inscription
|
||||
join_bda = forms.BooleanField(
|
||||
label=_("Join BDA Club"),
|
||||
required=False,
|
||||
initial=True,
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.models import User
|
||||
|
@ -238,9 +239,8 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||
fee += bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid
|
||||
kfet = Club.objects.get(name="Kfet")
|
||||
fee += kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
|
||||
if Club.objects.filter(name__iexact="BDA").exists():
|
||||
bda = Club.objects.get(name__iexact="BDA")
|
||||
fee += bda.membership_fee_paid if user.profile.paid else bda.membership_fee_unpaid
|
||||
for club in Club.objects.filter(add_registration_form=True):
|
||||
fee += club.membership_fee_paid if user.profile.paid else club.membership_fee_unpaid
|
||||
ctx["total_fee"] = "{:.02f}".format(fee / 100, )
|
||||
|
||||
# ctx["declare_soge_account"] = SogeCredit.objects.filter(user=user).exists()
|
||||
|
@ -249,6 +249,16 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
|
||||
# add clubs that are in registration form
|
||||
for club in Club.objects.filter(add_registration_form=True).order_by("name"):
|
||||
form_join_club = forms.BooleanField(
|
||||
label=_("Join %(club)s Club") % {'club': club.name},
|
||||
required=False,
|
||||
initial=False,
|
||||
)
|
||||
form.fields.update({f"join_{club.id}": form_join_club})
|
||||
|
||||
user = self.get_object()
|
||||
form.fields["last_name"].initial = user.last_name
|
||||
form.fields["first_name"].initial = user.first_name
|
||||
|
@ -266,11 +276,6 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||
form.add_error(None, _("An alias with a similar name already exists."))
|
||||
return self.form_invalid(form)
|
||||
|
||||
# Check if BDA exist to propose membership at regisration
|
||||
bda_exists = False
|
||||
if Club.objects.filter(name__iexact="BDA").exists():
|
||||
bda_exists = True
|
||||
|
||||
# Get form data
|
||||
# soge = form.cleaned_data["soge"]
|
||||
credit_type = form.cleaned_data["credit_type"]
|
||||
|
@ -280,8 +285,9 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||
bank = form.cleaned_data["bank"]
|
||||
join_bde = form.cleaned_data["join_bde"]
|
||||
join_kfet = form.cleaned_data["join_kfet"]
|
||||
if bda_exists:
|
||||
join_bda = form.cleaned_data["join_bda"]
|
||||
|
||||
clubs_registration = Club.objects.filter(add_registration_form=True).order_by("name")
|
||||
join_clubs = [(club, form.cleaned_data[f"join_{club.id}"]) for club in clubs_registration]
|
||||
|
||||
# if soge:
|
||||
# # If Société Générale pays the inscription, the user automatically joins the two clubs.
|
||||
|
@ -303,11 +309,12 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||
kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
|
||||
# Add extra fee for the full membership
|
||||
fee += kfet_fee if join_kfet else 0
|
||||
if bda_exists:
|
||||
bda = Club.objects.get(name__iexact="BDA")
|
||||
bda_fee = bda.membership_fee_paid if user.profile.paid else bda.membership_fee_unpaid
|
||||
# Add extra fee for the bda membership
|
||||
fee += bda_fee if join_bda else 0
|
||||
clubs_fee = dict()
|
||||
for club, join_club in join_clubs:
|
||||
club_fee = club.membership_fee_paid if user.profile.paid else club.membership_fee_unpaid
|
||||
# Add extra fee for the club membership
|
||||
clubs_fee[club] = club_fee
|
||||
fee += club_fee if join_club else 0
|
||||
|
||||
# # If the bank pays, then we don't credit now. Treasurers will validate the transaction
|
||||
# # and credit the note later.
|
||||
|
@ -387,17 +394,18 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||
membership.roles.add(Role.objects.get(name="Adhérent Kfet"))
|
||||
membership.save()
|
||||
|
||||
if bda_exists and join_bda:
|
||||
# Create membership for the user to the BDA starting today
|
||||
membership = Membership(
|
||||
club=bda,
|
||||
user=user,
|
||||
fee=bda_fee,
|
||||
)
|
||||
membership.save()
|
||||
membership.refresh_from_db()
|
||||
membership.roles.add(Role.objects.get(name="Membre de club"))
|
||||
membership.save()
|
||||
for club, join_club in join_clubs:
|
||||
if join_club:
|
||||
# Create membership for the user to the BDA starting today
|
||||
membership = Membership(
|
||||
club=club,
|
||||
user=user,
|
||||
fee=clubs_fee[club],
|
||||
)
|
||||
membership.save()
|
||||
membership.refresh_from_db()
|
||||
membership.roles.add(Role.objects.get(name="Membre de club"))
|
||||
membership.save()
|
||||
|
||||
# if soge:
|
||||
# soge_credit = SogeCredit.objects.get(user=user)
|
||||
|
|
|
@ -5,7 +5,7 @@ from django_filters.rest_framework import DjangoFilterBackend
|
|||
from rest_framework.filters import SearchFilter
|
||||
from api.viewsets import ReadProtectedModelViewSet
|
||||
|
||||
from .serializers import InvoiceSerializer, ProductSerializer, RemittanceTypeSerializer, RemittanceSerializer,\
|
||||
from .serializers import InvoiceSerializer, ProductSerializer, RemittanceTypeSerializer, RemittanceSerializer, \
|
||||
SogeCreditSerializer
|
||||
from ..models import Invoice, Product, RemittanceType, Remittance, SogeCredit
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.28 on 2024-03-11 14:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('treasury', '0006_auto_20230414_1651'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='bde',
|
||||
field=models.CharField(choices=[('RavePartlist', 'RavePart[list]'), ('SecretStorlist', 'SecretStor[list]'), ('TotalistSpies', 'Tota[list]Spies'), ('Saperlistpopette', 'Saper[list]popette'), ('Finalist', 'Fina[list]'), ('Listorique', '[List]orique'), ('Satellist', 'Satel[list]'), ('Monopolist', 'Monopo[list]'), ('Kataclist', 'Katac[list]')], default='RavePartlist', max_length=32, verbose_name='BDE'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 2.2.28 on 2024-03-21 23:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('treasury', '0007_auto_20240311_1549'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='invoice',
|
||||
name='payment_date',
|
||||
field=models.CharField(default='', max_length=255, verbose_name='Payment date'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoice',
|
||||
name='quotation',
|
||||
field=models.BooleanField(default=False, verbose_name='Quotation'),
|
||||
),
|
||||
]
|
|
@ -41,6 +41,10 @@ class Invoice(models.Model):
|
|||
),
|
||||
verbose_name=_("BDE"),
|
||||
)
|
||||
quotation = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_("Quotation"),
|
||||
)
|
||||
|
||||
object = models.CharField(
|
||||
max_length=255,
|
||||
|
@ -65,6 +69,12 @@ class Invoice(models.Model):
|
|||
verbose_name=_("Date"),
|
||||
)
|
||||
|
||||
payment_date = models.CharField(
|
||||
default="",
|
||||
max_length=255,
|
||||
verbose_name=_("Payment date"),
|
||||
)
|
||||
|
||||
acquitted = models.BooleanField(
|
||||
verbose_name=_("Acquitted"),
|
||||
default=False,
|
||||
|
|
|
@ -76,8 +76,11 @@
|
|||
|
||||
\def\FactureNum {{"{"}}{{ obj.id }}} % Numéro de facture
|
||||
\def\FactureAcquittee {% if obj.acquitted %} {oui} {% else %} {non} {% endif %} % Facture acquittée : oui/non
|
||||
\def\Devis {% if obj.quotation %} {oui} {% else %} {non} {% endif %}
|
||||
% Devis : oui/non
|
||||
\def\FactureLieu {{"{"}}{{ obj.place|escape_tex }}} % Lieu de l'édition de la facture
|
||||
\def\FactureDate {{"{"}}{{ obj.date }}} % Date de l'édition de la facture
|
||||
\def\FacturePaymentDate {{"{"}}{{ obj.payment_date|escape_tex }}} % Date de paiement de la facture
|
||||
\def\FactureObjet {{"{"}}{{ obj.object|escape_tex }} } % Objet du document
|
||||
% Description de la facture
|
||||
\def\FactureDescr {{"{"}}{{ obj.description|escape_tex }}}
|
||||
|
@ -118,10 +121,12 @@
|
|||
% Nom et adresse de la société
|
||||
\MonNom \\
|
||||
\MonAdresseRue \\
|
||||
\MonAdresseVille
|
||||
|
||||
\MonAdresseVille \\
|
||||
\ifthenelse{\equal{\Devis}{oui}}{
|
||||
Devis n°\FactureNum
|
||||
}{
|
||||
Facture n°\FactureNum
|
||||
|
||||
}
|
||||
|
||||
{\addtolength{\leftskip}{10.5cm} %in ERT
|
||||
\ClientNom \\
|
||||
|
@ -139,6 +144,7 @@ Facture n°\FactureNum
|
|||
|
||||
\textnormal{\FactureDescr}
|
||||
|
||||
|
||||
~\\
|
||||
|
||||
\begin{center}
|
||||
|
@ -154,6 +160,11 @@ Facture n°\FactureNum
|
|||
\ifthenelse{\equal{\FactureAcquittee}{oui}}{
|
||||
Facture acquittée.
|
||||
}{
|
||||
Echéance de paiement : \FacturePaymentDate
|
||||
|
||||
Conditions d'escompte : Aucune
|
||||
|
||||
Taux de pénalité en cas de non paiement ou retard de paiement : 0 \%
|
||||
|
||||
À régler par chèque ou par virement bancaire :
|
||||
|
||||
|
@ -176,5 +187,6 @@ Facture n°\FactureNum
|
|||
TVA non applicable, article 293 B du CGI.
|
||||
\end{center}
|
||||
|
||||
|
||||
\end{document}
|
||||
{% endlanguage %}
|
||||
|
|
|
@ -69,9 +69,11 @@ class TestInvoices(TestCase):
|
|||
response = self.client.post(reverse("treasury:invoice_create"), data={
|
||||
"id": 42,
|
||||
"object": "Same object",
|
||||
"quotation": True,
|
||||
"description": "Longer description",
|
||||
"name": "Me and others",
|
||||
"address": "Alwways earth",
|
||||
"payment_date": "Maybe someday...",
|
||||
"acquitted": True,
|
||||
"products-0-designation": "Designation",
|
||||
"products-0-quantity": 1,
|
||||
|
@ -97,8 +99,10 @@ class TestInvoices(TestCase):
|
|||
"object": "Same object",
|
||||
"description": "Longer description",
|
||||
"name": "Me and others",
|
||||
"quotation": False,
|
||||
"address": "Always earth",
|
||||
"acquitted": True,
|
||||
"payment_date": "Never",
|
||||
"locked": True,
|
||||
"products-0-designation": "Designation",
|
||||
"products-0-quantity": 1,
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
from django.urls import path
|
||||
|
||||
from .views import InvoiceCreateView, InvoiceListView, InvoiceUpdateView, InvoiceDeleteView, InvoiceRenderView,\
|
||||
RemittanceListView, RemittanceCreateView, RemittanceUpdateView, LinkTransactionToRemittanceView,\
|
||||
from .views import InvoiceCreateView, InvoiceListView, InvoiceUpdateView, InvoiceDeleteView, InvoiceRenderView, \
|
||||
RemittanceListView, RemittanceCreateView, RemittanceUpdateView, LinkTransactionToRemittanceView, \
|
||||
UnlinkTransactionToRemittanceView, SogeCreditListView, SogeCreditManageView
|
||||
|
||||
app_name = 'treasury'
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
|||
"pk": 1,
|
||||
"fields": {
|
||||
"domain": "note.crans.org",
|
||||
"name": "La Note Kfet \ud83c\udf7b"
|
||||
"name": "La Note Kfet 🍪"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -68,7 +68,7 @@ mark {
|
|||
/* background-color: rgb(18, 67, 4) !important; */
|
||||
/* MODE VIEUXCON=ON */
|
||||
/* background-color: rgb(166, 0, 2) !important; */
|
||||
background-color: rgb(0, 0, 0) !important;
|
||||
background-color: rgb(100, 30, 100) !important;
|
||||
}
|
||||
|
||||
html {
|
||||
|
@ -83,81 +83,81 @@ body {
|
|||
.btn-outline-primary:hover,
|
||||
.btn-outline-primary:not(:disabled):not(.disabled).active,
|
||||
.btn-outline-primary:not(:disabled):not(.disabled):active {
|
||||
color: rgb(241, 229, 52);
|
||||
background-color: rgb(228, 35, 132);
|
||||
border-color: rgb(228, 35, 132);
|
||||
color: rgb(240, 200, 240);
|
||||
background-color: rgb(30, 120, 150);
|
||||
border-color: rgb(190, 150, 190);
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
border-color: #464647;
|
||||
color: #a2a;
|
||||
background-color: #6bc;
|
||||
border-color: #719;
|
||||
}
|
||||
|
||||
.turbolinks-progress-bar {
|
||||
background-color: #12432E;
|
||||
background-color: #12342E;
|
||||
}
|
||||
|
||||
.btn-primary:hover,
|
||||
.btn-primary:not(:disabled):not(.disabled).active,
|
||||
.btn-primary:not(:disabled):not(.disabled):active {
|
||||
color: rgb(241, 229, 52);
|
||||
background-color: rgb(228, 35, 132);
|
||||
border-color: rgb(228, 35, 132);
|
||||
color: rgb(150, 200, 240);
|
||||
background-color: rgb(50, 100, 140);
|
||||
border-color: rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
border-color: #adb5bd;
|
||||
color: #eae;
|
||||
background-color: #616;
|
||||
border-color: #000000;
|
||||
}
|
||||
|
||||
.border-primary {
|
||||
border-color: rgb(228, 35, 132) !important;
|
||||
border-color: rgb(222, 180, 222) !important;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
border-color: #adb5bd;
|
||||
color: #eae;
|
||||
background-color: #616;
|
||||
border-color: #000000;
|
||||
}
|
||||
|
||||
.btn-secondary:hover,
|
||||
.btn-secondary:not(:disabled):not(.disabled).active,
|
||||
.btn-secondary:not(:disabled):not(.disabled):active {
|
||||
color: rgb(241, 229, 52);
|
||||
background-color: rgb(228, 35, 132);
|
||||
border-color: rgb(228, 35, 132);
|
||||
color: rgb(150, 200, 240);
|
||||
background-color: rgb(50, 100, 140);
|
||||
border-color: rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
.btn-outline-dark {
|
||||
color: #343a40;
|
||||
border-color: #343a40;
|
||||
color: #000000;
|
||||
border-color: #000000;
|
||||
}
|
||||
|
||||
.btn-outline-dark:hover,
|
||||
.btn-outline-dark:not(:disabled):not(.disabled).active,
|
||||
.btn-outline-dark:not(:disabled):not(.disabled):active {
|
||||
color: rgb(241, 229, 52);
|
||||
background-color: rgb(228, 35, 132);
|
||||
border-color: rgb(228, 35, 132);
|
||||
color: rgb(50, 100, 160);
|
||||
background-color: rgb(240, 150, 240);
|
||||
border-color: rgb(50, 100, 160);
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
color: rgb(228, 35, 132);
|
||||
color: rgb(0, 150, 150);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: rgb(228, 35, 132);
|
||||
color: rgb(200, 0, 200);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
box-shadow: 0 0 0 0.25rem rgb(228 35 132 / 50%);
|
||||
border-color: rgb(228, 35, 132);
|
||||
box-shadow: 0 0 0 0.25rem rgb(0 150 150 / 50%);
|
||||
border-color: rgb(0, 200, 200);
|
||||
}
|
||||
|
||||
.btn-outline-primary.focus {
|
||||
box-shadow: 0 0 0 0.25rem rgb(228 35 132 / 10%);
|
||||
box-shadow: 0 0 0 0.25rem rgb(0 150 150 / 22%);
|
||||
}
|
||||
|
|
|
@ -194,6 +194,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
class="text-muted">{% trans "Contact us" %}</a> —
|
||||
<a href="mailto:{{ "SUPPORT_EMAIL" | getenv }}"
|
||||
class="text-muted">{% trans "Technical Support" %}</a> —
|
||||
<a href="https://perso.crans.org/club-bde/charte_informatique.pdf"
|
||||
class="text-muted">{% trans "Charte Info (FR)" %}</a> —
|
||||
<a href="https://note.crans.org/doc/faq/"
|
||||
class="text-muted">{% trans "FAQ (FR)" %}</a> —
|
||||
</span>
|
||||
|
|
2
tox.ini
2
tox.ini
|
@ -51,4 +51,4 @@ max-complexity = 15
|
|||
max-line-length = 160
|
||||
import-order-style = google
|
||||
application-import-names = flake8
|
||||
format = ${cyan}%(path)s${reset}:${yellow_bold}%(row)d${reset}:${green_bold}%(col)d${reset}: ${red_bold}%(code)s${reset} %(text)s
|
||||
format = %(cyan)s%(path)s%(reset)s:%(yellow)s%(bold)s%(row)d%(reset)s:%(green)s%(bold)s%(col)d%(reset)s: %(red)s%(bold)s%(code)s%(reset)s %(text)s
|
||||
|
|
Loading…
Reference in New Issue